mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-13 14:21:27 +00:00
Compare commits
55 Commits
langchain-
...
langchain-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
315223ce26 | ||
|
|
0345990a42 | ||
|
|
cda3025ee1 | ||
|
|
ad7581751f | ||
|
|
cc451effd1 | ||
|
|
3d16dcd88d | ||
|
|
3a5365a33e | ||
|
|
03d62a737a | ||
|
|
e00cc74926 | ||
|
|
c4d2a53f18 | ||
|
|
190988d93e | ||
|
|
5f593c172a | ||
|
|
b65ac8d39c | ||
|
|
4f3b4fc7fe | ||
|
|
ee399e3ec5 | ||
|
|
2b6a262f84 | ||
|
|
148766ddc1 | ||
|
|
59880a9147 | ||
|
|
20690db482 | ||
|
|
c623ae6661 | ||
|
|
69eacaa887 | ||
|
|
703491e824 | ||
|
|
8734cabc09 | ||
|
|
ce067c19e9 | ||
|
|
4840db6892 | ||
|
|
218c554c4f | ||
|
|
0fe29b4343 | ||
|
|
5c7e589aaf | ||
|
|
0fdbaf4a8d | ||
|
|
871bf5a841 | ||
|
|
8b7cffc363 | ||
|
|
58dd69f7f2 | ||
|
|
dfbd12b384 | ||
|
|
38d30e285a | ||
|
|
89bcca3542 | ||
|
|
cd563fb628 | ||
|
|
256bad3251 | ||
|
|
5fdbdd6bec | ||
|
|
221486687a | ||
|
|
d6631919f4 | ||
|
|
235eb38d3e | ||
|
|
7dd6b32991 | ||
|
|
4b1b7959a2 | ||
|
|
afee851645 | ||
|
|
a73e2222d4 | ||
|
|
e160b669c8 | ||
|
|
d59c656ea5 | ||
|
|
2394807033 | ||
|
|
acfce30017 | ||
|
|
8f3c052db1 | ||
|
|
29a3b3a711 | ||
|
|
20fe4deea0 | ||
|
|
3a55f4bfe9 | ||
|
|
fea9ff3831 | ||
|
|
b55f6105c6 |
1
.github/workflows/_release.yml
vendored
1
.github/workflows/_release.yml
vendored
@@ -290,6 +290,7 @@ jobs:
|
||||
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
|
||||
UPSTAGE_API_KEY: ${{ secrets.UPSTAGE_API_KEY }}
|
||||
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
||||
UNSTRUCTURED_API_KEY: ${{ secrets.UNSTRUCTURED_API_KEY }}
|
||||
run: make integration_tests
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ Notebook | Description
|
||||
[llm_symbolic_math.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/llm_symbolic_math.ipynb) | Solve algebraic equations with the help of llms (language learning models) and sympy, a python library for symbolic mathematics.
|
||||
[meta_prompt.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/meta_prompt.ipynb) | Implement the meta-prompt concept, which is a method for building self-improving agents that reflect on their own performance and modify their instructions accordingly.
|
||||
[multi_modal_output_agent.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/multi_modal_output_agent.ipynb) | Generate multi-modal outputs, specifically images and text.
|
||||
[multi_modal_RAG_vdms.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/multi_modal_RAG_vdms.ipynb) | Perform retrieval-augmented generation (rag) on documents including text and images, using unstructured for parsing, Intel's Visual Data Management System (VDMS) as the vectorstore, and chains.
|
||||
[multi_player_dnd.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/multi_player_dnd.ipynb) | Simulate multi-player dungeons & dragons games, with a custom function determining the speaking schedule of the agents.
|
||||
[multiagent_authoritarian.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/multiagent_authoritarian.ipynb) | Implement a multi-agent simulation where a privileged agent controls the conversation, including deciding who speaks and when the conversation ends, in the context of a simulated news network.
|
||||
[multiagent_bidding.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/multiagent_bidding.ipynb) | Implement a multi-agent simulation where agents bid to speak, with the highest bidder speaking next, demonstrated through a fictitious presidential debate example.
|
||||
|
||||
@@ -18,26 +18,7 @@
|
||||
"* Use of multimodal embeddings (such as [CLIP](https://openai.com/research/clip)) to embed images and text\n",
|
||||
"* Use of [VDMS](https://github.com/IntelLabs/vdms/blob/master/README.md) as a vector store with support for multi-modal\n",
|
||||
"* Retrieval of both images and text using similarity search\n",
|
||||
"* Passing raw images and text chunks to a multimodal LLM for answer synthesis \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Packages\n",
|
||||
"\n",
|
||||
"For `unstructured`, you will also need `poppler` ([installation instructions](https://pdf2image.readthedocs.io/en/latest/installation.html)) and `tesseract` ([installation instructions](https://tesseract-ocr.github.io/tessdoc/Installation.html)) in your system."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "febbc459-ebba-4c1a-a52b-fed7731593f8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# (newest versions required for multi-modal)\n",
|
||||
"! pip install --quiet -U vdms langchain-experimental\n",
|
||||
"\n",
|
||||
"# lock to 0.10.19 due to a persistent bug in more recent versions\n",
|
||||
"! pip install --quiet pdf2image \"unstructured[all-docs]==0.10.19\" pillow pydantic lxml open_clip_torch"
|
||||
"* Passing raw images and text chunks to a multimodal LLM for answer synthesis "
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -53,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 1,
|
||||
"id": "5f483872",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -61,8 +42,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"docker: Error response from daemon: Conflict. The container name \"/vdms_rag_nb\" is already in use by container \"0c19ed281463ac10d7efe07eb815643e3e534ddf24844357039453ad2b0c27e8\". You have to remove (or rename) that container to be able to reuse that name.\n",
|
||||
"See 'docker run --help'.\n"
|
||||
"a1b9206b08ef626e15b356bf9e031171f7c7eb8f956a2733f196f0109246fe2b\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -75,9 +55,32 @@
|
||||
"vdms_client = VDMS_Client(port=55559)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2498a0a1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Packages\n",
|
||||
"\n",
|
||||
"For `unstructured`, you will also need `poppler` ([installation instructions](https://pdf2image.readthedocs.io/en/latest/installation.html)) and `tesseract` ([installation instructions](https://tesseract-ocr.github.io/tessdoc/Installation.html)) in your system."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 2,
|
||||
"id": "febbc459-ebba-4c1a-a52b-fed7731593f8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip install --quiet -U vdms langchain-experimental\n",
|
||||
"\n",
|
||||
"# lock to 0.10.19 due to a persistent bug in more recent versions\n",
|
||||
"! pip install --quiet pdf2image \"unstructured[all-docs]==0.10.19\" pillow pydantic lxml open_clip_torch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "78ac6543",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -95,14 +98,9 @@
|
||||
"\n",
|
||||
"### Partition PDF text and images\n",
|
||||
" \n",
|
||||
"Let's look at an example pdf containing interesting images.\n",
|
||||
"Let's use famous photographs from the PDF version of Library of Congress Magazine in this example.\n",
|
||||
"\n",
|
||||
"Famous photographs from library of congress:\n",
|
||||
"\n",
|
||||
"* https://www.loc.gov/lcm/pdf/LCM_2020_1112.pdf\n",
|
||||
"* We'll use this as an example below\n",
|
||||
"\n",
|
||||
"We can use `partition_pdf` below from [Unstructured](https://unstructured-io.github.io/unstructured/introduction.html#key-concepts) to extract text and images."
|
||||
"We can use `partition_pdf` from [Unstructured](https://unstructured-io.github.io/unstructured/introduction.html#key-concepts) to extract text and images."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -116,8 +114,8 @@
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"\n",
|
||||
"# Folder with pdf and extracted images\n",
|
||||
"datapath = Path(\"./multimodal_files\").resolve()\n",
|
||||
"# Folder to store pdf and extracted images\n",
|
||||
"datapath = Path(\"./data/multimodal_files\").resolve()\n",
|
||||
"datapath.mkdir(parents=True, exist_ok=True)\n",
|
||||
"\n",
|
||||
"pdf_url = \"https://www.loc.gov/lcm/pdf/LCM_2020_1112.pdf\"\n",
|
||||
@@ -174,14 +172,8 @@
|
||||
"source": [
|
||||
"## Multi-modal embeddings with our document\n",
|
||||
"\n",
|
||||
"We will use [OpenClip multimodal embeddings](https://python.langchain.com/docs/integrations/text_embedding/open_clip).\n",
|
||||
"\n",
|
||||
"We use a larger model for better performance (set in `langchain_experimental.open_clip.py`).\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"model_name = \"ViT-g-14\"\n",
|
||||
"checkpoint = \"laion2b_s34b_b88k\"\n",
|
||||
"```"
|
||||
"In this section, we initialize the VDMS vector store for both text and images. For better performance, we use model `ViT-g-14` from [OpenClip multimodal embeddings](https://python.langchain.com/docs/integrations/text_embedding/open_clip).\n",
|
||||
"The images are stored as base64 encoded strings with `vectorstore.add_images`.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -200,9 +192,7 @@
|
||||
"vectorstore = VDMS(\n",
|
||||
" client=vdms_client,\n",
|
||||
" collection_name=\"mm_rag_clip_photos\",\n",
|
||||
" embedding_function=OpenCLIPEmbeddings(\n",
|
||||
" model_name=\"ViT-g-14\", checkpoint=\"laion2b_s34b_b88k\"\n",
|
||||
" ),\n",
|
||||
" embedding=OpenCLIPEmbeddings(model_name=\"ViT-g-14\", checkpoint=\"laion2b_s34b_b88k\"),\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Get image URIs with .jpg extension only\n",
|
||||
@@ -233,7 +223,7 @@
|
||||
"source": [
|
||||
"## RAG\n",
|
||||
"\n",
|
||||
"`vectorstore.add_images` will store / retrieve images as base64 encoded strings."
|
||||
"Here we define helper functions for image results."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -392,7 +382,8 @@
|
||||
"id": "1566096d-97c2-4ddc-ba4a-6ef88c525e4e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test retrieval and run RAG"
|
||||
"## Test retrieval and run RAG\n",
|
||||
"Now let's query for a `woman with children` and retrieve the top results."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -452,6 +443,14 @@
|
||||
" print(doc.page_content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "15e9b54d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now let's use the `multi_modal_rag_chain` to process the same query and display the response."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
@@ -462,10 +461,10 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1. Detailed description of the visual elements in the image: The image features a woman with children, likely a mother and her family, standing together outside. They appear to be poor or struggling financially, as indicated by their attire and surroundings.\n",
|
||||
"2. Historical and cultural context of the image: The photo was taken in 1936 during the Great Depression, when many families struggled to make ends meet. Dorothea Lange, a renowned American photographer, took this iconic photograph that became an emblem of poverty and hardship experienced by many Americans at that time.\n",
|
||||
"3. Interpretation of the image's symbolism and meaning: The image conveys a sense of unity and resilience despite adversity. The woman and her children are standing together, displaying their strength as a family unit in the face of economic challenges. The photograph also serves as a reminder of the importance of empathy and support for those who are struggling.\n",
|
||||
"4. Connections between the image and the related text: The text provided offers additional context about the woman in the photo, her background, and her feelings towards the photograph. It highlights the historical backdrop of the Great Depression and emphasizes the significance of this particular image as a representation of that time period.\n"
|
||||
" The image depicts a woman with several children. The woman appears to be of Cherokee heritage, as suggested by the text provided. The image is described as having been initially regretted by the subject, Florence Owens Thompson, due to her feeling that it did not accurately represent her leadership qualities.\n",
|
||||
"The historical and cultural context of the image is tied to the Great Depression and the Dust Bowl, both of which affected the Cherokee people in Oklahoma. The photograph was taken during this period, and its subject, Florence Owens Thompson, was a leader within her community who worked tirelessly to help those affected by these crises.\n",
|
||||
"The image's symbolism and meaning can be interpreted as a representation of resilience and strength in the face of adversity. The woman is depicted with multiple children, which could signify her role as a caregiver and protector during difficult times.\n",
|
||||
"Connections between the image and the related text include Florence Owens Thompson's leadership qualities and her regretted feelings about the photograph. Additionally, the mention of Dorothea Lange, the photographer who took this photo, ties the image to its historical context and the broader narrative of the Great Depression and Dust Bowl in Oklahoma. \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -492,14 +491,6 @@
|
||||
"source": [
|
||||
"! docker kill vdms_rag_nb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8ba652da",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -518,7 +509,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.13"
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -38,6 +38,8 @@ generate-files:
|
||||
|
||||
$(PYTHON) scripts/model_feat_table.py $(INTERMEDIATE_DIR)
|
||||
|
||||
$(PYTHON) scripts/tool_feat_table.py $(INTERMEDIATE_DIR)
|
||||
|
||||
$(PYTHON) scripts/document_loader_feat_table.py $(INTERMEDIATE_DIR)
|
||||
|
||||
$(PYTHON) scripts/copy_templates.py $(INTERMEDIATE_DIR)
|
||||
|
||||
@@ -165,7 +165,7 @@ Some important things to note:
|
||||
ChatModels also accept other parameters that are specific to that integration. To find all the parameters supported by a ChatModel head to the API reference for that model.
|
||||
|
||||
:::important
|
||||
**Tool Calling** Some chat models have been fine-tuned for tool calling and provide a dedicated API for tool calling.
|
||||
Some chat models have been fine-tuned for **tool calling** and provide a dedicated API for it.
|
||||
Generally, such models are better at tool calling than non-fine-tuned models, and are recommended for use cases that require tool calling.
|
||||
Please see the [tool calling section](/docs/concepts/#functiontool-calling) for more information.
|
||||
:::
|
||||
@@ -255,7 +255,7 @@ This represents the result of a tool call. In addition to `role` and `content`,
|
||||
|
||||
#### (Legacy) FunctionMessage
|
||||
|
||||
This is a legacy message type, corresponding to OpenAI's legacy function-calling API. ToolMessage should be used instead to correspond to the updated tool-calling API.
|
||||
This is a legacy message type, corresponding to OpenAI's legacy function-calling API. `ToolMessage` should be used instead to correspond to the updated tool-calling API.
|
||||
|
||||
This represents the result of a function call. In addition to `role` and `content`, this message has a `name` parameter which conveys the name of the function that was called to produce this result.
|
||||
|
||||
@@ -826,6 +826,61 @@ units (like words or subwords) that carry meaning, rather than individual charac
|
||||
to learn and understand the structure of the language, including grammar and context.
|
||||
Furthermore, using tokens can also improve efficiency, since the model processes fewer units of text compared to character-level processing.
|
||||
|
||||
### Function/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.
|
||||
:::
|
||||
|
||||
Tool calling allows a [chat model](/docs/concepts/#chat-models) 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 only generates the arguments to a tool, and actually running the tool (or not) is up to the user.
|
||||
One common example where you **wouldn't** want to call a function with the generated arguments
|
||||
is if you want to [extract structured output matching some schema](/docs/concepts/#structured-output)
|
||||
from unstructured text. You would give the model an "extraction" tool that takes
|
||||
parameters matching the desired schema, then treat the generated output as your final
|
||||
result.
|
||||
|
||||

|
||||
|
||||
Tool calling is not universal, but is supported by many popular LLM providers, including [Anthropic](/docs/integrations/chat/anthropic/),
|
||||
[Cohere](/docs/integrations/chat/cohere/), [Google](/docs/integrations/chat/google_vertex_ai_palm/),
|
||||
[Mistral](/docs/integrations/chat/mistralai/), [OpenAI](/docs/integrations/chat/openai/), and even for locally-running models via [Ollama](/docs/integrations/chat/ollama/).
|
||||
|
||||
LangChain provides a standardized interface for tool calling that is consistent across different models.
|
||||
|
||||
The standard interface consists of:
|
||||
|
||||
* `ChatModel.bind_tools()`: a method for specifying which tools are available for a model to call. This method accepts [LangChain tools](/docs/concepts/#tools) as well as [Pydantic](https://pydantic.dev/) objects.
|
||||
* `AIMessage.tool_calls`: an attribute on the `AIMessage` returned from the model for accessing the tool calls requested by the model.
|
||||
|
||||
#### Tool usage
|
||||
|
||||
After the model calls tools, you can use the tool by invoking it, then passing the arguments back to the model.
|
||||
LangChain provides the [`Tool`](/docs/concepts/#tools) abstraction to help you handle this.
|
||||
|
||||
The general flow is this:
|
||||
|
||||
1. Generate tool calls with a chat model in response to a query.
|
||||
2. Invoke the appropriate tools using the generated tool call as arguments.
|
||||
3. Format the result of the tool invocations as [`ToolMessages`](/docs/concepts/#toolmessage).
|
||||
4. Pass the entire list of messages back to the model so that it can generate a final answer (or call more tools).
|
||||
|
||||

|
||||
|
||||
This is how tool calling [agents](/docs/concepts/#agents) perform tasks and answer queries.
|
||||
|
||||
Check out some more focused guides below:
|
||||
|
||||
- [How to use chat models to call tools](/docs/how_to/tool_calling/)
|
||||
- [How to pass tool outputs to chat models](/docs/how_to/tool_results_pass_to_model/)
|
||||
- [Building an agent with LangGraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/)
|
||||
|
||||
### Structured output
|
||||
|
||||
LLMs are capable of generating arbitrary text. This enables the model to respond appropriately to a wide
|
||||
@@ -958,48 +1013,48 @@ chain.invoke({ "question": "What is the powerhouse of the cell?" })
|
||||
|
||||
For a full list of model providers that support JSON mode, see [this table](/docs/integrations/chat/#advanced-features).
|
||||
|
||||
#### Function/tool calling
|
||||
#### Tool calling {#structured-output-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
|
||||
:::
|
||||
For models that support it, [tool calling](/docs/concepts/#functiontool-calling) can be very convenient for structured output. It removes the
|
||||
guesswork around how best to prompt schemas in favor of a built-in model feature.
|
||||
|
||||
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/tutorials/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.
|
||||
It works by first binding the desired schema either directly or via a [LangChain tool](/docs/concepts/#tools) to a
|
||||
[chat model](/docs/concepts/#chat-models) using the `.bind_tools()` method. The model will then generate an `AIMessage` containing
|
||||
a `tool_calls` field containing `args` that match the desired shape.
|
||||
|
||||
For models that support it, tool calling can be very convenient. It removes the
|
||||
guesswork around how best to prompt schemas in favor of a built-in model feature. It can also
|
||||
more naturally support agentic flows, since you can just pass multiple tool schemas instead
|
||||
of fiddling with enums or unions.
|
||||
There are several acceptable formats you can use to bind tools to a model in LangChain. Here's one example:
|
||||
|
||||
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/how_to/custom_tools).
|
||||
```python
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
LangChain provides a standardized interface for tool calling that is consistent across different models.
|
||||
class ResponseFormatter(BaseModel):
|
||||
"""Always use this tool to structure your response to the user."""
|
||||
|
||||
The standard interface consists of:
|
||||
answer: str = Field(description="The answer to the user's question")
|
||||
followup_question: str = Field(description="A followup question the user could ask")
|
||||
|
||||
* `ChatModel.bind_tools()`: a method for specifying which tools are available for a model to call. This method accepts [LangChain tools](/docs/concepts/#tools) here.
|
||||
* `AIMessage.tool_calls`: an attribute on the `AIMessage` returned from the model for accessing the tool calls requested by the model.
|
||||
model = ChatOpenAI(
|
||||
model="gpt-4o",
|
||||
temperature=0,
|
||||
)
|
||||
|
||||
The following how-to guides are good practical resources for using function/tool calling:
|
||||
model_with_tools = model.bind_tools([ResponseFormatter])
|
||||
|
||||
ai_msg = model_with_tools.invoke("What is the powerhouse of the cell?")
|
||||
|
||||
ai_msg.tool_calls[0]["args"]
|
||||
```
|
||||
|
||||
```
|
||||
{'answer': "The powerhouse of the cell is the mitochondrion. It generates most of the cell's supply of adenosine triphosphate (ATP), which is used as a source of chemical energy.",
|
||||
'followup_question': 'How do mitochondria generate ATP?'}
|
||||
```
|
||||
|
||||
Tool calling is a generally consistent way to get a model to generate structured output, and is the default technique
|
||||
used for the [`.with_structured_output()`](/docs/concepts/#with_structured_output) method when a model supports it.
|
||||
|
||||
The following how-to guides are good practical resources for using function/tool calling for structured output:
|
||||
|
||||
- [How to return structured data from an LLM](/docs/how_to/structured_output/)
|
||||
- [How to use a model to call tools](/docs/how_to/tool_calling)
|
||||
|
||||
Binary file not shown.
146
docs/docs/how_to/chat_model_rate_limiting.ipynb
Normal file
146
docs/docs/how_to/chat_model_rate_limiting.ipynb
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "dcf87b32",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# How to handle rate limits\n",
|
||||
"\n",
|
||||
":::info Prerequisites\n",
|
||||
"\n",
|
||||
"This guide assumes familiarity with the following concepts:\n",
|
||||
"- [Chat models](/docs/concepts/#chat-models)\n",
|
||||
"- [LLMs](/docs/concepts/#llms)\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"You may find yourself in a situation where you are getting rate limited by the model provider API because you're making too many requests.\n",
|
||||
"\n",
|
||||
"For example, this might happen if you are running many parallel queries to benchmark the chat model on a test dataset.\n",
|
||||
"\n",
|
||||
"If you are facing such a situation, you can use a rate limiter to help match the rate at which you're making request to the rate allowed\n",
|
||||
"by the API.\n",
|
||||
"\n",
|
||||
":::info Requires ``langchain-core >= 0.2.24``\n",
|
||||
"\n",
|
||||
"This functionality was added in ``langchain-core == 0.2.24``. Please make sure your package is up to date.\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cbc3c873-6109-4e03-b775-b73c1003faea",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Initialize a rate limiter\n",
|
||||
"\n",
|
||||
"Langchain comes with a built-in in memory rate limiter. This rate limiter is thread safe and can be shared by multiple threads in the same process.\n",
|
||||
"\n",
|
||||
"The provided rate limiter can only limit the number of requests per unit time. It will not help if you need to also limited based on the size\n",
|
||||
"of the requests."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "aa9c3c8c-0464-4190-a8c5-d69d173505a6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.rate_limiters import InMemoryRateLimiter\n",
|
||||
"\n",
|
||||
"rate_limiter = InMemoryRateLimiter(\n",
|
||||
" requests_per_second=0.1, # <-- Super slow! We can only make a request once every 10 seconds!!\n",
|
||||
" check_every_n_seconds=0.1, # Wake up every 100 ms to check whether allowed to make a request,\n",
|
||||
" max_bucket_size=10, # Controls the maximum burst size.\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8e058bde-9413-4b08-8cc6-0c9cb638f19f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Choose a model\n",
|
||||
"\n",
|
||||
"Choose any model and pass to it the rate_limiter via the `rate_limiter` attribute."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0f880a3a-c047-4e94-a323-fff2a4c0e96d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"import time\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"if \"ANTHROPIC_API_KEY\" not in os.environ:\n",
|
||||
" os.environ[\"ANTHROPIC_API_KEY\"] = getpass()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"model = ChatAnthropic(model_name=\"claude-3-opus-20240229\", rate_limiter=rate_limiter)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "80c9ab3a-299a-460f-985c-90280a046f52",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's confirm that the rate limiter works. We should only be able to invoke the model once per 10 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "d074265c-9f32-4c5f-b914-944148993c4d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"11.599073648452759\n",
|
||||
"10.7502121925354\n",
|
||||
"10.244257926940918\n",
|
||||
"8.83088755607605\n",
|
||||
"11.645203590393066\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for _ in range(5):\n",
|
||||
" tic = time.time()\n",
|
||||
" model.invoke(\"hello\")\n",
|
||||
" toc = time.time()\n",
|
||||
" print(toc - tic)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
"source": [
|
||||
"### StructuredTool\n",
|
||||
"\n",
|
||||
"The `StrurcturedTool.from_function` class method provides a bit more configurability than the `@tool` decorator, without requiring much additional code."
|
||||
"The `StructuredTool.from_function` class method provides a bit more configurability than the `@tool` decorator, without requiring much additional code."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -83,6 +83,7 @@ These are the core building blocks you can use when building applications.
|
||||
- [How to: track response metadata across providers](/docs/how_to/response_metadata)
|
||||
- [How to: use chat model to call tools](/docs/how_to/tool_calling)
|
||||
- [How to: stream tool calls](/docs/how_to/tool_streaming)
|
||||
- [How to: handle rate limits](/docs/how_to/chat_model_rate_limiting)
|
||||
- [How to: few shot prompt tool behavior](/docs/how_to/tools_few_shot)
|
||||
- [How to: bind model-specific formatted tools](/docs/how_to/tools_model_specific)
|
||||
- [How to: force a specific tool call](/docs/how_to/tool_choice)
|
||||
|
||||
@@ -15,7 +15,23 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "25b0b0fa",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain_openai langchain_community\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass()\n",
|
||||
"# Please manually enter OpenAI Key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0aa6d335",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -23,13 +39,14 @@
|
||||
"from langchain.globals import set_llm_cache\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"\n",
|
||||
"# To make the caching really obvious, lets use a slower model.\n",
|
||||
"llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", n=2, best_of=2)"
|
||||
"# To make the caching really obvious, lets use a slower and older model.\n",
|
||||
"# Caching supports newer chat models as well.\n",
|
||||
"llm = OpenAI(model=\"gpt-3.5-turbo-instruct\", n=2, best_of=2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 3,
|
||||
"id": "f168ff0d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -37,17 +54,17 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 13.7 ms, sys: 6.54 ms, total: 20.2 ms\n",
|
||||
"Wall time: 330 ms\n"
|
||||
"CPU times: user 546 ms, sys: 379 ms, total: 925 ms\n",
|
||||
"Wall time: 1.11 s\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was two-tired!\""
|
||||
"\"\\nWhy don't scientists trust atoms?\\n\\nBecause they make up everything!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -59,12 +76,12 @@
|
||||
"set_llm_cache(InMemoryCache())\n",
|
||||
"\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm.predict(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 4,
|
||||
"id": "ce7620fb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -72,17 +89,17 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 436 µs, sys: 921 µs, total: 1.36 ms\n",
|
||||
"Wall time: 1.36 ms\n"
|
||||
"CPU times: user 192 µs, sys: 77 µs, total: 269 µs\n",
|
||||
"Wall time: 270 µs\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was two-tired!\""
|
||||
"\"\\nWhy don't scientists trust atoms?\\n\\nBecause they make up everything!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -90,7 +107,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm.predict(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -103,7 +120,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 5,
|
||||
"id": "2e65de83",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -113,7 +130,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 6,
|
||||
"id": "0be83715",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -126,7 +143,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 7,
|
||||
"id": "9b427ce7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -134,17 +151,17 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 29.3 ms, sys: 17.3 ms, total: 46.7 ms\n",
|
||||
"Wall time: 364 ms\n"
|
||||
"CPU times: user 10.6 ms, sys: 4.21 ms, total: 14.8 ms\n",
|
||||
"Wall time: 851 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\n\\nWhy did the tomato turn red?\\n\\nBecause it saw the salad dressing!'"
|
||||
"\"\\n\\nWhy don't scientists trust atoms?\\n\\nBecause they make up everything!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -152,12 +169,12 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm.predict(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 8,
|
||||
"id": "87f52611",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -165,17 +182,17 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 4.58 ms, sys: 2.23 ms, total: 6.8 ms\n",
|
||||
"Wall time: 4.68 ms\n"
|
||||
"CPU times: user 59.7 ms, sys: 63.6 ms, total: 123 ms\n",
|
||||
"Wall time: 134 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\n\\nWhy did the tomato turn red?\\n\\nBecause it saw the salad dressing!'"
|
||||
"\"\\n\\nWhy don't scientists trust atoms?\\n\\nBecause they make up everything!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -183,7 +200,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm.predict(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -211,7 +228,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -22,57 +22,36 @@
|
||||
":::info Prerequisites\n",
|
||||
"\n",
|
||||
"This guide assumes familiarity with the following concepts:\n",
|
||||
"\n",
|
||||
"- [Chat models](/docs/concepts/#chat-models)\n",
|
||||
"- [LangChain Tools](/docs/concepts/#tools)\n",
|
||||
"- [Tool calling](/docs/concepts/#functiontool-calling)\n",
|
||||
"- [Output parsers](/docs/concepts/#output-parsers)\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
":::info Tool calling vs function calling\n",
|
||||
"[Tool calling](/docs/concepts/#functiontool-calling) allows a chat model to respond to a given prompt by \"calling a tool\".\n",
|
||||
"\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",
|
||||
"Remember, while the name \"tool calling\" implies that the model is directly performing some action, this is actually not the case! The model only generates the arguments to a tool, and actually running the tool (or not) is up to the user.\n",
|
||||
"\n",
|
||||
"Tool calling is a general technique that generates structured output from a model, and you can use it even when you don't intend to invoke any tools. An example use-case of that is [extraction from unstructured text](/docs/tutorials/extraction/).\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"If you want to see how to use the model-generated tool call to actually run a tool function [check out this guide](/docs/how_to/tool_results_pass_to_model/).\n",
|
||||
"\n",
|
||||
":::note Supported models\n",
|
||||
"\n",
|
||||
"Tool calling is not universal, but is supported by many popular LLM providers, including [Anthropic](/docs/integrations/chat/anthropic/), \n",
|
||||
"[Cohere](/docs/integrations/chat/cohere/), [Google](/docs/integrations/chat/google_vertex_ai_palm/), \n",
|
||||
"[Mistral](/docs/integrations/chat/mistralai/), [OpenAI](/docs/integrations/chat/openai/), and even for locally-running models via [Ollama](/docs/integrations/chat/ollama/).\n",
|
||||
"\n",
|
||||
"You can find a [list of all models that support tool calling here](/docs/integrations/chat/).\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
":::info Supported models\n",
|
||||
"\n",
|
||||
"You can find a [list of all models that support tool calling](/docs/integrations/chat/).\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"Tool calling allows a chat model to respond to a given prompt by \"calling a tool\".\n",
|
||||
"While the name implies that the model is performing \n",
|
||||
"some action, this is actually not the case! The model generates 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/how_to/structured_output/) \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",
|
||||
":::note\n",
|
||||
"\n",
|
||||
"If you only need formatted values, try the [.with_structured_output()](/docs/how_to/structured_output/#the-with_structured_output-method) chat model method as a simpler entrypoint.\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"However, tool calling goes beyond [structured output](/docs/how_to/structured_output/)\n",
|
||||
"since you can pass responses from called tools back to the model to create longer interactions.\n",
|
||||
"For instance, given a search engine tool, an LLM might handle a \n",
|
||||
"query by first issuing a call to the search engine with arguments. 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/how_to/custom_tools). \n",
|
||||
"\n",
|
||||
"Tool calling is not universal, but many popular 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.\n",
|
||||
"\n",
|
||||
"LangChain implements standard interfaces for defining tools, passing them to LLMs, \n",
|
||||
"and representing tool calls. This guide and the other How-to pages in the Tool section will show you how to use tools with LangChain."
|
||||
"LangChain implements standard interfaces for defining tools, passing them to LLMs, and representing tool calls.\n",
|
||||
"This guide will cover how to bind tools to an LLM, then invoke the LLM to generate these arguments."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -91,7 +70,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -112,14 +91,14 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"LangChain also implements a `@tool` decorator that allows for further control of the tool schema, such as tool names and argument descriptions. See the how-to guide [here](/docs/how_to/custom_tools/#creating-tools-from-functions) for detail.\n",
|
||||
"LangChain also implements a `@tool` decorator that allows for further control of the tool schema, such as tool names and argument descriptions. See the how-to guide [here](/docs/how_to/custom_tools/#creating-tools-from-functions) for details.\n",
|
||||
"\n",
|
||||
"We can also define the schema using [Pydantic](https://docs.pydantic.dev):"
|
||||
"We can also define the schemas without the accompanying functions using [Pydantic](https://docs.pydantic.dev):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -149,7 +128,8 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can bind them to chat models as follows:\n",
|
||||
"To actually bind those schemas to a chat model, we'll use the `.bind_tools()` method. This handles converting\n",
|
||||
"the `Add` and `Multiply` schemas to the proper format for the model. The tool schema will then be passed it in each time the model is invoked.\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
@@ -158,11 +138,7 @@
|
||||
" customVarName=\"llm\"\n",
|
||||
" fireworksParams={`model=\"accounts/fireworks/models/firefunction-v1\", temperature=0`}\n",
|
||||
"/>\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"We'll use the `.bind_tools()` method to handle converting\n",
|
||||
"`Multiply` to the proper format for the model, then and bind it (i.e.,\n",
|
||||
"passing it in each time the model is invoked)."
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -183,7 +159,7 @@
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass()\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -194,7 +170,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_g4RuAijtDcSeM96jXyCuiLSN', 'function': {'arguments': '{\"a\":3,\"b\":12}', 'name': 'Multiply'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 95, 'total_tokens': 113}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-5157d15a-7e0e-4ab1-af48-3d98010cd152-0', tool_calls=[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_g4RuAijtDcSeM96jXyCuiLSN'}], usage_metadata={'input_tokens': 95, 'output_tokens': 18, 'total_tokens': 113})"
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_wLTBasMppAwpdiA5CD92l9x7', 'function': {'arguments': '{\"a\":3,\"b\":12}', 'name': 'Multiply'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 89, 'total_tokens': 107}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0f03d4f0ee', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-d3f36cca-f225-416f-ac16-0217046f0b38-0', tool_calls=[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_wLTBasMppAwpdiA5CD92l9x7', 'type': 'tool_call'}], usage_metadata={'input_tokens': 89, 'output_tokens': 18, 'total_tokens': 107})"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
@@ -214,7 +190,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"As we can see, even though the prompt didn't really suggest a tool call, our LLM made one since it was forced to do so. You can look at the docs for [bind_tools()](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.BaseChatOpenAI.html#langchain_openai.chat_models.base.BaseChatOpenAI.bind_tools) to learn about all the ways to customize how your LLM selects tools."
|
||||
"As we can see our LLM generated arguments to a tool! You can look at the docs for [bind_tools()](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.BaseChatOpenAI.html#langchain_openai.chat_models.base.BaseChatOpenAI.bind_tools) to learn about all the ways to customize how your LLM selects tools, as well as [this guide on how to force the LLM to call a tool](/docs/how_to/tool_choice/) rather than letting it decide."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -246,10 +222,12 @@
|
||||
"text/plain": [
|
||||
"[{'name': 'Multiply',\n",
|
||||
" 'args': {'a': 3, 'b': 12},\n",
|
||||
" 'id': 'call_TnadLbWJu9HwDULRb51RNSMw'},\n",
|
||||
" 'id': 'call_uqJsNrDJ8ZZnFa1BHHYAllEv',\n",
|
||||
" 'type': 'tool_call'},\n",
|
||||
" {'name': 'Add',\n",
|
||||
" 'args': {'a': 11, 'b': 49},\n",
|
||||
" 'id': 'call_Q9vt1up05sOQScXvUYWzSpCg'}]"
|
||||
" 'id': 'call_ud1uHAaYsdpWuxugwoJ63BDs',\n",
|
||||
" 'type': 'tool_call'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
@@ -308,17 +286,17 @@
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"Now you've learned how to bind tool schemas to a chat model and to call those tools. Next, you can learn more about how to use tools:\n",
|
||||
"Now you've learned how to bind tool schemas to a chat model and have the model call the tool.\n",
|
||||
"\n",
|
||||
"Next, check out this guide on actually using the tool by invoking the function and passing the results back to the model:\n",
|
||||
"\n",
|
||||
"- Few shot promting [with tools](/docs/how_to/tools_few_shot/)\n",
|
||||
"- Stream [tool calls](/docs/how_to/tool_streaming/)\n",
|
||||
"- Bind [model-specific tools](/docs/how_to/tools_model_specific/)\n",
|
||||
"- Pass [runtime values to tools](/docs/how_to/tool_runtime)\n",
|
||||
"- Pass [tool results back to model](/docs/how_to/tool_results_pass_to_model)\n",
|
||||
"\n",
|
||||
"You can also check out some more specific uses of tool calling:\n",
|
||||
"\n",
|
||||
"- Building [tool-using chains and agents](/docs/how_to#tools)\n",
|
||||
"- Few shot prompting [with tools](/docs/how_to/tools_few_shot/)\n",
|
||||
"- Stream [tool calls](/docs/how_to/tool_streaming/)\n",
|
||||
"- Pass [runtime values to tools](/docs/how_to/tool_runtime)\n",
|
||||
"- Getting [structured outputs](/docs/how_to/structured_output/) from models"
|
||||
]
|
||||
}
|
||||
@@ -339,7 +317,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.10.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -9,12 +9,34 @@
|
||||
":::info Prerequisites\n",
|
||||
"This guide assumes familiarity with the following concepts:\n",
|
||||
"\n",
|
||||
"- [Tools](/docs/concepts/#tools)\n",
|
||||
"- [LangChain Tools](/docs/concepts/#tools)\n",
|
||||
"- [Function/tool calling](/docs/concepts/#functiontool-calling)\n",
|
||||
"- [Using chat models to call tools](/docs/how_to/tool_calling)\n",
|
||||
"- [Defining custom tools](/docs/how_to/custom_tools/)\n",
|
||||
"\n",
|
||||
":::\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 and `ToolCall`s. First, let's define our tools and our model."
|
||||
"Some models are capable of [**tool calling**](/docs/concepts/#functiontool-calling) - generating arguments that conform to a specific user-provided schema. This guide will demonstrate how to use those tool cals to actually call a function and properly pass the results back to the model.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"First, let's define our tools and our model:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"```{=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",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -22,6 +44,25 @@
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | output: false\n",
|
||||
"# | echo: false\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass()\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.tools import tool\n",
|
||||
"\n",
|
||||
@@ -38,23 +79,8 @@
|
||||
" return a * b\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [add, multiply]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"tools = [add, multiply]\n",
|
||||
"\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass()\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"llm_with_tools = llm.bind_tools(tools)"
|
||||
]
|
||||
},
|
||||
@@ -62,15 +88,88 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The nice thing about Tools is that if we invoke them with a ToolCall, we'll automatically get back a ToolMessage that can be fed back to the model: \n",
|
||||
"Now, let's get the model to call a tool. We'll add it to a list of messages that we'll treat as conversation history:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_GPGPE943GORirhIAYnWv00rK', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': 'call_dm8o64ZrY3WFZHAvCh1bEJ6i', 'type': 'tool_call'}]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"\n",
|
||||
":::info Requires ``langchain-core >= 0.2.19``\n",
|
||||
"query = \"What is 3 * 12? Also, what is 11 + 49?\"\n",
|
||||
"\n",
|
||||
"This functionality was added in ``langchain-core == 0.2.19``. Please make sure your package is up to date.\n",
|
||||
"messages = [HumanMessage(query)]\n",
|
||||
"\n",
|
||||
"ai_msg = llm_with_tools.invoke(messages)\n",
|
||||
"\n",
|
||||
"print(ai_msg.tool_calls)\n",
|
||||
"\n",
|
||||
"messages.append(ai_msg)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Next let's invoke the tool functions using the args the model populated!\n",
|
||||
"\n",
|
||||
"Conveniently, if we invoke a LangChain `Tool` with a `ToolCall`, we'll automatically get back a `ToolMessage` that can be fed back to the model:\n",
|
||||
"\n",
|
||||
":::caution Compatibility\n",
|
||||
"\n",
|
||||
"This functionality was added in `langchain-core == 0.2.19`. Please make sure your package is up to date.\n",
|
||||
"\n",
|
||||
"If you are on earlier versions of `langchain-core`, you will need to extract the `args` field from the tool and construct a `ToolMessage` manually.\n",
|
||||
"\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"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_loT2pliJwJe3p7nkgXYF48A1', 'function': {'arguments': '{\"a\": 3, \"b\": 12}', 'name': 'multiply'}, 'type': 'function'}, {'id': 'call_bG9tYZCXOeYDZf3W46TceoV4', 'function': {'arguments': '{\"a\": 11, \"b\": 49}', 'name': 'add'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 50, 'prompt_tokens': 87, 'total_tokens': 137}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_661538dc1f', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-e3db3c46-bf9e-478e-abc1-dc9a264f4afe-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_loT2pliJwJe3p7nkgXYF48A1', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': 'call_bG9tYZCXOeYDZf3W46TceoV4', 'type': 'tool_call'}], usage_metadata={'input_tokens': 87, 'output_tokens': 50, 'total_tokens': 137}),\n",
|
||||
" ToolMessage(content='36', name='multiply', tool_call_id='call_loT2pliJwJe3p7nkgXYF48A1'),\n",
|
||||
" ToolMessage(content='60', name='add', tool_call_id='call_bG9tYZCXOeYDZf3W46TceoV4')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for tool_call in ai_msg.tool_calls:\n",
|
||||
" selected_tool = {\"add\": add, \"multiply\": multiply}[tool_call[\"name\"].lower()]\n",
|
||||
" tool_msg = selected_tool.invoke(tool_call)\n",
|
||||
" messages.append(tool_msg)\n",
|
||||
"\n",
|
||||
"messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And finally, we'll invoke the model with the tool results. The model will use this information to generate a final answer to our original query:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
@@ -79,10 +178,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[HumanMessage(content='What is 3 * 12? Also, what is 11 + 49?'),\n",
|
||||
" AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Smg3NHJNxrKfAmd4f9GkaYn3', 'function': {'arguments': '{\"a\": 3, \"b\": 12}', 'name': 'multiply'}, 'type': 'function'}, {'id': 'call_55K1C0DmH6U5qh810gW34xZ0', 'function': {'arguments': '{\"a\": 11, \"b\": 49}', 'name': 'add'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 49, 'prompt_tokens': 88, 'total_tokens': 137}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-56657feb-96dd-456c-ab8e-1857eab2ade0-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_Smg3NHJNxrKfAmd4f9GkaYn3', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': 'call_55K1C0DmH6U5qh810gW34xZ0', 'type': 'tool_call'}], usage_metadata={'input_tokens': 88, 'output_tokens': 49, 'total_tokens': 137}),\n",
|
||||
" ToolMessage(content='36', name='multiply', tool_call_id='call_Smg3NHJNxrKfAmd4f9GkaYn3'),\n",
|
||||
" ToolMessage(content='60', name='add', tool_call_id='call_55K1C0DmH6U5qh810gW34xZ0')]"
|
||||
"AIMessage(content='The result of \\\\(3 \\\\times 12\\\\) is 36, and the result of \\\\(11 + 49\\\\) is 60.', response_metadata={'token_usage': {'completion_tokens': 31, 'prompt_tokens': 153, 'total_tokens': 184}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_661538dc1f', 'finish_reason': 'stop', 'logprobs': None}, id='run-87d1ef0a-1223-4bb3-9310-7b591789323d-0', usage_metadata={'input_tokens': 153, 'output_tokens': 31, 'total_tokens': 184})"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
@@ -90,37 +186,6 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage, ToolMessage\n",
|
||||
"\n",
|
||||
"query = \"What is 3 * 12? Also, what is 11 + 49?\"\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_msg = selected_tool.invoke(tool_call)\n",
|
||||
" messages.append(tool_msg)\n",
|
||||
"messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"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': 153, 'total_tokens': 171}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-ba5032f0-f773-406d-a408-8314e66511d0-0', usage_metadata={'input_tokens': 153, 'output_tokens': 18, 'total_tokens': 171})"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_tools.invoke(messages)"
|
||||
]
|
||||
@@ -129,15 +194,25 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that we pass back the same `id` in the `ToolMessage` as the what we receive from the model in order to help the model match tool responses with tool calls."
|
||||
"Note that each `ToolMessage` must include a `tool_call_id` that matches an `id` in the original tool calls that the model generates. This helps the model match tool responses with tool calls.\n",
|
||||
"\n",
|
||||
"Tool calling agents, like those in [LangGraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/), use this basic flow to answer queries and solve tasks.\n",
|
||||
"\n",
|
||||
"## Related\n",
|
||||
"\n",
|
||||
"- [LangGraph quickstart](https://langchain-ai.github.io/langgraph/tutorials/introduction/)\n",
|
||||
"- Few shot prompting [with tools](/docs/how_to/tools_few_shot/)\n",
|
||||
"- Stream [tool calls](/docs/how_to/tool_streaming/)\n",
|
||||
"- Pass [runtime values to tools](/docs/how_to/tool_runtime)\n",
|
||||
"- Getting [structured outputs](/docs/how_to/structured_output/) from models"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv-311",
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "poetry-venv-311"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
@@ -149,7 +224,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
"version": "3.10.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -2,298 +2,259 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "raw"
|
||||
}
|
||||
},
|
||||
"id": "afaf8039",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"sidebar_label: Groq\n",
|
||||
"keywords: [chatgroq]\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e49f1e0d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Groq\n",
|
||||
"# ChatGroq\n",
|
||||
"\n",
|
||||
"LangChain supports integration with [Groq](https://groq.com/) chat models. Groq specializes in fast AI inference.\n",
|
||||
"This will help you getting started with Groq [chat models](../../concepts.mdx#chat-models). For detailed documentation of all ChatGroq features and configurations head to the [API reference](https://api.python.langchain.com/en/latest/chat_models/langchain_groq.chat_models.ChatGroq.html). For a list of all Groq models, visit this [link](https://console.groq.com/docs/models).\n",
|
||||
"\n",
|
||||
"To get started, you'll first need to install the langchain-groq package:"
|
||||
"## Overview\n",
|
||||
"### Integration details\n",
|
||||
"\n",
|
||||
"| Class | Package | Local | Serializable | [JS support](https://js.langchain.com/v0.2/docs/integrations/chat/groq) | Package downloads | Package latest |\n",
|
||||
"| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| [ChatGroq](https://api.python.langchain.com/en/latest/chat_models/langchain_groq.chat_models.ChatGroq.html) | [langchain-groq](https://api.python.langchain.com/en/latest/groq_api_reference.html) | ❌ | beta | ✅ |  |  |\n",
|
||||
"\n",
|
||||
"### Model features\n",
|
||||
"| [Tool calling](../../how_to/tool_calling.ipynb) | [Structured output](../../how_to/structured_output.ipynb) | JSON mode | [Image input](../../how_to/multimodal_inputs.ipynb) | Audio input | Video input | [Token-level streaming](../../how_to/chat_streaming.ipynb) | Native async | [Token usage](../../how_to/chat_token_usage_tracking.ipynb) | [Logprobs](../../how_to/logprobs.ipynb) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"To access Groq models you'll need to create a Groq account, get an API key, and install the `langchain-groq` integration package.\n",
|
||||
"\n",
|
||||
"### Credentials\n",
|
||||
"\n",
|
||||
"Head to the [Groq console](https://console.groq.com/keys) to sign up to Groq and generate an API key. Once you've done this set the GROQ_API_KEY environment variable:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"id": "433e8d2b-9519-4b49-b2c4-7ab65b046c94",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-groq"
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"GROQ_API_KEY\"] = getpass.getpass(\"Enter your Groq API key: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "72ee0c4b-9764-423a-9dbf-95129e185210",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Request an [API key](https://wow.groq.com) and set it as an environment variable:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"export GROQ_API_KEY=<YOUR API KEY>\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Alternatively, you may configure the API key when you initialize ChatGroq.\n",
|
||||
"\n",
|
||||
"Here's an example of it in action:"
|
||||
"If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 2,
|
||||
"id": "a15d341e-3e26-4ca3-830b-5aab30ed66de",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content=\"Low latency is crucial for Large Language Models (LLMs) because it directly impacts the user experience, model performance, and overall efficiency. Here are some reasons why low latency is essential for LLMs:\\n\\n1. **Real-time Interaction**: LLMs are often used in applications that require real-time interaction, such as chatbots, virtual assistants, and language translation. Low latency ensures that the model responds quickly to user input, providing a seamless and engaging experience.\\n2. **Conversational Flow**: In conversational AI, latency can disrupt the natural flow of conversation. Low latency helps maintain a smooth conversation, allowing users to respond quickly and naturally, without feeling like they're waiting for the model to catch up.\\n3. **Model Performance**: High latency can lead to increased error rates, as the model may struggle to keep up with the input pace. Low latency enables the model to process information more efficiently, resulting in better accuracy and performance.\\n4. **Scalability**: As the number of users and requests increases, low latency becomes even more critical. It allows the model to handle a higher volume of requests without sacrificing performance, making it more scalable and efficient.\\n5. **Resource Utilization**: Low latency can reduce the computational resources required to process requests. By minimizing latency, you can optimize resource allocation, reduce costs, and improve overall system efficiency.\\n6. **User Experience**: High latency can lead to frustration, abandonment, and a poor user experience. Low latency ensures that users receive timely responses, which is essential for building trust and satisfaction.\\n7. **Competitive Advantage**: In applications like customer service or language translation, low latency can be a key differentiator. It can provide a competitive advantage by offering a faster and more responsive experience, setting your application apart from others.\\n8. **Edge Computing**: With the increasing adoption of edge computing, low latency is critical for processing data closer to the user. This reduces latency even further, enabling real-time processing and analysis of data.\\n9. **Real-time Analytics**: Low latency enables real-time analytics and insights, which are essential for applications like sentiment analysis, trend detection, and anomaly detection.\\n10. **Future-Proofing**: As LLMs continue to evolve and become more complex, low latency will become even more critical. By prioritizing low latency now, you'll be better prepared to handle the demands of future LLM applications.\\n\\nIn summary, low latency is vital for LLMs because it ensures a seamless user experience, improves model performance, and enables efficient resource utilization. By prioritizing low latency, you can build more effective, scalable, and efficient LLM applications that meet the demands of real-time interaction and processing.\", response_metadata={'token_usage': {'completion_tokens': 541, 'prompt_tokens': 33, 'total_tokens': 574, 'completion_time': 1.499777658, 'prompt_time': 0.008344704, 'queue_time': None, 'total_time': 1.508122362}, 'model_name': 'llama3-70b-8192', 'system_fingerprint': 'fp_87cbfbbc4d', 'finish_reason': 'stop', 'logprobs': None}, id='run-49dad960-ace8-4cd7-90b3-2db99ecbfa44-0')"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_groq import ChatGroq\n",
|
||||
"\n",
|
||||
"chat = ChatGroq(\n",
|
||||
" temperature=0,\n",
|
||||
" model=\"llama3-70b-8192\",\n",
|
||||
" # api_key=\"\" # Optional if not set as an environment variable\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"system = \"You are a helpful assistant.\"\n",
|
||||
"human = \"{text}\"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages([(\"system\", system), (\"human\", human)])\n",
|
||||
"\n",
|
||||
"chain = prompt | chat\n",
|
||||
"chain.invoke({\"text\": \"Explain the importance of low latency for LLMs.\"})"
|
||||
"# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")\n",
|
||||
"# os.environ[\"LANGSMITH_TRACING\"] = \"true\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0730d6a1-c893-4840-9817-5e5251676d5d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can view the available models [here](https://console.groq.com/docs/models).\n",
|
||||
"### Installation\n",
|
||||
"\n",
|
||||
"## Tool calling\n",
|
||||
"\n",
|
||||
"Groq chat models support [tool calling](/docs/how_to/tool_calling) to generate output matching a specific schema. The model may choose to call multiple tools or the same tool multiple times if appropriate.\n",
|
||||
"\n",
|
||||
"Here's an example:"
|
||||
"The LangChain Groq integration lives in the `langchain-groq` package:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'name': 'get_current_weather',\n",
|
||||
" 'args': {'location': 'San Francisco', 'unit': 'Celsius'},\n",
|
||||
" 'id': 'call_pydj'},\n",
|
||||
" {'name': 'get_current_weather',\n",
|
||||
" 'args': {'location': 'Tokyo', 'unit': 'Celsius'},\n",
|
||||
" 'id': 'call_jgq3'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from typing import Optional\n",
|
||||
"\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def get_current_weather(location: str, unit: Optional[str]):\n",
|
||||
" \"\"\"Get the current weather in a given location\"\"\"\n",
|
||||
" return \"Cloudy with a chance of rain.\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tool_model = chat.bind_tools([get_current_weather], tool_choice=\"auto\")\n",
|
||||
"\n",
|
||||
"res = tool_model.invoke(\"What is the weather like in San Francisco and Tokyo?\")\n",
|
||||
"\n",
|
||||
"res.tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### `.with_structured_output()`\n",
|
||||
"\n",
|
||||
"You can also use the convenience [`.with_structured_output()`](/docs/how_to/structured_output/#the-with_structured_output-method) method to coerce `ChatGroq` into returning a structured output.\n",
|
||||
"Here is an example:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Joke(setup='Why did the cat join a band?', punchline='Because it wanted to be the purr-cussionist!', rating=None)"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Joke(BaseModel):\n",
|
||||
" \"\"\"Joke to tell user.\"\"\"\n",
|
||||
"\n",
|
||||
" setup: str = Field(description=\"The setup of the joke\")\n",
|
||||
" punchline: str = Field(description=\"The punchline to the joke\")\n",
|
||||
" rating: Optional[int] = Field(description=\"How funny the joke is, from 1 to 10\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"structured_llm = chat.with_structured_output(Joke)\n",
|
||||
"\n",
|
||||
"structured_llm.invoke(\"Tell me a joke about cats\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Behind the scenes, this takes advantage of the above tool calling functionality.\n",
|
||||
"\n",
|
||||
"## Async"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Here is a limerick about the sun:\\n\\nThere once was a sun in the sky,\\nWhose warmth and light caught the eye,\\nIt shone bright and bold,\\nWith a fiery gold,\\nAnd brought life to all, as it flew by.', response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 18, 'total_tokens': 69, 'completion_time': 0.144614022, 'prompt_time': 0.00585394, 'queue_time': None, 'total_time': 0.150467962}, 'model_name': 'llama3-70b-8192', 'system_fingerprint': 'fp_2f30b0b571', 'finish_reason': 'stop', 'logprobs': None}, id='run-e42340ba-f0ad-4b54-af61-8308d8ec8256-0')"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chat = ChatGroq(temperature=0, model=\"llama3-70b-8192\")\n",
|
||||
"prompt = ChatPromptTemplate.from_messages([(\"human\", \"Write a Limerick about {topic}\")])\n",
|
||||
"chain = prompt | chat\n",
|
||||
"await chain.ainvoke({\"topic\": \"The Sun\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Streaming"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 3,
|
||||
"id": "652d6238-1f87-422a-b135-f5abbb8652fc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Silvery glow bright\n",
|
||||
"Luna's gentle light shines down\n",
|
||||
"Midnight's gentle queen"
|
||||
"\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.1.2\u001b[0m\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
|
||||
"Note: you may need to restart the kernel to use updated packages.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chat = ChatGroq(temperature=0, model=\"llama3-70b-8192\")\n",
|
||||
"prompt = ChatPromptTemplate.from_messages([(\"human\", \"Write a haiku about {topic}\")])\n",
|
||||
"chain = prompt | chat\n",
|
||||
"for chunk in chain.stream({\"topic\": \"The Moon\"}):\n",
|
||||
" print(chunk.content, end=\"\", flush=True)"
|
||||
"%pip install -qU langchain-groq"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a38cde65-254d-4219-a441-068766c0d4b5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Passing custom parameters\n",
|
||||
"## Instantiation\n",
|
||||
"\n",
|
||||
"You can pass other Groq-specific parameters using the `model_kwargs` argument on initialization. Here's an example of enabling JSON mode:"
|
||||
"Now we can instantiate our model object and generate chat completions:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 4,
|
||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_groq import ChatGroq\n",
|
||||
"\n",
|
||||
"llm = ChatGroq(\n",
|
||||
" model=\"mixtral-8x7b-32768\",\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=None,\n",
|
||||
" timeout=None,\n",
|
||||
" max_retries=2,\n",
|
||||
" # other params...\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2b4f3e15",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Invocation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "62e0dbc3",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='{ \"response\": \"That\\'s a tough question! There are eight species of bears found in the world, and each one is unique and amazing in its own way. However, if I had to pick one, I\\'d say the giant panda is a popular favorite among many people. Who can resist those adorable black and white markings?\", \"followup_question\": \"Would you like to know more about the giant panda\\'s habitat and diet?\" }', response_metadata={'token_usage': {'completion_tokens': 89, 'prompt_tokens': 50, 'total_tokens': 139, 'completion_time': 0.249032839, 'prompt_time': 0.011134497, 'queue_time': None, 'total_time': 0.260167336}, 'model_name': 'llama3-70b-8192', 'system_fingerprint': 'fp_2f30b0b571', 'finish_reason': 'stop', 'logprobs': None}, id='run-558ce67e-8c63-43fe-a48f-6ecf181bc922-0')"
|
||||
"AIMessage(content='I enjoy programming. (The French translation is: \"J\\'aime programmer.\")\\n\\nNote: I chose to translate \"I love programming\" as \"J\\'aime programmer\" instead of \"Je suis amoureux de programmer\" because the latter has a romantic connotation that is not present in the original English sentence.', response_metadata={'token_usage': {'completion_tokens': 73, 'prompt_tokens': 31, 'total_tokens': 104, 'completion_time': 0.1140625, 'prompt_time': 0.003352463, 'queue_time': None, 'total_time': 0.117414963}, 'model_name': 'mixtral-8x7b-32768', 'system_fingerprint': 'fp_c5f20b5bb1', 'finish_reason': 'stop', 'logprobs': None}, id='run-64433c19-eadf-42fc-801e-3071e3c40160-0', usage_metadata={'input_tokens': 31, 'output_tokens': 73, 'total_tokens': 104})"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chat = ChatGroq(\n",
|
||||
" model=\"llama3-70b-8192\", model_kwargs={\"response_format\": {\"type\": \"json_object\"}}\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"system = \"\"\"\n",
|
||||
"You are a helpful assistant.\n",
|
||||
"Always respond with a JSON object with two string keys: \"response\" and \"followup_question\".\n",
|
||||
"\"\"\"\n",
|
||||
"human = \"{question}\"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages([(\"system\", system), (\"human\", human)])\n",
|
||||
"\n",
|
||||
"chain = prompt | chat\n",
|
||||
"\n",
|
||||
"chain.invoke({\"question\": \"what bear is best?\"})"
|
||||
"messages = [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"You are a helpful assistant that translates English to French. Translate the user sentence.\",\n",
|
||||
" ),\n",
|
||||
" (\"human\", \"I love programming.\"),\n",
|
||||
"]\n",
|
||||
"ai_msg = llm.invoke(messages)\n",
|
||||
"ai_msg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 6,
|
||||
"id": "d86145b3-bfef-46e8-b227-4dda5c9c2705",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"I enjoy programming. (The French translation is: \"J'aime programmer.\")\n",
|
||||
"\n",
|
||||
"Note: I chose to translate \"I love programming\" as \"J'aime programmer\" instead of \"Je suis amoureux de programmer\" because the latter has a romantic connotation that is not present in the original English sentence.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(ai_msg.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "18e2bfc0-7e78-4528-a73f-499ac150dca8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chaining\n",
|
||||
"\n",
|
||||
"We can [chain](../../how_to/sequence.ipynb) our model with a prompt template like so:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "e197d1d7-a070-4c96-9f8a-a0e86d046e0b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='That\\'s great! I can help you translate English phrases related to programming into German.\\n\\n\"I love programming\" can be translated as \"Ich liebe Programmieren\" in German.\\n\\nHere are some more programming-related phrases translated into German:\\n\\n* \"Programming language\" = \"Programmiersprache\"\\n* \"Code\" = \"Code\"\\n* \"Variable\" = \"Variable\"\\n* \"Function\" = \"Funktion\"\\n* \"Array\" = \"Array\"\\n* \"Object-oriented programming\" = \"Objektorientierte Programmierung\"\\n* \"Algorithm\" = \"Algorithmus\"\\n* \"Data structure\" = \"Datenstruktur\"\\n* \"Debugging\" = \"Fehlersuche\"\\n* \"Compile\" = \"Kompilieren\"\\n* \"Link\" = \"Verknüpfen\"\\n* \"Run\" = \"Ausführen\"\\n* \"Test\" = \"Testen\"\\n* \"Deploy\" = \"Bereitstellen\"\\n* \"Version control\" = \"Versionskontrolle\"\\n* \"Open source\" = \"Open Source\"\\n* \"Software development\" = \"Softwareentwicklung\"\\n* \"Agile methodology\" = \"Agile Methodik\"\\n* \"DevOps\" = \"DevOps\"\\n* \"Cloud computing\" = \"Cloud Computing\"\\n\\nI hope this helps! Let me know if you have any other questions or if you need further translations.', response_metadata={'token_usage': {'completion_tokens': 331, 'prompt_tokens': 25, 'total_tokens': 356, 'completion_time': 0.520006542, 'prompt_time': 0.00250165, 'queue_time': None, 'total_time': 0.522508192}, 'model_name': 'mixtral-8x7b-32768', 'system_fingerprint': 'fp_c5f20b5bb1', 'finish_reason': 'stop', 'logprobs': None}, id='run-74207fb7-85d3-417d-b2b9-621116b75d41-0', usage_metadata={'input_tokens': 25, 'output_tokens': 331, 'total_tokens': 356})"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"You are a helpful assistant that translates {input_language} to {output_language}.\",\n",
|
||||
" ),\n",
|
||||
" (\"human\", \"{input}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chain = prompt | llm\n",
|
||||
"chain.invoke(\n",
|
||||
" {\n",
|
||||
" \"input_language\": \"English\",\n",
|
||||
" \"output_language\": \"German\",\n",
|
||||
" \"input\": \"I love programming.\",\n",
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all ChatGroq features and configurations head to the API reference: https://api.python.langchain.com/en/latest/chat_models/langchain_groq.chat_models.ChatGroq.html"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -307,9 +268,9 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.5"
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
||||
@@ -302,9 +302,6 @@
|
||||
"\n",
|
||||
"NVIDIA also supports multimodal inputs, meaning you can provide both images and text for the model to reason over. An example model supporting multimodal inputs is `nvidia/neva-22b`.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"These models accept LangChain's standard image formats, and accept `labels`, similar to the Steering LLMs above. In addition to `creativity`, `complexity`, and `verbosity`, these models support a `quality` toggle.\n",
|
||||
"\n",
|
||||
"Below is an example use:"
|
||||
]
|
||||
},
|
||||
@@ -447,92 +444,6 @@
|
||||
"llm.invoke(f'What\\'s in this image?\\n<img src=\"{base64_with_mime_type}\" />')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3e61d868",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### **Advanced Use Case:** Forcing Payload \n",
|
||||
"\n",
|
||||
"You may notice that some newer models may have strong parameter expectations that the LangChain connector may not support by default. For example, we cannot invoke the [Kosmos](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/ai-foundation/models/kosmos-2) model at the time of this notebook's latest release due to the lack of a streaming argument on the server side: "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d143e0d6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
|
||||
"\n",
|
||||
"kosmos = ChatNVIDIA(model=\"microsoft/kosmos-2\")\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"\n",
|
||||
"# kosmos.invoke(\n",
|
||||
"# [\n",
|
||||
"# HumanMessage(\n",
|
||||
"# content=[\n",
|
||||
"# {\"type\": \"text\", \"text\": \"Describe this image:\"},\n",
|
||||
"# {\"type\": \"image_url\", \"image_url\": {\"url\": image_url}},\n",
|
||||
"# ]\n",
|
||||
"# )\n",
|
||||
"# ]\n",
|
||||
"# )\n",
|
||||
"\n",
|
||||
"# Exception: [422] Unprocessable Entity\n",
|
||||
"# body -> stream\n",
|
||||
"# Extra inputs are not permitted (type=extra_forbidden)\n",
|
||||
"# RequestID: 35538c9a-4b45-4616-8b75-7ef816fccf38"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1e230b70",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"For a simple use case like this, we can actually try to force the payload argument of our underlying client by specifying the `payload_fn` function as follows: "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0925b2b1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def drop_streaming_key(d):\n",
|
||||
" \"\"\"Takes in payload dictionary, outputs new payload dictionary\"\"\"\n",
|
||||
" if \"stream\" in d:\n",
|
||||
" d.pop(\"stream\")\n",
|
||||
" return d\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Override the payload passthrough. Default is to pass through the payload as is.\n",
|
||||
"kosmos = ChatNVIDIA(model=\"microsoft/kosmos-2\")\n",
|
||||
"kosmos.client.payload_fn = drop_streaming_key\n",
|
||||
"\n",
|
||||
"kosmos.invoke(\n",
|
||||
" [\n",
|
||||
" HumanMessage(\n",
|
||||
" content=[\n",
|
||||
" {\"type\": \"text\", \"text\": \"Describe this image:\"},\n",
|
||||
" {\"type\": \"image_url\", \"image_url\": {\"url\": image_url}},\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fe6e1758",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"For more advanced or custom use-cases (i.e. supporting the diffusion models), you may be interested in leveraging the `NVEModel` client as a requests backbone. The `NVIDIAEmbeddings` class is a good source of inspiration for this. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "137662a6",
|
||||
@@ -540,7 +451,7 @@
|
||||
"id": "137662a6"
|
||||
},
|
||||
"source": [
|
||||
"## Example usage within RunnableWithMessageHistory "
|
||||
"## Example usage within a RunnableWithMessageHistory"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -630,14 +541,14 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "uHIMZxVSVNBC",
|
||||
"id": "LyD1xVKmVSs4",
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 284
|
||||
"height": 350
|
||||
},
|
||||
"id": "uHIMZxVSVNBC",
|
||||
"outputId": "79acc89d-a820-4f2c-bac2-afe99da95580"
|
||||
"id": "LyD1xVKmVSs4",
|
||||
"outputId": "a1714513-a8fd-4d14-f974-233e39d5c4f5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -646,6 +557,79 @@
|
||||
" config=config,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f3cbbba0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Tool calling\n",
|
||||
"\n",
|
||||
"Starting in v0.2, `ChatNVIDIA` supports [bind_tools](https://api.python.langchain.com/en/latest/language_models/langchain_core.language_models.chat_models.BaseChatModel.html#langchain_core.language_models.chat_models.BaseChatModel.bind_tools).\n",
|
||||
"\n",
|
||||
"`ChatNVIDIA` provides integration with the variety of models on [build.nvidia.com](https://build.nvidia.com) as well as local NIMs. Not all these models are trained for tool calling. Be sure to select a model that does have tool calling for your experimention and applications."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6f7b535e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can get a list of models that are known to support tool calling with,"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e36c8911",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tool_models = [\n",
|
||||
" model for model in ChatNVIDIA.get_available_models() if model.supports_tools\n",
|
||||
"]\n",
|
||||
"tool_models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b01d75a7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"With a tool capable model,"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bd54f174",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.pydantic_v1 import Field\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def get_current_weather(\n",
|
||||
" location: str = Field(..., description=\"The location to get the weather for.\"),\n",
|
||||
"):\n",
|
||||
" \"\"\"Get the current weather for a location.\"\"\"\n",
|
||||
" ...\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"llm = ChatNVIDIA(model=tool_models[0].id).bind_tools(tools=[get_current_weather])\n",
|
||||
"response = llm.invoke(\"What is the weather in Boston?\")\n",
|
||||
"response.tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e08df68c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"See [How to use chat models to call tools](https://python.langchain.com/v0.2/docs/how_to/tool_calling/) for additional examples."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -667,7 +651,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.2"
|
||||
"version": "3.10.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"### Model features\n",
|
||||
"| [Tool calling](/docs/how_to/tool_calling/) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | \n",
|
||||
"| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"### Model features\n",
|
||||
"| [Tool calling](/docs/how_to/tool_calling/) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | \n",
|
||||
"| ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
|
||||
@@ -284,7 +284,9 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "For more on binding tools and tool call outputs, head to the [tool calling](docs/how_to/function_calling) docs."
|
||||
"source": [
|
||||
"For more on binding tools and tool call outputs, head to the [tool calling](../../how_to/function_calling.ipynb) docs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
|
||||
@@ -1,103 +1,263 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2970dd75-8ebf-4b51-8282-9b454b8f356d",
|
||||
"cell_type": "raw",
|
||||
"id": "afaf8039",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Together AI\n",
|
||||
"\n",
|
||||
"[Together AI](https://www.together.ai/) offers an API to query [50+ leading open-source models](https://docs.together.ai/docs/inference-models) in a couple lines of code.\n",
|
||||
"\n",
|
||||
"This example goes over how to use LangChain to interact with Together AI models."
|
||||
"---\n",
|
||||
"sidebar_label: Together\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1c47fc36",
|
||||
"id": "e49f1e0d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Installation"
|
||||
"# ChatTogether\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This page will help you get started with Together AI [chat models](../../concepts.mdx#chat-models). For detailed documentation of all ChatTogether features and configurations head to the [API reference](https://api.python.langchain.com/en/latest/chat_models/langchain_together.chat_models.ChatTogether.html).\n",
|
||||
"\n",
|
||||
"[Together AI](https://www.together.ai/) offers an API to query [50+ leading open-source models](https://docs.together.ai/docs/chat-models)\n",
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"### Integration details\n",
|
||||
"\n",
|
||||
"| Class | Package | Local | Serializable | [JS support](https://js.langchain.com/v0.2/docs/integrations/chat/togetherai) | Package downloads | Package latest |\n",
|
||||
"| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| [ChatTogether](https://api.python.langchain.com/en/latest/chat_models/langchain_together.chat_models.ChatTogether.html) | [langchain-together](https://api.python.langchain.com/en/latest/together_api_reference.html) | ❌ | beta | ✅ |  |  |\n",
|
||||
"\n",
|
||||
"### Model features\n",
|
||||
"| [Tool calling](../../how_to/tool_calling.ipynb) | [Structured output](../../how_to/structured_output.ipynb) | JSON mode | [Image input](../../how_to/multimodal_inputs.ipynb) | Audio input | Video input | [Token-level streaming](../../how_to/chat_streaming.ipynb) | Native async | [Token usage](../../how_to/chat_token_usage_tracking.ipynb) | [Logprobs](../../how_to/logprobs.ipynb) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"To access Together models you'll need to create a/an Together account, get an API key, and install the `langchain-together` integration package.\n",
|
||||
"\n",
|
||||
"### Credentials\n",
|
||||
"\n",
|
||||
"Head to [this page](https://api.together.ai) to sign up to Together and generate an API key. Once you've done this set the TOGETHER_API_KEY environment variable:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1ecdb29d",
|
||||
"execution_count": 1,
|
||||
"id": "433e8d2b-9519-4b49-b2c4-7ab65b046c94",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade langchain-together"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "89883202",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Environment\n",
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"To use Together AI, you'll need an API key which you can find here:\n",
|
||||
"https://api.together.ai/settings/api-keys. This can be passed in as an init param\n",
|
||||
"``together_api_key`` or set as environment variable ``TOGETHER_API_KEY``.\n"
|
||||
"os.environ[\"TOGETHER_API_KEY\"] = getpass.getpass(\"Enter your Together API key: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8304b4d9",
|
||||
"id": "72ee0c4b-9764-423a-9dbf-95129e185210",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example"
|
||||
"If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "637bb53f",
|
||||
"execution_count": 2,
|
||||
"id": "a15d341e-3e26-4ca3-830b-5aab30ed66de",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Querying chat models with Together AI\n",
|
||||
"# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")\n",
|
||||
"# os.environ[\"LANGSMITH_TRACING\"] = \"true\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0730d6a1-c893-4840-9817-5e5251676d5d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Installation\n",
|
||||
"\n",
|
||||
"The LangChain Together integration lives in the `langchain-together` package:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "652d6238-1f87-422a-b135-f5abbb8652fc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.1.2\u001b[0m\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
|
||||
"Note: you may need to restart the kernel to use updated packages.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install -qU langchain-together"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a38cde65-254d-4219-a441-068766c0d4b5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Instantiation\n",
|
||||
"\n",
|
||||
"Now we can instantiate our model object and generate chat completions:\n",
|
||||
"\n",
|
||||
"- TODO: Update model instantiation with relevant params."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_together import ChatTogether\n",
|
||||
"\n",
|
||||
"# choose from our 50+ models here: https://docs.together.ai/docs/inference-models\n",
|
||||
"chat = ChatTogether(\n",
|
||||
" # together_api_key=\"YOUR_API_KEY\",\n",
|
||||
"llm = ChatTogether(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# stream the response back from the model\n",
|
||||
"for m in chat.stream(\"Tell me fun things to do in NYC\"):\n",
|
||||
" print(m.content, end=\"\", flush=True)\n",
|
||||
"\n",
|
||||
"# if you don't want to do streaming, you can use the invoke method\n",
|
||||
"# chat.invoke(\"Tell me fun things to do in NYC\")"
|
||||
" temperature=0,\n",
|
||||
" max_tokens=None,\n",
|
||||
" timeout=None,\n",
|
||||
" max_retries=2,\n",
|
||||
" # other params...\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2b4f3e15",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Invocation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e7b7170d-d7c5-4890-9714-a37238343805",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"execution_count": 6,
|
||||
"id": "62e0dbc3",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content=\"J'adore la programmation.\", response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 35, 'total_tokens': 44}, 'model_name': 'meta-llama/Llama-3-70b-chat-hf', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-79efa49b-dbaf-4ef8-9dce-958533823ef6-0', usage_metadata={'input_tokens': 35, 'output_tokens': 9, 'total_tokens': 44})"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Querying code and language models with Together AI\n",
|
||||
"messages = [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"You are a helpful assistant that translates English to French. Translate the user sentence.\",\n",
|
||||
" ),\n",
|
||||
" (\"human\", \"I love programming.\"),\n",
|
||||
"]\n",
|
||||
"ai_msg = llm.invoke(messages)\n",
|
||||
"ai_msg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "d86145b3-bfef-46e8-b227-4dda5c9c2705",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"J'adore la programmation.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(ai_msg.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "18e2bfc0-7e78-4528-a73f-499ac150dca8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chaining\n",
|
||||
"\n",
|
||||
"from langchain_together import Together\n",
|
||||
"We can [chain](../../how_to/sequence.ipynb) our model with a prompt template like so:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "e197d1d7-a070-4c96-9f8a-a0e86d046e0b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Ich liebe das Programmieren.', response_metadata={'token_usage': {'completion_tokens': 7, 'prompt_tokens': 30, 'total_tokens': 37}, 'model_name': 'meta-llama/Llama-3-70b-chat-hf', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-80bba5fa-1723-4242-8d5a-c09b76b8350b-0', usage_metadata={'input_tokens': 30, 'output_tokens': 7, 'total_tokens': 37})"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"\n",
|
||||
"llm = Together(\n",
|
||||
" model=\"codellama/CodeLlama-70b-Python-hf\",\n",
|
||||
" # together_api_key=\"...\"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"You are a helpful assistant that translates {input_language} to {output_language}.\",\n",
|
||||
" ),\n",
|
||||
" (\"human\", \"{input}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(llm.invoke(\"def bubble_sort(): \"))"
|
||||
"chain = prompt | llm\n",
|
||||
"chain.invoke(\n",
|
||||
" {\n",
|
||||
" \"input_language\": \"English\",\n",
|
||||
" \"output_language\": \"German\",\n",
|
||||
" \"input\": \"I love programming.\",\n",
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all ChatTogether features and configurations head to the API reference: https://api.python.langchain.com/en/latest/chat_models/langchain_together.chat_models.ChatTogether.html"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -111,7 +271,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
228
docs/docs/integrations/chat/yi.ipynb
Normal file
228
docs/docs/integrations/chat/yi.ipynb
Normal file
@@ -0,0 +1,228 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# ChatYI\n",
|
||||
"\n",
|
||||
"This will help you getting started with Yi [chat models](/docs/concepts/#chat-models). For detailed documentation of all ChatYi features and configurations head to the [API reference](https://api.python.langchain.com/en/latest/chat_models/lanchain_community.chat_models.yi.ChatYi.html).\n",
|
||||
"\n",
|
||||
"[01.AI](https://www.lingyiwanwu.com/en), founded by Dr. Kai-Fu Lee, is a global company at the forefront of AI 2.0. They offer cutting-edge large language models, including the Yi series, which range from 6B to hundreds of billions of parameters. 01.AI also provides multimodal models, an open API platform, and open-source options like Yi-34B/9B/6B and Yi-VL.\n",
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"### Integration details\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"| Class | Package | Local | Serializable | JS support | Package downloads | Package latest |\n",
|
||||
"| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| [ChatYi](https://api.python.langchain.com/en/latest/chat_models/lanchain_community.chat_models.yi.ChatYi.html) | [langchain_community](https://api.python.langchain.com/en/latest/community_api_reference.html) | ✅ | ❌ | ❌ |  |  |\n",
|
||||
"\n",
|
||||
"### Model features\n",
|
||||
"| [Tool calling](/docs/how_to/tool_calling) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"To access ChatYi models you'll need to create a/an 01.AI account, get an API key, and install the `langchain_community` integration package.\n",
|
||||
"\n",
|
||||
"### Credentials\n",
|
||||
"\n",
|
||||
"Head to [01.AI](https://platform.01.ai) to sign up to 01.AI and generate an API key. Once you've done this set the `YI_API_KEY` environment variable:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"YI_API_KEY\"] = getpass.getpass(\"Enter your Yi API key: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")\n",
|
||||
"# os.environ[\"LANGSMITH_TRACING\"] = \"true\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Installation\n",
|
||||
"\n",
|
||||
"The LangChain __ModuleName__ integration lives in the `langchain_community` package:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain_community"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Instantiation\n",
|
||||
"\n",
|
||||
"Now we can instantiate our model object and generate chat completions:\n",
|
||||
"\n",
|
||||
"- TODO: Update model instantiation with relevant params."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.chat_models.yi import ChatYi\n",
|
||||
"\n",
|
||||
"llm = ChatYi(\n",
|
||||
" model=\"yi-large\",\n",
|
||||
" temperature=0,\n",
|
||||
" timeout=60,\n",
|
||||
" yi_api_base=\"https://api.01.ai/v1/chat/completions\",\n",
|
||||
" # other params...\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Invocation\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content=\"Large Language Models (LLMs) have the potential to significantly impact healthcare by enhancing various aspects of patient care, research, and administrative processes. Here are some potential applications:\\n\\n1. **Clinical Documentation and Reporting**: LLMs can assist in generating patient reports and documentation by understanding and summarizing clinical notes, making the process more efficient and reducing the administrative burden on healthcare professionals.\\n\\n2. **Medical Coding and Billing**: These models can help in automating the coding process for medical billing by accurately translating clinical notes into standardized codes, reducing errors and improving billing efficiency.\\n\\n3. **Clinical Decision Support**: LLMs can analyze patient data and medical literature to provide evidence-based recommendations to healthcare providers, aiding in diagnosis and treatment planning.\\n\\n4. **Patient Education and Communication**: By simplifying medical jargon, LLMs can help in educating patients about their conditions, treatment options, and preventive care, improving patient engagement and health literacy.\\n\\n5. **Natural Language Processing (NLP) for EHRs**: LLMs can enhance NLP capabilities in Electronic Health Records (EHRs) systems, enabling better extraction of information from unstructured data, such as clinical notes, to support data-driven decision-making.\\n\\n6. **Drug Discovery and Development**: LLMs can analyze biomedical literature and clinical trial data to identify new drug candidates, predict drug interactions, and support the development of personalized medicine.\\n\\n7. **Telemedicine and Virtual Health Assistants**: Integrated into telemedicine platforms, LLMs can provide preliminary assessments and triage, offering patients basic health advice and determining the urgency of their needs, thus optimizing the utilization of healthcare resources.\\n\\n8. **Research and Literature Review**: LLMs can expedite the process of reviewing medical literature by quickly identifying relevant studies and summarizing findings, accelerating research and evidence-based practice.\\n\\n9. **Personalized Medicine**: By analyzing a patient's genetic information and medical history, LLMs can help in tailoring treatment plans and medication dosages, contributing to the advancement of personalized medicine.\\n\\n10. **Quality Improvement and Risk Assessment**: LLMs can analyze healthcare data to identify patterns that may indicate areas for quality improvement or potential risks, such as hospital-acquired infections or adverse drug events.\\n\\n11. **Mental Health Support**: LLMs can provide mental health support by offering coping strategies, mindfulness exercises, and preliminary assessments, serving as a complement to professional mental health services.\\n\\n12. **Continuing Medical Education (CME)**: LLMs can personalize CME by recommending educational content based on a healthcare provider's practice area, patient demographics, and emerging medical literature, ensuring that professionals stay updated with the latest advancements.\\n\\nWhile the applications of LLMs in healthcare are promising, it's crucial to address challenges such as data privacy, model bias, and the need for regulatory approval to ensure that these technologies are implemented safely and ethically.\", response_metadata={'token_usage': {'completion_tokens': 656, 'prompt_tokens': 40, 'total_tokens': 696}, 'model': 'yi-large'}, id='run-870850bd-e4bf-4265-8730-1736409c0acf-0')"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage, SystemMessage\n",
|
||||
"\n",
|
||||
"messages = [\n",
|
||||
" SystemMessage(content=\"You are an AI assistant specializing in technology trends.\"),\n",
|
||||
" HumanMessage(\n",
|
||||
" content=\"What are the potential applications of large language models in healthcare?\"\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"ai_msg = llm.invoke(messages)\n",
|
||||
"ai_msg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chaining\n",
|
||||
"\n",
|
||||
"We can [chain](/docs/how_to/sequence/) our model with a prompt template like so:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Ich liebe das Programmieren.', response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 33, 'total_tokens': 41}, 'model': 'yi-large'}, id='run-daa3bc58-8289-4d72-a24e-80622fa90d6d-0')"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"You are a helpful assistant that translates {input_language} to {output_language}.\",\n",
|
||||
" ),\n",
|
||||
" (\"human\", \"{input}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chain = prompt | llm\n",
|
||||
"chain.invoke(\n",
|
||||
" {\n",
|
||||
" \"input_language\": \"English\",\n",
|
||||
" \"output_language\": \"German\",\n",
|
||||
" \"input\": \"I love programming.\",\n",
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all ChatYi features and configurations head to the API reference: https://api.python.langchain.com/en/latest/chat_models/langchain_community.chat_models.yi.ChatYi.html"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"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.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
188
docs/docs/integrations/document_loaders/scrapingant.ipynb
Normal file
188
docs/docs/integrations/document_loaders/scrapingant.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
||||
"id": "20deed05",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Unstructured File\n",
|
||||
"# Unstructured\n",
|
||||
"\n",
|
||||
"This notebook covers how to use `Unstructured` package to load files of many types. `Unstructured` currently supports loading of text files, powerpoints, html, pdfs, images, and more.\n",
|
||||
"\n",
|
||||
@@ -14,79 +14,69 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "2886982e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.1.1\u001b[0m\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
|
||||
"Note: you may need to restart the kernel to use updated packages.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# # Install package\n",
|
||||
"%pip install --upgrade --quiet \"unstructured[all-docs]\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "54d62efd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Install other dependencies\n",
|
||||
"# # https://github.com/Unstructured-IO/unstructured/blob/main/docs/source/installing.rst\n",
|
||||
"# !brew install libmagic\n",
|
||||
"# !brew install poppler\n",
|
||||
"# !brew install tesseract\n",
|
||||
"# # If parsing xml / html documents:\n",
|
||||
"# !brew install libxml2\n",
|
||||
"# !brew install libxslt"
|
||||
"# Install package, compatible with API partitioning\n",
|
||||
"%pip install --upgrade --quiet \"langchain-unstructured\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "af6a64f5",
|
||||
"cell_type": "markdown",
|
||||
"id": "e75e2a6d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# import nltk\n",
|
||||
"# nltk.download('punkt')"
|
||||
"### Local Partitioning (Optional)\n",
|
||||
"\n",
|
||||
"By default, `langchain-unstructured` installs a smaller footprint that requires\n",
|
||||
"offloading of the partitioning logic to the Unstructured API.\n",
|
||||
"\n",
|
||||
"If you would like to run the partitioning logic locally, you will need to install\n",
|
||||
"a combination of system dependencies, as outlined in the \n",
|
||||
"[Unstructured documentation here](https://docs.unstructured.io/open-source/installation/full-installation).\n",
|
||||
"\n",
|
||||
"For example, on Macs you can install the required dependencies with:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"# base dependencies\n",
|
||||
"brew install libmagic poppler tesseract\n",
|
||||
"\n",
|
||||
"# If parsing xml / html documents:\n",
|
||||
"brew install libxml2 libxslt\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"You can install the required `pip` dependencies with:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"pip install \"langchain-unstructured[local]\"\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a9c1c775",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Quickstart\n",
|
||||
"\n",
|
||||
"To simply load a file as a document, you can use the LangChain `DocumentLoader.load` \n",
|
||||
"interface:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": null,
|
||||
"id": "79d3e549",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.\\n\\nLast year COVID-19 kept us apart. This year we are finally together again.\\n\\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans.\\n\\nWith a duty to one another to the American people to the Constit'"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders import UnstructuredFileLoader\n",
|
||||
"from langchain_unstructured import UnstructuredLoader\n",
|
||||
"\n",
|
||||
"loader = UnstructuredFileLoader(\"./example_data/state_of_the_union.txt\")\n",
|
||||
"loader = UnstructuredLoader(\"./example_data/state_of_the_union.txt\")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"docs[0].page_content[:400]"
|
||||
"docs = loader.load()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -99,113 +89,31 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 5,
|
||||
"id": "092d9a0b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'1/22/23, 6:30 PM - User 1: Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!\\n\\n1/22/23, 8:24 PM - User 2: Goodmorning! $50 is too low.\\n\\n1/23/23, 2:59 AM - User 1: How much do you want?\\n\\n1/23/23, 3:00 AM - User 2: Online is at least $100\\n\\n1/23/23, 3:01 AM - User 2: Here is $129\\n\\n1/23/23, 3:01 AM - User 2: <Media omitted>\\n\\n1/23/23, 3:01 AM - User 1: Im not int'"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"whatsapp_chat.txt : 1/22/23, 6:30 PM - User 1: Hi! Im interested in your bag. Im offering $50. Let me know if you are in\n",
|
||||
"state_of_the_union.txt : May God bless you all. May God protect our troops.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"files = [\"./example_data/whatsapp_chat.txt\", \"./example_data/layout-parser-paper.pdf\"]\n",
|
||||
"file_paths = [\n",
|
||||
" \"./example_data/whatsapp_chat.txt\",\n",
|
||||
" \"./example_data/state_of_the_union.txt\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"loader = UnstructuredFileLoader(files)\n",
|
||||
"loader = UnstructuredLoader(file_paths)\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"docs[0].page_content[:400]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7874d01d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Retain Elements\n",
|
||||
"\n",
|
||||
"Under the hood, Unstructured creates different \"elements\" for different chunks of text. By default we combine those together, but you can easily keep that separation by specifying `mode=\"elements\"`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "ff5b616d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.', metadata={'source': './example_data/state_of_the_union.txt', 'file_directory': './example_data', 'filename': 'state_of_the_union.txt', 'last_modified': '2024-07-01T11:18:22', 'languages': ['eng'], 'filetype': 'text/plain', 'category': 'NarrativeText'}),\n",
|
||||
" Document(page_content='Last year COVID-19 kept us apart. This year we are finally together again.', metadata={'source': './example_data/state_of_the_union.txt', 'file_directory': './example_data', 'filename': 'state_of_the_union.txt', 'last_modified': '2024-07-01T11:18:22', 'languages': ['eng'], 'filetype': 'text/plain', 'category': 'NarrativeText'}),\n",
|
||||
" Document(page_content='Tonight, we meet as Democrats Republicans and Independents. But most importantly as Americans.', metadata={'source': './example_data/state_of_the_union.txt', 'file_directory': './example_data', 'filename': 'state_of_the_union.txt', 'last_modified': '2024-07-01T11:18:22', 'languages': ['eng'], 'filetype': 'text/plain', 'category': 'NarrativeText'}),\n",
|
||||
" Document(page_content='With a duty to one another to the American people to the Constitution.', metadata={'source': './example_data/state_of_the_union.txt', 'file_directory': './example_data', 'filename': 'state_of_the_union.txt', 'last_modified': '2024-07-01T11:18:22', 'languages': ['eng'], 'filetype': 'text/plain', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='And with an unwavering resolve that freedom will always triumph over tyranny.', metadata={'source': './example_data/state_of_the_union.txt', 'file_directory': './example_data', 'filename': 'state_of_the_union.txt', 'last_modified': '2024-07-01T11:18:22', 'languages': ['eng'], 'filetype': 'text/plain', 'category': 'NarrativeText'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"loader = UnstructuredFileLoader(\n",
|
||||
" \"./example_data/state_of_the_union.txt\", mode=\"elements\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"docs[:5]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "672733fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Define a Partitioning Strategy\n",
|
||||
"\n",
|
||||
"Unstructured document loader allow users to pass in a `strategy` parameter that lets `unstructured` know how to partition the document. Currently supported strategies are `\"hi_res\"` (the default) and `\"fast\"`. Hi res partitioning strategies are more accurate, but take longer to process. Fast strategies partition the document more quickly, but trade-off accuracy. Not all document types have separate hi res and fast partitioning strategies. For those document types, the `strategy` kwarg is ignored. In some cases, the high res strategy will fallback to fast if there is a dependency missing (i.e. a model for document partitioning). You can see how to apply a strategy to an `UnstructuredFileLoader` below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "767238a4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='2 v 8 4 3 5 1 . 3 0 1 2 : v i X r a', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((16.34, 393.9), (16.34, 560.0), (36.34, 560.0), (36.34, 393.9)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': '89565df026a24279aaea20dc08cedbec', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='LayoutParser: A Unified Toolkit for Deep Learning Based Document Image Analysis', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((157.62199999999999, 114.23496279999995), (157.62199999999999, 146.5141628), (457.7358962799999, 146.5141628), (457.7358962799999, 114.23496279999995)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'filetype': 'application/pdf', 'category': 'Title'}),\n",
|
||||
" Document(page_content='Zejiang Shen1 ((cid:0)), Ruochen Zhang2, Melissa Dell3, Benjamin Charles Germain Lee4, Jacob Carlson3, and Weining Li5', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((134.809, 168.64029940800003), (134.809, 192.2517444), (480.5464199080001, 192.2517444), (480.5464199080001, 168.64029940800003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='1 Allen Institute for AI shannons@allenai.org 2 Brown University ruochen zhang@brown.edu 3 Harvard University {melissadell,jacob carlson}@fas.harvard.edu 4 University of Washington bcgl@cs.washington.edu 5 University of Waterloo w422li@uwaterloo.ca', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((207.23000000000002, 202.57205439999996), (207.23000000000002, 311.8195408), (408.12676, 311.8195408), (408.12676, 202.57205439999996)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='Abstract. Recent advances in document image analysis (DIA) have been primarily driven by the application of neural networks. Ideally, research outcomes could be easily deployed in production and extended for further investigation. However, various factors like loosely organized codebases and sophisticated model configurations complicate the easy reuse of im- portant innovations by a wide audience. Though there have been on-going efforts to improve reusability and simplify deep learning (DL) model development in disciplines like natural language processing and computer vision, none of them are optimized for challenges in the domain of DIA. This represents a major gap in the existing toolkit, as DIA is central to academic research across a wide range of disciplines in the social sciences and humanities. This paper introduces LayoutParser, an open-source library for streamlining the usage of DL in DIA research and applica- tions. The core LayoutParser library comes with a set of simple and intuitive interfaces for applying and customizing DL models for layout de- tection, character recognition, and many other document processing tasks. To promote extensibility, LayoutParser also incorporates a community platform for sharing both pre-trained models and full document digiti- zation pipelines. We demonstrate that LayoutParser is helpful for both lightweight and large-scale digitization pipelines in real-word use cases. The library is publicly available at https://layout-parser.github.io.', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((162.779, 338.45008160000003), (162.779, 566.8455408), (454.0372021523199, 566.8455408), (454.0372021523199, 338.45008160000003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'links': [{'text': ':// layout - parser . github . io', 'url': 'https://layout-parser.github.io', 'start_index': 1477}], 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'NarrativeText'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders import UnstructuredFileLoader\n",
|
||||
"\n",
|
||||
"loader = UnstructuredFileLoader(\n",
|
||||
" \"./example_data/layout-parser-paper.pdf\", strategy=\"fast\", mode=\"elements\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"docs[5:10]"
|
||||
"print(docs[0].metadata.get(\"filename\"), \": \", docs[0].page_content[:100])\n",
|
||||
"print(docs[-1].metadata.get(\"filename\"), \": \", docs[-1].page_content[:100])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -215,37 +123,52 @@
|
||||
"source": [
|
||||
"## PDF Example\n",
|
||||
"\n",
|
||||
"Processing PDF documents works exactly the same way. Unstructured detects the file type and extracts the same types of elements. Modes of operation are \n",
|
||||
"- `single` all the text from all elements are combined into one (default)\n",
|
||||
"- `elements` maintain individual elements\n",
|
||||
"- `paged` texts from each page are only combined"
|
||||
"Processing PDF documents works exactly the same way. Unstructured detects the file type and extracts the same types of elements."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "672733fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define a Partitioning Strategy\n",
|
||||
"\n",
|
||||
"Unstructured document loader allow users to pass in a `strategy` parameter that lets Unstructured\n",
|
||||
"know how to partition pdf and other OCR'd documents. Currently supported strategies are `\"auto\"`,\n",
|
||||
"`\"hi_res\"`, `\"ocr_only\"`, and `\"fast\"`. Learn more about the different strategies\n",
|
||||
"[here](https://docs.unstructured.io/open-source/core-functionality/partitioning#partition-pdf). \n",
|
||||
"\n",
|
||||
"Not all document types have separate hi res and fast partitioning strategies. For those document types, the `strategy` kwarg is\n",
|
||||
"ignored. In some cases, the high res strategy will fallback to fast if there is a dependency missing\n",
|
||||
"(i.e. a model for document partitioning). You can see how to apply a strategy to an\n",
|
||||
"`UnstructuredLoader` below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "686e5eb4",
|
||||
"execution_count": 6,
|
||||
"id": "60685353",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='2 v 8 4 3 5 1 . 3 0 1 2 : v i X r a', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((16.34, 393.9), (16.34, 560.0), (36.34, 560.0), (36.34, 393.9)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': '89565df026a24279aaea20dc08cedbec', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='LayoutParser: A Unified Toolkit for Deep Learning Based Document Image Analysis', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((157.62199999999999, 114.23496279999995), (157.62199999999999, 146.5141628), (457.7358962799999, 146.5141628), (457.7358962799999, 114.23496279999995)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'filetype': 'application/pdf', 'category': 'Title'}),\n",
|
||||
" Document(page_content='Zejiang Shen1 ((cid:0)), Ruochen Zhang2, Melissa Dell3, Benjamin Charles Germain Lee4, Jacob Carlson3, and Weining Li5', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((134.809, 168.64029940800003), (134.809, 192.2517444), (480.5464199080001, 192.2517444), (480.5464199080001, 168.64029940800003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='1 Allen Institute for AI shannons@allenai.org 2 Brown University ruochen zhang@brown.edu 3 Harvard University {melissadell,jacob carlson}@fas.harvard.edu 4 University of Washington bcgl@cs.washington.edu 5 University of Waterloo w422li@uwaterloo.ca', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((207.23000000000002, 202.57205439999996), (207.23000000000002, 311.8195408), (408.12676, 311.8195408), (408.12676, 202.57205439999996)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='Abstract. Recent advances in document image analysis (DIA) have been primarily driven by the application of neural networks. Ideally, research outcomes could be easily deployed in production and extended for further investigation. However, various factors like loosely organized codebases and sophisticated model configurations complicate the easy reuse of im- portant innovations by a wide audience. Though there have been on-going efforts to improve reusability and simplify deep learning (DL) model development in disciplines like natural language processing and computer vision, none of them are optimized for challenges in the domain of DIA. This represents a major gap in the existing toolkit, as DIA is central to academic research across a wide range of disciplines in the social sciences and humanities. This paper introduces LayoutParser, an open-source library for streamlining the usage of DL in DIA research and applica- tions. The core LayoutParser library comes with a set of simple and intuitive interfaces for applying and customizing DL models for layout de- tection, character recognition, and many other document processing tasks. To promote extensibility, LayoutParser also incorporates a community platform for sharing both pre-trained models and full document digiti- zation pipelines. We demonstrate that LayoutParser is helpful for both lightweight and large-scale digitization pipelines in real-word use cases. The library is publicly available at https://layout-parser.github.io.', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((162.779, 338.45008160000003), (162.779, 566.8455408), (454.0372021523199, 566.8455408), (454.0372021523199, 338.45008160000003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'links': [{'text': ':// layout - parser . github . io', 'url': 'https://layout-parser.github.io', 'start_index': 1477}], 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'NarrativeText'})]"
|
||||
"[Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((16.34, 393.9), (16.34, 560.0), (36.34, 560.0), (36.34, 393.9)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'parent_id': '89565df026a24279aaea20dc08cedbec', 'filetype': 'application/pdf', 'category': 'UncategorizedText', 'element_id': 'e9fa370aef7ee5c05744eb7bb7d9981b'}, page_content='2 v 8 4 3 5 1 . 3 0 1 2 : v i X r a'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((157.62199999999999, 114.23496279999995), (157.62199999999999, 146.5141628), (457.7358962799999, 146.5141628), (457.7358962799999, 114.23496279999995)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'filetype': 'application/pdf', 'category': 'Title', 'element_id': 'bde0b230a1aa488e3ce837d33015181b'}, page_content='LayoutParser: A Unified Toolkit for Deep Learning Based Document Image Analysis'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((134.809, 168.64029940800003), (134.809, 192.2517444), (480.5464199080001, 192.2517444), (480.5464199080001, 168.64029940800003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText', 'element_id': '54700f902899f0c8c90488fa8d825bce'}, page_content='Zejiang Shen1 ((cid:0)), Ruochen Zhang2, Melissa Dell3, Benjamin Charles Germain Lee4, Jacob Carlson3, and Weining Li5'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((207.23000000000002, 202.57205439999996), (207.23000000000002, 311.8195408), (408.12676, 311.8195408), (408.12676, 202.57205439999996)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText', 'element_id': 'b650f5867bad9bb4e30384282c79bcfe'}, page_content='1 Allen Institute for AI shannons@allenai.org 2 Brown University ruochen zhang@brown.edu 3 Harvard University {melissadell,jacob carlson}@fas.harvard.edu 4 University of Washington bcgl@cs.washington.edu 5 University of Waterloo w422li@uwaterloo.ca'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((162.779, 338.45008160000003), (162.779, 566.8455408), (454.0372021523199, 566.8455408), (454.0372021523199, 338.45008160000003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'links': [{'text': ':// layout - parser . github . io', 'url': 'https://layout-parser.github.io', 'start_index': 1477}], 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'NarrativeText', 'element_id': 'cfc957c94fe63c8fd7c7f4bcb56e75a7'}, page_content='Abstract. Recent advances in document image analysis (DIA) have been primarily driven by the application of neural networks. Ideally, research outcomes could be easily deployed in production and extended for further investigation. However, various factors like loosely organized codebases and sophisticated model configurations complicate the easy reuse of im- portant innovations by a wide audience. Though there have been on-going efforts to improve reusability and simplify deep learning (DL) model development in disciplines like natural language processing and computer vision, none of them are optimized for challenges in the domain of DIA. This represents a major gap in the existing toolkit, as DIA is central to academic research across a wide range of disciplines in the social sciences and humanities. This paper introduces LayoutParser, an open-source library for streamlining the usage of DL in DIA research and applica- tions. The core LayoutParser library comes with a set of simple and intuitive interfaces for applying and customizing DL models for layout de- tection, character recognition, and many other document processing tasks. To promote extensibility, LayoutParser also incorporates a community platform for sharing both pre-trained models and full document digiti- zation pipelines. We demonstrate that LayoutParser is helpful for both lightweight and large-scale digitization pipelines in real-word use cases. The library is publicly available at https://layout-parser.github.io.')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"loader = UnstructuredFileLoader(\n",
|
||||
" \"./example_data/layout-parser-paper.pdf\", mode=\"elements\"\n",
|
||||
")\n",
|
||||
"from langchain_unstructured import UnstructuredLoader\n",
|
||||
"\n",
|
||||
"loader = UnstructuredLoader(\"./example_data/layout-parser-paper.pdf\", strategy=\"fast\")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
@@ -257,37 +180,39 @@
|
||||
"id": "1cf27fc8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If you need to post process the `unstructured` elements after extraction, you can pass in a list of `str` -> `str` functions to the `post_processors` kwarg when you instantiate the `UnstructuredFileLoader`. This applies to other Unstructured loaders as well. Below is an example."
|
||||
"## Post Processing\n",
|
||||
"\n",
|
||||
"If you need to post process the `unstructured` elements after extraction, you can pass in a list of\n",
|
||||
"`str` -> `str` functions to the `post_processors` kwarg when you instantiate the `UnstructuredLoader`. This applies to other Unstructured loaders as well. Below is an example."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 7,
|
||||
"id": "112e5538",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='2 v 8 4 3 5 1 . 3 0 1 2 : v i X r a', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((16.34, 393.9), (16.34, 560.0), (36.34, 560.0), (36.34, 393.9)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': '89565df026a24279aaea20dc08cedbec', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='LayoutParser: A Unified Toolkit for Deep Learning Based Document Image Analysis', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((157.62199999999999, 114.23496279999995), (157.62199999999999, 146.5141628), (457.7358962799999, 146.5141628), (457.7358962799999, 114.23496279999995)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'filetype': 'application/pdf', 'category': 'Title'}),\n",
|
||||
" Document(page_content='Zejiang Shen1 ((cid:0)), Ruochen Zhang2, Melissa Dell3, Benjamin Charles Germain Lee4, Jacob Carlson3, and Weining Li5', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((134.809, 168.64029940800003), (134.809, 192.2517444), (480.5464199080001, 192.2517444), (480.5464199080001, 168.64029940800003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='1 Allen Institute for AI shannons@allenai.org 2 Brown University ruochen zhang@brown.edu 3 Harvard University {melissadell,jacob carlson}@fas.harvard.edu 4 University of Washington bcgl@cs.washington.edu 5 University of Waterloo w422li@uwaterloo.ca', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((207.23000000000002, 202.57205439999996), (207.23000000000002, 311.8195408), (408.12676, 311.8195408), (408.12676, 202.57205439999996)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText'}),\n",
|
||||
" Document(page_content='Abstract. Recent advances in document image analysis (DIA) have been primarily driven by the application of neural networks. Ideally, research outcomes could be easily deployed in production and extended for further investigation. However, various factors like loosely organized codebases and sophisticated model configurations complicate the easy reuse of im- portant innovations by a wide audience. Though there have been on-going efforts to improve reusability and simplify deep learning (DL) model development in disciplines like natural language processing and computer vision, none of them are optimized for challenges in the domain of DIA. This represents a major gap in the existing toolkit, as DIA is central to academic research across a wide range of disciplines in the social sciences and humanities. This paper introduces LayoutParser, an open-source library for streamlining the usage of DL in DIA research and applica- tions. The core LayoutParser library comes with a set of simple and intuitive interfaces for applying and customizing DL models for layout de- tection, character recognition, and many other document processing tasks. To promote extensibility, LayoutParser also incorporates a community platform for sharing both pre-trained models and full document digiti- zation pipelines. We demonstrate that LayoutParser is helpful for both lightweight and large-scale digitization pipelines in real-word use cases. The library is publicly available at https://layout-parser.github.io.', metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((162.779, 338.45008160000003), (162.779, 566.8455408), (454.0372021523199, 566.8455408), (454.0372021523199, 338.45008160000003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2023-12-19T13:42:18', 'links': [{'text': ':// layout - parser . github . io', 'url': 'https://layout-parser.github.io', 'start_index': 1477}], 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'NarrativeText'})]"
|
||||
"[Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((16.34, 393.9), (16.34, 560.0), (36.34, 560.0), (36.34, 393.9)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'parent_id': '89565df026a24279aaea20dc08cedbec', 'filetype': 'application/pdf', 'category': 'UncategorizedText', 'element_id': 'e9fa370aef7ee5c05744eb7bb7d9981b'}, page_content='2 v 8 4 3 5 1 . 3 0 1 2 : v i X r a'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((157.62199999999999, 114.23496279999995), (157.62199999999999, 146.5141628), (457.7358962799999, 146.5141628), (457.7358962799999, 114.23496279999995)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'filetype': 'application/pdf', 'category': 'Title', 'element_id': 'bde0b230a1aa488e3ce837d33015181b'}, page_content='LayoutParser: A Unified Toolkit for Deep Learning Based Document Image Analysis'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((134.809, 168.64029940800003), (134.809, 192.2517444), (480.5464199080001, 192.2517444), (480.5464199080001, 168.64029940800003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText', 'element_id': '54700f902899f0c8c90488fa8d825bce'}, page_content='Zejiang Shen1 ((cid:0)), Ruochen Zhang2, Melissa Dell3, Benjamin Charles Germain Lee4, Jacob Carlson3, and Weining Li5'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((207.23000000000002, 202.57205439999996), (207.23000000000002, 311.8195408), (408.12676, 311.8195408), (408.12676, 202.57205439999996)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'UncategorizedText', 'element_id': 'b650f5867bad9bb4e30384282c79bcfe'}, page_content='1 Allen Institute for AI shannons@allenai.org 2 Brown University ruochen zhang@brown.edu 3 Harvard University {melissadell,jacob carlson}@fas.harvard.edu 4 University of Washington bcgl@cs.washington.edu 5 University of Waterloo w422li@uwaterloo.ca'),\n",
|
||||
" Document(metadata={'source': './example_data/layout-parser-paper.pdf', 'coordinates': {'points': ((162.779, 338.45008160000003), (162.779, 566.8455408), (454.0372021523199, 566.8455408), (454.0372021523199, 338.45008160000003)), 'system': 'PixelSpace', 'layout_width': 612, 'layout_height': 792}, 'file_directory': './example_data', 'filename': 'layout-parser-paper.pdf', 'languages': ['eng'], 'last_modified': '2024-02-27T15:49:27', 'links': [{'text': ':// layout - parser . github . io', 'url': 'https://layout-parser.github.io', 'start_index': 1477}], 'page_number': 1, 'parent_id': 'bde0b230a1aa488e3ce837d33015181b', 'filetype': 'application/pdf', 'category': 'NarrativeText', 'element_id': 'cfc957c94fe63c8fd7c7f4bcb56e75a7'}, page_content='Abstract. Recent advances in document image analysis (DIA) have been primarily driven by the application of neural networks. Ideally, research outcomes could be easily deployed in production and extended for further investigation. However, various factors like loosely organized codebases and sophisticated model configurations complicate the easy reuse of im- portant innovations by a wide audience. Though there have been on-going efforts to improve reusability and simplify deep learning (DL) model development in disciplines like natural language processing and computer vision, none of them are optimized for challenges in the domain of DIA. This represents a major gap in the existing toolkit, as DIA is central to academic research across a wide range of disciplines in the social sciences and humanities. This paper introduces LayoutParser, an open-source library for streamlining the usage of DL in DIA research and applica- tions. The core LayoutParser library comes with a set of simple and intuitive interfaces for applying and customizing DL models for layout de- tection, character recognition, and many other document processing tasks. To promote extensibility, LayoutParser also incorporates a community platform for sharing both pre-trained models and full document digiti- zation pipelines. We demonstrate that LayoutParser is helpful for both lightweight and large-scale digitization pipelines in real-word use cases. The library is publicly available at https://layout-parser.github.io.')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders import UnstructuredFileLoader\n",
|
||||
"from langchain_unstructured import UnstructuredLoader\n",
|
||||
"from unstructured.cleaners.core import clean_extra_whitespace\n",
|
||||
"\n",
|
||||
"loader = UnstructuredFileLoader(\n",
|
||||
"loader = UnstructuredLoader(\n",
|
||||
" \"./example_data/layout-parser-paper.pdf\",\n",
|
||||
" mode=\"elements\",\n",
|
||||
" post_processors=[clean_extra_whitespace],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
@@ -303,34 +228,70 @@
|
||||
"source": [
|
||||
"## Unstructured API\n",
|
||||
"\n",
|
||||
"If you want to get up and running with less set up, you can simply run `pip install unstructured` and use `UnstructuredAPIFileLoader` or `UnstructuredAPIFileIOLoader`. That will process your document using the hosted Unstructured API. You can generate a free Unstructured API key [here](https://www.unstructured.io/api-key/). The [Unstructured documentation](https://unstructured-io.github.io/unstructured/) page will have instructions on how to generate an API key once they’re available. Check out the instructions [here](https://github.com/Unstructured-IO/unstructured-api#dizzy-instructions-for-using-the-docker-image) if you’d like to self-host the Unstructured API or run it locally."
|
||||
"If you want to get up and running with smaller packages and get the most up-to-date partitioning you can `pip install\n",
|
||||
"unstructured-client` and `pip install langchain-unstructured`. For\n",
|
||||
"more information about the `UnstructuredLoader`, refer to the\n",
|
||||
"[Unstructured provider page](https://python.langchain.com/v0.1/docs/integrations/document_loaders/unstructured_file/).\n",
|
||||
"\n",
|
||||
"The loader will process your document using the hosted Unstructured serverless API when you pass in\n",
|
||||
"your `api_key` and set `partition_via_api=True`. You can generate a free\n",
|
||||
"Unstructured API key [here](https://unstructured.io/api-key/).\n",
|
||||
"\n",
|
||||
"Check out the instructions [here](https://github.com/Unstructured-IO/unstructured-api#dizzy-instructions-for-using-the-docker-image)\n",
|
||||
"if you’d like to self-host the Unstructured API or run it locally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": null,
|
||||
"id": "6e5fde16",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Install package\n",
|
||||
"%pip install \"langchain-unstructured\"\n",
|
||||
"%pip install \"unstructured-client\"\n",
|
||||
"\n",
|
||||
"# Set API key\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"UNSTRUCTURED_API_KEY\"] = \"FAKE_API_KEY\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "386eb63c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"INFO: Preparing to split document for partition.\n",
|
||||
"INFO: Given file doesn't have '.pdf' extension, so splitting is not enabled.\n",
|
||||
"INFO: Partitioning without split.\n",
|
||||
"INFO: Successfully partitioned the document.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Document(page_content='Lorem ipsum dolor sit amet.', metadata={'source': 'example_data/fake.docx'})"
|
||||
"Document(metadata={'source': 'example_data/fake.docx', 'category_depth': 0, 'filename': 'fake.docx', 'languages': ['por', 'cat'], 'filetype': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'category': 'Title', 'element_id': '56d531394823d81787d77a04462ed096'}, page_content='Lorem ipsum dolor sit amet.')"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders import UnstructuredAPIFileLoader\n",
|
||||
"from langchain_unstructured import UnstructuredLoader\n",
|
||||
"\n",
|
||||
"filenames = [\"example_data/fake.docx\", \"example_data/fake-email.eml\"]\n",
|
||||
"\n",
|
||||
"loader = UnstructuredAPIFileLoader(\n",
|
||||
" file_path=filenames[0],\n",
|
||||
" api_key=\"FAKE_API_KEY\",\n",
|
||||
"loader = UnstructuredLoader(\n",
|
||||
" file_path=\"example_data/fake.docx\",\n",
|
||||
" api_key=os.getenv(\"UNSTRUCTURED_API_KEY\"),\n",
|
||||
" partition_via_api=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
@@ -342,43 +303,197 @@
|
||||
"id": "94158999",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can also batch multiple files through the Unstructured API in a single API using `UnstructuredAPIFileLoader`."
|
||||
"You can also batch multiple files through the Unstructured API in a single API using `UnstructuredLoader`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 10,
|
||||
"id": "a3d7c846",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Document(page_content='Lorem ipsum dolor sit amet.\\n\\nThis is a test email to use for unit tests.\\n\\nImportant points:\\n\\nRoses are red\\n\\nViolets are blue', metadata={'source': ['example_data/fake.docx', 'example_data/fake-email.eml']})"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"INFO: Preparing to split document for partition.\n",
|
||||
"INFO: Given file doesn't have '.pdf' extension, so splitting is not enabled.\n",
|
||||
"INFO: Partitioning without split.\n",
|
||||
"INFO: Successfully partitioned the document.\n",
|
||||
"INFO: Preparing to split document for partition.\n",
|
||||
"INFO: Given file doesn't have '.pdf' extension, so splitting is not enabled.\n",
|
||||
"INFO: Partitioning without split.\n",
|
||||
"INFO: Successfully partitioned the document.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"fake.docx : Lorem ipsum dolor sit amet.\n",
|
||||
"fake-email.eml : Violets are blue\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"loader = UnstructuredAPIFileLoader(\n",
|
||||
" file_path=filenames,\n",
|
||||
" api_key=\"FAKE_API_KEY\",\n",
|
||||
"loader = UnstructuredLoader(\n",
|
||||
" file_path=[\"example_data/fake.docx\", \"example_data/fake-email.eml\"],\n",
|
||||
" api_key=os.getenv(\"UNSTRUCTURED_API_KEY\"),\n",
|
||||
" partition_via_api=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"docs[0]"
|
||||
"\n",
|
||||
"print(docs[0].metadata[\"filename\"], \": \", docs[0].page_content[:100])\n",
|
||||
"print(docs[-1].metadata[\"filename\"], \": \", docs[-1].page_content[:100])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a324a0db",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Unstructured SDK Client\n",
|
||||
"\n",
|
||||
"Partitioning with the Unstructured API relies on the [Unstructured SDK\n",
|
||||
"Client](https://docs.unstructured.io/api-reference/api-services/sdk).\n",
|
||||
"\n",
|
||||
"Below is an example showing how you can customize some features of the client and use your own\n",
|
||||
"`requests.Session()`, pass in an alternative `server_url`, or customize the `RetryConfig` object for more control over how failed requests are handled."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0e510495",
|
||||
"execution_count": 11,
|
||||
"id": "58e55264",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"INFO: Preparing to split document for partition.\n",
|
||||
"INFO: Concurrency level set to 5\n",
|
||||
"INFO: Splitting pages 1 to 16 (16 total)\n",
|
||||
"INFO: Determined optimal split size of 4 pages.\n",
|
||||
"INFO: Partitioning 4 files with 4 page(s) each.\n",
|
||||
"INFO: Partitioning set #1 (pages 1-4).\n",
|
||||
"INFO: Partitioning set #2 (pages 5-8).\n",
|
||||
"INFO: Partitioning set #3 (pages 9-12).\n",
|
||||
"INFO: Partitioning set #4 (pages 13-16).\n",
|
||||
"INFO: HTTP Request: POST https://api.unstructuredapp.io/general/v0/general \"HTTP/1.1 200 OK\"\n",
|
||||
"INFO: HTTP Request: POST https://api.unstructuredapp.io/general/v0/general \"HTTP/1.1 200 OK\"\n",
|
||||
"INFO: HTTP Request: POST https://api.unstructuredapp.io/general/v0/general \"HTTP/1.1 200 OK\"\n",
|
||||
"INFO: Successfully partitioned set #1, elements added to the final result.\n",
|
||||
"INFO: Successfully partitioned set #2, elements added to the final result.\n",
|
||||
"INFO: Successfully partitioned set #3, elements added to the final result.\n",
|
||||
"INFO: Successfully partitioned set #4, elements added to the final result.\n",
|
||||
"INFO: Successfully partitioned the document.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"layout-parser-paper.pdf : LayoutParser: A Unified Toolkit for Deep Learning Based Document Image Analysis\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import requests\n",
|
||||
"from langchain_unstructured import UnstructuredLoader\n",
|
||||
"from unstructured_client import UnstructuredClient\n",
|
||||
"from unstructured_client.utils import BackoffStrategy, RetryConfig\n",
|
||||
"\n",
|
||||
"client = UnstructuredClient(\n",
|
||||
" api_key_auth=os.getenv(\n",
|
||||
" \"UNSTRUCTURED_API_KEY\"\n",
|
||||
" ), # Note: the client API param is \"api_key_auth\" instead of \"api_key\"\n",
|
||||
" client=requests.Session(),\n",
|
||||
" server_url=\"https://api.unstructuredapp.io/general/v0/general\",\n",
|
||||
" retry_config=RetryConfig(\n",
|
||||
" strategy=\"backoff\",\n",
|
||||
" retry_connection_errors=True,\n",
|
||||
" backoff=BackoffStrategy(\n",
|
||||
" initial_interval=500,\n",
|
||||
" max_interval=60000,\n",
|
||||
" exponent=1.5,\n",
|
||||
" max_elapsed_time=900000,\n",
|
||||
" ),\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"loader = UnstructuredLoader(\n",
|
||||
" \"./example_data/layout-parser-paper.pdf\",\n",
|
||||
" partition_via_api=True,\n",
|
||||
" client=client,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"print(docs[0].metadata[\"filename\"], \": \", docs[0].page_content[:100])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c66fbeb3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chunking\n",
|
||||
"\n",
|
||||
"The `UnstructuredLoader` does not support `mode` as parameter for grouping text like the older\n",
|
||||
"loader `UnstructuredFileLoader` and others did. It instead supports \"chunking\". Chunking in\n",
|
||||
"unstructured differs from other chunking mechanisms you may be familiar with that form chunks based\n",
|
||||
"on plain-text features--character sequences like \"\\n\\n\" or \"\\n\" that might indicate a paragraph\n",
|
||||
"boundary or list-item boundary. Instead, all documents are split using specific knowledge about each\n",
|
||||
"document format to partition the document into semantic units (document elements) and we only need to\n",
|
||||
"resort to text-splitting when a single element exceeds the desired maximum chunk size. In general,\n",
|
||||
"chunking combines consecutive elements to form chunks as large as possible without exceeding the\n",
|
||||
"maximum chunk size. Chunking produces a sequence of CompositeElement, Table, or TableChunk elements.\n",
|
||||
"Each “chunk” is an instance of one of these three types.\n",
|
||||
"\n",
|
||||
"See this [page](https://docs.unstructured.io/open-source/core-functionality/chunking) for more\n",
|
||||
"details about chunking options, but to reproduce the same behavior as `mode=\"single\"`, you can set\n",
|
||||
"`chunking_strategy=\"basic\"`, `max_characters=<some-really-big-number>`, and `include_orig_elements=False`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "e9f1c20d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"WARNING: Partitioning locally even though api_key is defined since partition_via_api=False.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Number of LangChain documents: 1\n",
|
||||
"Length of text in the document: 42772\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_unstructured import UnstructuredLoader\n",
|
||||
"\n",
|
||||
"loader = UnstructuredLoader(\n",
|
||||
" \"./example_data/layout-parser-paper.pdf\",\n",
|
||||
" chunking_strategy=\"basic\",\n",
|
||||
" max_characters=1000000,\n",
|
||||
" include_orig_elements=False,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"print(\"Number of LangChain documents:\", len(docs))\n",
|
||||
"print(\"Length of text in the document:\", len(docs[0].page_content))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -397,7 +512,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.5"
|
||||
"version": "3.10.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -7,12 +7,29 @@
|
||||
"source": [
|
||||
"# Model caches\n",
|
||||
"\n",
|
||||
"This notebook covers how to cache results of individual LLM calls using different caches."
|
||||
"This notebook covers how to cache results of individual LLM calls using different caches.\n",
|
||||
"\n",
|
||||
"First, let's install some dependencies"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "88486f6f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-openai langchain-community\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "10ad9224",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
@@ -25,8 +42,9 @@
|
||||
"from langchain.globals import set_llm_cache\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"\n",
|
||||
"# To make the caching really obvious, lets use a slower model.\n",
|
||||
"llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", n=2, best_of=2)"
|
||||
"# To make the caching really obvious, lets use a slower and older model.\n",
|
||||
"# Caching supports newer chat models as well.\n",
|
||||
"llm = OpenAI(model=\"gpt-3.5-turbo-instruct\", n=2, best_of=2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -41,7 +59,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 3,
|
||||
"id": "426ff912",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -53,7 +71,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 4,
|
||||
"id": "64005d1f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -61,45 +79,14 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 52.2 ms, sys: 15.2 ms, total: 67.4 ms\n",
|
||||
"Wall time: 1.19 s\n"
|
||||
"CPU times: user 7.57 ms, sys: 8.22 ms, total: 15.8 ms\n",
|
||||
"Wall time: 649 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was...two tired!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "c8a1cb2b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 191 µs, sys: 11 µs, total: 202 µs\n",
|
||||
"Wall time: 205 µs\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was...two tired!\""
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was two-tired!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
@@ -107,10 +94,41 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "c8a1cb2b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 551 µs, sys: 221 µs, total: 772 µs\n",
|
||||
"Wall time: 1.23 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was two-tired!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -125,7 +143,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 6,
|
||||
"id": "aefd9d2f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -135,7 +153,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 7,
|
||||
"id": "5f036236",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -148,7 +166,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 8,
|
||||
"id": "fa18e3af",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -156,47 +174,14 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 33.2 ms, sys: 18.1 ms, total: 51.2 ms\n",
|
||||
"Wall time: 667 ms\n"
|
||||
"CPU times: user 12.6 ms, sys: 3.51 ms, total: 16.1 ms\n",
|
||||
"Wall time: 486 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\n\\nWhy did the chicken cross the road?\\n\\nTo get to the other side.'"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "5bf2f6fd",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 4.86 ms, sys: 1.97 ms, total: 6.83 ms\n",
|
||||
"Wall time: 5.79 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\n\\nWhy did the chicken cross the road?\\n\\nTo get to the other side.'"
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was two-tired!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
@@ -204,10 +189,43 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "5bf2f6fd",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 52.6 ms, sys: 57.7 ms, total: 110 ms\n",
|
||||
"Wall time: 113 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was two-tired!\""
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -227,6 +245,16 @@
|
||||
"Use [Upstash Redis](https://upstash.com) to cache prompts and responses with a serverless HTTP API."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9bd81e8e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU upstash_redis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
@@ -272,7 +300,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -303,7 +331,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -326,6 +354,16 @@
|
||||
"Use [Redis](/docs/integrations/providers/redis) to cache prompts and responses."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d104226b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU redis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
@@ -369,7 +407,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -400,7 +438,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -414,7 +452,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": null,
|
||||
"id": "77b3e4e0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU redis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "64df3099",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -455,7 +503,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -487,7 +535,7 @@
|
||||
"%%time\n",
|
||||
"# The second time, while not a direct hit, the question is semantically similar to the original question,\n",
|
||||
"# so it uses the cached result!\n",
|
||||
"llm(\"Tell me one joke\")"
|
||||
"llm.invoke(\"Tell me one joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -505,6 +553,16 @@
|
||||
"Let's first start with an example of exact match"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7fe96cea",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU gptcache"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
@@ -563,7 +621,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -594,7 +652,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -659,7 +717,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -690,7 +748,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# This is an exact match, so it finds it in the cache\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -721,7 +779,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# This is not an exact match, but semantically within distance so it hits!\n",
|
||||
"llm(\"Tell me joke\")"
|
||||
"llm.invoke(\"Tell me joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -744,7 +802,11 @@
|
||||
"### `MongoDBCache`\n",
|
||||
"An abstraction to store a simple cache in MongoDB. This does not use Semantic Caching, nor does it require an index to be made on the collection before generation.\n",
|
||||
"\n",
|
||||
"To import this cache:\n",
|
||||
"To import this cache, first install the required dependency:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"%pip install -qU langchain-mongodb\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"from langchain_mongodb.cache import MongoDBCache\n",
|
||||
@@ -822,7 +884,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet momento"
|
||||
"%pip install -qU momento"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -877,7 +939,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -909,7 +971,7 @@
|
||||
"%%time\n",
|
||||
"# The second time it is, so it goes faster\n",
|
||||
"# When run in the same region as the cache, latencies are single digit ms\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1015,7 +1077,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet \"cassio>=0.1.4\""
|
||||
"%pip install -qU \"cassio>=0.1.4\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1350,6 +1412,8 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install -qU langchain_astradb\n",
|
||||
"\n",
|
||||
"import getpass\n",
|
||||
"\n",
|
||||
"ASTRA_DB_API_ENDPOINT = input(\"ASTRA_DB_API_ENDPOINT = \")\n",
|
||||
@@ -1633,7 +1697,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1669,7 +1733,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1690,7 +1754,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -U langchain-elasticsearch"
|
||||
"%pip install -qU langchain-elasticsearch"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1823,7 +1887,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", n=2, best_of=2, cache=False)"
|
||||
"llm = OpenAI(model=\"gpt-3.5-turbo-instruct\", n=2, best_of=2, cache=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1853,7 +1917,7 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1883,7 +1947,7 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1901,18 +1965,18 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 10,
|
||||
"id": "9afa3f7a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\")\n",
|
||||
"no_cache_llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", cache=False)"
|
||||
"llm = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n",
|
||||
"no_cache_llm = OpenAI(model=\"gpt-3.5-turbo-instruct\", cache=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 11,
|
||||
"id": "98a78e8e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -1924,19 +1988,19 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 14,
|
||||
"id": "2bfb099b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with open(\"../../how_to/state_of_the_union.txt\") as f:\n",
|
||||
"with open(\"../how_to/state_of_the_union.txt\") as f:\n",
|
||||
" state_of_the_union = f.read()\n",
|
||||
"texts = text_splitter.split_text(state_of_the_union)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"execution_count": 15,
|
||||
"id": "f78b7f51",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -1949,7 +2013,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"execution_count": 16,
|
||||
"id": "a2a30822",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -1959,7 +2023,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"execution_count": 17,
|
||||
"id": "a545b743",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -1967,24 +2031,27 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 452 ms, sys: 60.3 ms, total: 512 ms\n",
|
||||
"Wall time: 5.09 s\n"
|
||||
"CPU times: user 176 ms, sys: 23.2 ms, total: 199 ms\n",
|
||||
"Wall time: 4.42 s\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\n\\nPresident Biden is discussing the American Rescue Plan and the Bipartisan Infrastructure Law, which will create jobs and help Americans. He also talks about his vision for America, which includes investing in education and infrastructure. In response to Russian aggression in Ukraine, the United States is joining with European allies to impose sanctions and isolate Russia. American forces are being mobilized to protect NATO countries in the event that Putin decides to keep moving west. The Ukrainians are bravely fighting back, but the next few weeks will be hard for them. Putin will pay a high price for his actions in the long run. Americans should not be alarmed, as the United States is taking action to protect its interests and allies.'"
|
||||
"{'input_documents': [Document(page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. \\n\\nLast year COVID-19 kept us apart. This year we are finally together again. \\n\\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \\n\\nWith a duty to one another to the American people to the Constitution. \\n\\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \\n\\nSix days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \\n\\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \\n\\nHe met the Ukrainian people. \\n\\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world. \\n\\nGroups of citizens blocking tanks with their bodies. Everyone from students to retirees teachers turned soldiers defending their homeland. \\n\\nIn this struggle as President Zelenskyy said in his speech to the European Parliament “Light will win over darkness.” The Ukrainian Ambassador to the United States is here tonight. \\n\\nLet each of us here tonight in this Chamber send an unmistakable signal to Ukraine and to the world. \\n\\nPlease rise if you are able and show that, Yes, we the United States of America stand with the Ukrainian people. \\n\\nThroughout our history we’ve learned this lesson when dictators do not pay a price for their aggression they cause more chaos. \\n\\nThey keep moving. \\n\\nAnd the costs and the threats to America and the world keep rising. \\n\\nThat’s why the NATO Alliance was created to secure peace and stability in Europe after World War 2. \\n\\nThe United States is a member along with 29 other nations. \\n\\nIt matters. American diplomacy matters. American resolve matters. \\n\\nPutin’s latest attack on Ukraine was premeditated and unprovoked. \\n\\nHe rejected repeated efforts at diplomacy. \\n\\nHe thought the West and NATO wouldn’t respond. And he thought he could divide us at home. Putin was wrong. We were ready. Here is what we did. \\n\\nWe prepared extensively and carefully. \\n\\nWe spent months building a coalition of other freedom-loving nations from Europe and the Americas to Asia and Africa to confront Putin. \\n\\nI spent countless hours unifying our European allies. We shared with the world in advance what we knew Putin was planning and precisely how he would try to falsely justify his aggression. \\n\\nWe countered Russia’s lies with truth. \\n\\nAnd now that he has acted the free world is holding him accountable. \\n\\nAlong with twenty-seven members of the European Union including France, Germany, Italy, as well as countries like the United Kingdom, Canada, Japan, Korea, Australia, New Zealand, and many others, even Switzerland. \\n\\nWe are inflicting pain on Russia and supporting the people of Ukraine. Putin is now isolated from the world more than ever. \\n\\nTogether with our allies –we are right now enforcing powerful economic sanctions. \\n\\nWe are cutting off Russia’s largest banks from the international financial system. \\n\\nPreventing Russia’s central bank from defending the Russian Ruble making Putin’s $630 Billion “war fund” worthless. \\n\\nWe are choking off Russia’s access to technology that will sap its economic strength and weaken its military for years to come. \\n\\nTonight I say to the Russian oligarchs and corrupt leaders who have bilked billions of dollars off this violent regime no more. \\n\\nThe U.S. Department of Justice is assembling a dedicated task force to go after the crimes of Russian oligarchs. \\n\\nWe are joining with our European allies to find and seize your yachts your luxury apartments your private jets. We are coming for your ill-begotten gains.'),\n",
|
||||
" Document(page_content='We are joining with our European allies to find and seize your yachts your luxury apartments your private jets. We are coming for your ill-begotten gains. \\n\\nAnd tonight I am announcing that we will join our allies in closing off American air space to all Russian flights – further isolating Russia – and adding an additional squeeze –on their economy. The Ruble has lost 30% of its value. \\n\\nThe Russian stock market has lost 40% of its value and trading remains suspended. Russia’s economy is reeling and Putin alone is to blame. \\n\\nTogether with our allies we are providing support to the Ukrainians in their fight for freedom. Military assistance. Economic assistance. Humanitarian assistance. \\n\\nWe are giving more than $1 Billion in direct assistance to Ukraine. \\n\\nAnd we will continue to aid the Ukrainian people as they defend their country and to help ease their suffering. \\n\\nLet me be clear, our forces are not engaged and will not engage in conflict with Russian forces in Ukraine. \\n\\nOur forces are not going to Europe to fight in Ukraine, but to defend our NATO Allies – in the event that Putin decides to keep moving west. \\n\\nFor that purpose we’ve mobilized American ground forces, air squadrons, and ship deployments to protect NATO countries including Poland, Romania, Latvia, Lithuania, and Estonia. \\n\\nAs I have made crystal clear the United States and our Allies will defend every inch of territory of NATO countries with the full force of our collective power. \\n\\nAnd we remain clear-eyed. The Ukrainians are fighting back with pure courage. But the next few days weeks, months, will be hard on them. \\n\\nPutin has unleashed violence and chaos. But while he may make gains on the battlefield – he will pay a continuing high price over the long run. \\n\\nAnd a proud Ukrainian people, who have known 30 years of independence, have repeatedly shown that they will not tolerate anyone who tries to take their country backwards. \\n\\nTo all Americans, I will be honest with you, as I’ve always promised. A Russian dictator, invading a foreign country, has costs around the world. \\n\\nAnd 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\\nTonight, 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\\nAmerica 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\\nThese steps will help blunt gas prices here at home. And I know the news about what’s happening can seem alarming. \\n\\nBut I want you to know that we are going to be okay. \\n\\nWhen 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\\nWhile 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\\nWe see the unity among leaders of nations and a more unified Europe a more unified West. And we see unity among the people who are gathering in cities in large crowds around the world even in Russia to demonstrate their support for Ukraine. \\n\\nIn the battle between democracy and autocracy, democracies are rising to the moment, and the world is clearly choosing the side of peace and security. \\n\\nThis is a real test. It’s going to take time. So let us continue to draw inspiration from the iron will of the Ukrainian people. \\n\\nTo our fellow Ukrainian Americans who forge a deep bond that connects our two nations we stand with you. \\n\\nPutin may circle Kyiv with tanks, but he will never gain the hearts and souls of the Ukrainian people. \\n\\nHe will never extinguish their love of freedom. He will never weaken the resolve of the free world. \\n\\nWe meet tonight in an America that has lived through two of the hardest years this nation has ever faced.'),\n",
|
||||
" Document(page_content='We meet tonight in an America that has lived through two of the hardest years this nation has ever faced. \\n\\nThe pandemic has been punishing. \\n\\nAnd so many families are living paycheck to paycheck, struggling to keep up with the rising cost of food, gas, housing, and so much more. \\n\\nI understand. \\n\\nI remember when my Dad had to leave our home in Scranton, Pennsylvania to find work. I grew up in a family where if the price of food went up, you felt it. \\n\\nThat’s why one of the first things I did as President was fight to pass the American Rescue Plan. \\n\\nBecause people were hurting. We needed to act, and we did. \\n\\nFew pieces of legislation have done more in a critical moment in our history to lift us out of crisis. \\n\\nIt fueled our efforts to vaccinate the nation and combat COVID-19. It delivered immediate economic relief for tens of millions of Americans. \\n\\nHelped put food on their table, keep a roof over their heads, and cut the cost of health insurance. \\n\\nAnd as my Dad used to say, it gave people a little breathing room. \\n\\nAnd unlike the $2 Trillion tax cut passed in the previous administration that benefitted the top 1% of Americans, the American Rescue Plan helped working people—and left no one behind. \\n\\nAnd it worked. It created jobs. Lots of jobs. \\n\\nIn fact—our economy created over 6.5 Million new jobs just last year, more jobs created in one year \\nthan ever before in the history of America. \\n\\nOur economy grew at a rate of 5.7% last year, the strongest growth in nearly 40 years, the first step in bringing fundamental change to an economy that hasn’t worked for the working people of this nation for too long. \\n\\nFor the past 40 years we were told that if we gave tax breaks to those at the very top, the benefits would trickle down to everyone else. \\n\\nBut that trickle-down theory led to weaker economic growth, lower wages, bigger deficits, and the widest gap between those at the top and everyone else in nearly a century. \\n\\nVice President Harris and I ran for office with a new economic vision for America. \\n\\nInvest in America. Educate Americans. Grow the workforce. Build the economy from the bottom up \\nand the middle out, not from the top down. \\n\\nBecause we know that when the middle class grows, the poor have a ladder up and the wealthy do very well. \\n\\nAmerica used to have the best roads, bridges, and airports on Earth. \\n\\nNow our infrastructure is ranked 13th in the world. \\n\\nWe won’t be able to compete for the jobs of the 21st Century if we don’t fix that. \\n\\nThat’s why it was so important to pass the Bipartisan Infrastructure Law—the most sweeping investment to rebuild America in history. \\n\\nThis was a bipartisan effort, and I want to thank the members of both parties who worked to make it happen. \\n\\nWe’re done talking about infrastructure weeks. \\n\\nWe’re going to have an infrastructure decade. \\n\\nIt is going to transform America and put us on a path to win the economic competition of the 21st Century that we face with the rest of the world—particularly with China. \\n\\nAs I’ve told Xi Jinping, it is never a good bet to bet against the American people. \\n\\nWe’ll create good jobs for millions of Americans, modernizing roads, airports, ports, and waterways all across America. \\n\\nAnd we’ll do it all to withstand the devastating effects of the climate crisis and promote environmental justice. \\n\\nWe’ll build a national network of 500,000 electric vehicle charging stations, begin to replace poisonous lead pipes—so every child—and every American—has clean water to drink at home and at school, provide affordable high-speed internet for every American—urban, suburban, rural, and tribal communities. \\n\\n4,000 projects have already been announced. \\n\\nAnd tonight, I’m announcing that this year we will start fixing over 65,000 miles of highway and 1,500 bridges in disrepair. \\n\\nWhen we use taxpayer dollars to rebuild America – we are going to Buy American: buy American products to support American jobs.')],\n",
|
||||
" 'output_text': \" The speaker addresses the unity and strength of Americans and discusses the recent conflict with Russia and actions taken by the US and its allies. They announce closures of airspace, support for Ukraine, and measures to target corrupt Russian leaders. President Biden reflects on past hardships and highlights efforts to pass the American Rescue Plan. He criticizes the previous administration's policies and shares plans for the economy, including investing in America, education, rebuilding infrastructure, and supporting American jobs. \"}"
|
||||
]
|
||||
},
|
||||
"execution_count": 21,
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"chain.run(docs)"
|
||||
"chain.invoke(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1997,7 +2064,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"execution_count": 19,
|
||||
"id": "39cbb282",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -2005,32 +2072,43 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 11.5 ms, sys: 4.33 ms, total: 15.8 ms\n",
|
||||
"Wall time: 1.04 s\n"
|
||||
"CPU times: user 7 ms, sys: 1.94 ms, total: 8.94 ms\n",
|
||||
"Wall time: 1.06 s\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'\\n\\nPresident Biden is discussing the American Rescue Plan and the Bipartisan Infrastructure Law, which will create jobs and help Americans. He also talks about his vision for America, which includes investing in education and infrastructure.'"
|
||||
"{'input_documents': [Document(page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. \\n\\nLast year COVID-19 kept us apart. This year we are finally together again. \\n\\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \\n\\nWith a duty to one another to the American people to the Constitution. \\n\\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \\n\\nSix days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \\n\\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \\n\\nHe met the Ukrainian people. \\n\\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world. \\n\\nGroups of citizens blocking tanks with their bodies. Everyone from students to retirees teachers turned soldiers defending their homeland. \\n\\nIn this struggle as President Zelenskyy said in his speech to the European Parliament “Light will win over darkness.” The Ukrainian Ambassador to the United States is here tonight. \\n\\nLet each of us here tonight in this Chamber send an unmistakable signal to Ukraine and to the world. \\n\\nPlease rise if you are able and show that, Yes, we the United States of America stand with the Ukrainian people. \\n\\nThroughout our history we’ve learned this lesson when dictators do not pay a price for their aggression they cause more chaos. \\n\\nThey keep moving. \\n\\nAnd the costs and the threats to America and the world keep rising. \\n\\nThat’s why the NATO Alliance was created to secure peace and stability in Europe after World War 2. \\n\\nThe United States is a member along with 29 other nations. \\n\\nIt matters. American diplomacy matters. American resolve matters. \\n\\nPutin’s latest attack on Ukraine was premeditated and unprovoked. \\n\\nHe rejected repeated efforts at diplomacy. \\n\\nHe thought the West and NATO wouldn’t respond. And he thought he could divide us at home. Putin was wrong. We were ready. Here is what we did. \\n\\nWe prepared extensively and carefully. \\n\\nWe spent months building a coalition of other freedom-loving nations from Europe and the Americas to Asia and Africa to confront Putin. \\n\\nI spent countless hours unifying our European allies. We shared with the world in advance what we knew Putin was planning and precisely how he would try to falsely justify his aggression. \\n\\nWe countered Russia’s lies with truth. \\n\\nAnd now that he has acted the free world is holding him accountable. \\n\\nAlong with twenty-seven members of the European Union including France, Germany, Italy, as well as countries like the United Kingdom, Canada, Japan, Korea, Australia, New Zealand, and many others, even Switzerland. \\n\\nWe are inflicting pain on Russia and supporting the people of Ukraine. Putin is now isolated from the world more than ever. \\n\\nTogether with our allies –we are right now enforcing powerful economic sanctions. \\n\\nWe are cutting off Russia’s largest banks from the international financial system. \\n\\nPreventing Russia’s central bank from defending the Russian Ruble making Putin’s $630 Billion “war fund” worthless. \\n\\nWe are choking off Russia’s access to technology that will sap its economic strength and weaken its military for years to come. \\n\\nTonight I say to the Russian oligarchs and corrupt leaders who have bilked billions of dollars off this violent regime no more. \\n\\nThe U.S. Department of Justice is assembling a dedicated task force to go after the crimes of Russian oligarchs. \\n\\nWe are joining with our European allies to find and seize your yachts your luxury apartments your private jets. We are coming for your ill-begotten gains.'),\n",
|
||||
" Document(page_content='We are joining with our European allies to find and seize your yachts your luxury apartments your private jets. We are coming for your ill-begotten gains. \\n\\nAnd tonight I am announcing that we will join our allies in closing off American air space to all Russian flights – further isolating Russia – and adding an additional squeeze –on their economy. The Ruble has lost 30% of its value. \\n\\nThe Russian stock market has lost 40% of its value and trading remains suspended. Russia’s economy is reeling and Putin alone is to blame. \\n\\nTogether with our allies we are providing support to the Ukrainians in their fight for freedom. Military assistance. Economic assistance. Humanitarian assistance. \\n\\nWe are giving more than $1 Billion in direct assistance to Ukraine. \\n\\nAnd we will continue to aid the Ukrainian people as they defend their country and to help ease their suffering. \\n\\nLet me be clear, our forces are not engaged and will not engage in conflict with Russian forces in Ukraine. \\n\\nOur forces are not going to Europe to fight in Ukraine, but to defend our NATO Allies – in the event that Putin decides to keep moving west. \\n\\nFor that purpose we’ve mobilized American ground forces, air squadrons, and ship deployments to protect NATO countries including Poland, Romania, Latvia, Lithuania, and Estonia. \\n\\nAs I have made crystal clear the United States and our Allies will defend every inch of territory of NATO countries with the full force of our collective power. \\n\\nAnd we remain clear-eyed. The Ukrainians are fighting back with pure courage. But the next few days weeks, months, will be hard on them. \\n\\nPutin has unleashed violence and chaos. But while he may make gains on the battlefield – he will pay a continuing high price over the long run. \\n\\nAnd a proud Ukrainian people, who have known 30 years of independence, have repeatedly shown that they will not tolerate anyone who tries to take their country backwards. \\n\\nTo all Americans, I will be honest with you, as I’ve always promised. A Russian dictator, invading a foreign country, has costs around the world. \\n\\nAnd 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\\nTonight, 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\\nAmerica 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\\nThese steps will help blunt gas prices here at home. And I know the news about what’s happening can seem alarming. \\n\\nBut I want you to know that we are going to be okay. \\n\\nWhen 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\\nWhile 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\\nWe see the unity among leaders of nations and a more unified Europe a more unified West. And we see unity among the people who are gathering in cities in large crowds around the world even in Russia to demonstrate their support for Ukraine. \\n\\nIn the battle between democracy and autocracy, democracies are rising to the moment, and the world is clearly choosing the side of peace and security. \\n\\nThis is a real test. It’s going to take time. So let us continue to draw inspiration from the iron will of the Ukrainian people. \\n\\nTo our fellow Ukrainian Americans who forge a deep bond that connects our two nations we stand with you. \\n\\nPutin may circle Kyiv with tanks, but he will never gain the hearts and souls of the Ukrainian people. \\n\\nHe will never extinguish their love of freedom. He will never weaken the resolve of the free world. \\n\\nWe meet tonight in an America that has lived through two of the hardest years this nation has ever faced.'),\n",
|
||||
" Document(page_content='We meet tonight in an America that has lived through two of the hardest years this nation has ever faced. \\n\\nThe pandemic has been punishing. \\n\\nAnd so many families are living paycheck to paycheck, struggling to keep up with the rising cost of food, gas, housing, and so much more. \\n\\nI understand. \\n\\nI remember when my Dad had to leave our home in Scranton, Pennsylvania to find work. I grew up in a family where if the price of food went up, you felt it. \\n\\nThat’s why one of the first things I did as President was fight to pass the American Rescue Plan. \\n\\nBecause people were hurting. We needed to act, and we did. \\n\\nFew pieces of legislation have done more in a critical moment in our history to lift us out of crisis. \\n\\nIt fueled our efforts to vaccinate the nation and combat COVID-19. It delivered immediate economic relief for tens of millions of Americans. \\n\\nHelped put food on their table, keep a roof over their heads, and cut the cost of health insurance. \\n\\nAnd as my Dad used to say, it gave people a little breathing room. \\n\\nAnd unlike the $2 Trillion tax cut passed in the previous administration that benefitted the top 1% of Americans, the American Rescue Plan helped working people—and left no one behind. \\n\\nAnd it worked. It created jobs. Lots of jobs. \\n\\nIn fact—our economy created over 6.5 Million new jobs just last year, more jobs created in one year \\nthan ever before in the history of America. \\n\\nOur economy grew at a rate of 5.7% last year, the strongest growth in nearly 40 years, the first step in bringing fundamental change to an economy that hasn’t worked for the working people of this nation for too long. \\n\\nFor the past 40 years we were told that if we gave tax breaks to those at the very top, the benefits would trickle down to everyone else. \\n\\nBut that trickle-down theory led to weaker economic growth, lower wages, bigger deficits, and the widest gap between those at the top and everyone else in nearly a century. \\n\\nVice President Harris and I ran for office with a new economic vision for America. \\n\\nInvest in America. Educate Americans. Grow the workforce. Build the economy from the bottom up \\nand the middle out, not from the top down. \\n\\nBecause we know that when the middle class grows, the poor have a ladder up and the wealthy do very well. \\n\\nAmerica used to have the best roads, bridges, and airports on Earth. \\n\\nNow our infrastructure is ranked 13th in the world. \\n\\nWe won’t be able to compete for the jobs of the 21st Century if we don’t fix that. \\n\\nThat’s why it was so important to pass the Bipartisan Infrastructure Law—the most sweeping investment to rebuild America in history. \\n\\nThis was a bipartisan effort, and I want to thank the members of both parties who worked to make it happen. \\n\\nWe’re done talking about infrastructure weeks. \\n\\nWe’re going to have an infrastructure decade. \\n\\nIt is going to transform America and put us on a path to win the economic competition of the 21st Century that we face with the rest of the world—particularly with China. \\n\\nAs I’ve told Xi Jinping, it is never a good bet to bet against the American people. \\n\\nWe’ll create good jobs for millions of Americans, modernizing roads, airports, ports, and waterways all across America. \\n\\nAnd we’ll do it all to withstand the devastating effects of the climate crisis and promote environmental justice. \\n\\nWe’ll build a national network of 500,000 electric vehicle charging stations, begin to replace poisonous lead pipes—so every child—and every American—has clean water to drink at home and at school, provide affordable high-speed internet for every American—urban, suburban, rural, and tribal communities. \\n\\n4,000 projects have already been announced. \\n\\nAnd tonight, I’m announcing that this year we will start fixing over 65,000 miles of highway and 1,500 bridges in disrepair. \\n\\nWhen we use taxpayer dollars to rebuild America – we are going to Buy American: buy American products to support American jobs.')],\n",
|
||||
" 'output_text': '\\n\\nThe speaker addresses the unity of Americans and discusses the conflict with Russia and support for Ukraine. The US and allies are taking action against Russia and targeting corrupt leaders. There is also support and assurance for the American people. President Biden reflects on recent hardships and highlights efforts to pass the American Rescue Plan. He also shares plans for economic growth and investment in America. '}"
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"chain.run(docs)"
|
||||
"chain.invoke(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 20,
|
||||
"id": "9df0dab8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"rm: sqlite.db: No such file or directory\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!rm .langchain.db sqlite.db"
|
||||
]
|
||||
@@ -2105,7 +2183,7 @@
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"# The first time, it is not yet in cache, so it should take longer\n",
|
||||
"llm(\"Tell me a joke\")"
|
||||
"llm.invoke(\"Tell me a joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2142,7 +2220,7 @@
|
||||
"%%time\n",
|
||||
"# The second time, while not a direct hit, the question is semantically similar to the original question,\n",
|
||||
"# so it uses the cached result!\n",
|
||||
"llm(\"Tell me one joke\")"
|
||||
"llm.invoke(\"Tell me one joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2192,6 +2270,16 @@
|
||||
"The standard cache that looks for an exact match of the user prompt."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ac0a2276",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain_couchbase couchbase"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
@@ -2578,14 +2666,6 @@
|
||||
"| langchain_couchbase.cache | [CouchbaseCache](https://api.python.langchain.com/en/latest/cache/langchain_couchbase.cache.CouchbaseCache.html) |\n",
|
||||
"| langchain_couchbase.cache | [CouchbaseSemanticCache](https://api.python.langchain.com/en/latest/cache/langchain_couchbase.cache.CouchbaseSemanticCache.html) |\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "19067f14-c69a-4156-9504-af43a0713669",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -2604,7 +2684,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.13"
|
||||
"version": "3.10.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
133
docs/docs/integrations/llms/yi.ipynb
Normal file
133
docs/docs/integrations/llms/yi.ipynb
Normal file
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Yi\n",
|
||||
"[01.AI](https://www.lingyiwanwu.com/en), founded by Dr. Kai-Fu Lee, is a global company at the forefront of AI 2.0. They offer cutting-edge large language models, including the Yi series, which range from 6B to hundreds of billions of parameters. 01.AI also provides multimodal models, an open API platform, and open-source options like Yi-34B/9B/6B and Yi-VL."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Installing the langchain packages needed to use the integration\n",
|
||||
"%pip install -qU langchain-community"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Prerequisite\n",
|
||||
"An API key is required to access Yi LLM API. Visit https://www.lingyiwanwu.com/ to get your API key. When applying for the API key, you need to specify whether it's for domestic (China) or international use."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Use Yi LLM"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"YI_API_KEY\"] = \"YOUR_API_KEY\"\n",
|
||||
"\n",
|
||||
"from langchain_community.llms import YiLLM\n",
|
||||
"\n",
|
||||
"# Load the model\n",
|
||||
"llm = YiLLM(model=\"yi-large\")\n",
|
||||
"\n",
|
||||
"# You can specify the region if needed (default is \"auto\")\n",
|
||||
"# llm = YiLLM(model=\"yi-large\", region=\"domestic\") # or \"international\"\n",
|
||||
"\n",
|
||||
"# Basic usage\n",
|
||||
"res = llm.invoke(\"What's your name?\")\n",
|
||||
"print(res)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Generate method\n",
|
||||
"res = llm.generate(\n",
|
||||
" prompts=[\n",
|
||||
" \"Explain the concept of large language models.\",\n",
|
||||
" \"What are the potential applications of AI in healthcare?\",\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"print(res)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Streaming\n",
|
||||
"for chunk in llm.stream(\"Describe the key features of the Yi language model series.\"):\n",
|
||||
" print(chunk, end=\"\", flush=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Asynchronous streaming\n",
|
||||
"import asyncio\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def run_aio_stream():\n",
|
||||
" async for chunk in llm.astream(\n",
|
||||
" \"Write a brief on the future of AI according to Dr. Kai-Fu Lee's vision.\"\n",
|
||||
" ):\n",
|
||||
" print(chunk, end=\"\", flush=True)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"asyncio.run(run_aio_stream())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Adjusting parameters\n",
|
||||
"llm_with_params = YiLLM(\n",
|
||||
" model=\"yi-large\",\n",
|
||||
" temperature=0.7,\n",
|
||||
" top_p=0.9,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"res = llm_with_params(\n",
|
||||
" \"Propose an innovative AI application that could benefit society.\"\n",
|
||||
")\n",
|
||||
"print(res)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"source": [
|
||||
"# TiDB\n",
|
||||
"\n",
|
||||
"> [TiDB Cloud](https://tidbcloud.com/), is a comprehensive Database-as-a-Service (DBaaS) solution, that provides dedicated and serverless options. TiDB Serverless is now integrating a built-in vector search into the MySQL landscape. With this enhancement, you can seamlessly develop AI applications using TiDB Serverless without the need for a new database or additional technical stacks. Be among the first to experience it by joining the waitlist for the private beta at https://tidb.cloud/ai.\n",
|
||||
"> [TiDB Cloud](https://www.pingcap.com/tidb-serverless/), is a comprehensive Database-as-a-Service (DBaaS) solution, that provides dedicated and serverless options. TiDB Serverless is now integrating a built-in vector search into the MySQL landscape. With this enhancement, you can seamlessly develop AI applications using TiDB Serverless without the need for a new database or additional technical stacks. Create a free TiDB Serverless cluster and start using the vector search feature at https://pingcap.com/ai.\n",
|
||||
"\n",
|
||||
"This notebook introduces how to use TiDB to store chat message history. "
|
||||
]
|
||||
|
||||
@@ -140,6 +140,18 @@ See a [usage example](/docs/integrations/text_embedding/google_vertex_ai_palm).
|
||||
from langchain_google_vertexai import VertexAIEmbeddings
|
||||
```
|
||||
|
||||
### Palm Embedding
|
||||
|
||||
We need to install `langchain-community` python package.
|
||||
|
||||
```bash
|
||||
pip install langchain-community
|
||||
```
|
||||
|
||||
```python
|
||||
from langchain_community.embeddings.google_palm import GooglePalmEmbeddings
|
||||
```
|
||||
|
||||
## Document Loaders
|
||||
|
||||
### AlloyDB for PostgreSQL
|
||||
|
||||
@@ -40,6 +40,7 @@ These providers have standalone `langchain-{provider}` packages for improved ver
|
||||
- [Qdrant](/docs/integrations/providers/qdrant)
|
||||
- [Robocorp](/docs/integrations/providers/robocorp)
|
||||
- [Together AI](/docs/integrations/providers/together)
|
||||
- [Unstructured](/docs/integrations/providers/unstructured)
|
||||
- [Upstage](/docs/integrations/providers/upstage)
|
||||
- [Voyage AI](/docs/integrations/providers/voyageai)
|
||||
|
||||
|
||||
@@ -156,6 +156,20 @@ See a [usage example](/docs/integrations/document_loaders/microsoft_onedrive).
|
||||
from langchain_community.document_loaders import OneDriveLoader
|
||||
```
|
||||
|
||||
### Microsoft OneDrive File
|
||||
|
||||
>[Microsoft OneDrive](https://en.wikipedia.org/wiki/OneDrive) (formerly `SkyDrive`) is a file-hosting service operated by Microsoft.
|
||||
|
||||
First, you need to install a python package.
|
||||
|
||||
```bash
|
||||
pip install o365
|
||||
```
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders import OneDriveFileLoader
|
||||
```
|
||||
|
||||
|
||||
### Microsoft Word
|
||||
|
||||
@@ -338,7 +352,7 @@ Follow the documentation [here](/docs/integrations/tools/bing_search) to get a d
|
||||
|
||||
The environment variable `BING_SUBSCRIPTION_KEY` and `BING_SEARCH_URL` are required from Bing Search resource.
|
||||
|
||||
```bash
|
||||
```python
|
||||
from langchain_community.tools.bing_search import BingSearchResults
|
||||
from langchain_community.utilities import BingSearchAPIWrapper
|
||||
|
||||
|
||||
@@ -68,3 +68,18 @@ Learn more in the [example notebook](/docs/integrations/document_loaders/cassand
|
||||
|
||||
> Apache Cassandra, Cassandra and Apache are either registered trademarks or trademarks of
|
||||
> the [Apache Software Foundation](http://www.apache.org/) in the United States and/or other countries.
|
||||
|
||||
## Toolkit
|
||||
|
||||
The `Cassandra Database toolkit` enables AI engineers to efficiently integrate agents
|
||||
with Cassandra data.
|
||||
|
||||
```python
|
||||
from langchain_community.agent_toolkits.cassandra_database.toolkit import (
|
||||
CassandraDatabaseToolkit,
|
||||
)
|
||||
```
|
||||
|
||||
Learn more in the [example notebook](/docs/integrations/toolkits/cassandra_database).
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ You need to install `langchain-robocorp` python package:
|
||||
pip install langchain-robocorp
|
||||
```
|
||||
|
||||
You will need a running instance of Action Server to communicate with from your agent application. See the [Robocorp Quickstart](https://github.com/robocorp/robocorp#quickstart) on how to setup Action Server and create your Actions.
|
||||
You will need a running instance of `Action Server` to communicate with from your agent application.
|
||||
See the [Robocorp Quickstart](https://github.com/robocorp/robocorp#quickstart) on how to setup Action Server and create your Actions.
|
||||
|
||||
You can bootstrap a new project using Action Server `new` command.
|
||||
|
||||
@@ -21,6 +22,12 @@ cd ./your-project-name
|
||||
action-server start
|
||||
```
|
||||
|
||||
## Tool
|
||||
|
||||
```python
|
||||
from langchain_robocorp.toolkits import ActionServerRequestTool
|
||||
```
|
||||
|
||||
## Toolkit
|
||||
|
||||
See a [usage example](/docs/integrations/toolkits/robocorp).
|
||||
|
||||
25
docs/docs/integrations/providers/sap.mdx
Normal file
25
docs/docs/integrations/providers/sap.mdx
Normal file
@@ -0,0 +1,25 @@
|
||||
# SAP
|
||||
|
||||
>[SAP SE(Wikipedia)](https://www.sap.com/about/company.html) is a German multinational
|
||||
> software company. It develops enterprise software to manage business operation and
|
||||
> customer relations. The company is the world's leading
|
||||
> `enterprise resource planning (ERP)` software vendor.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
We need to install the `hdbcli` python package.
|
||||
|
||||
```bash
|
||||
pip install hdbcli
|
||||
```
|
||||
|
||||
## Vectorstore
|
||||
|
||||
>[SAP HANA Cloud Vector Engine](https://www.sap.com/events/teched/news-guide/ai.html#article8) is
|
||||
> a vector store fully integrated into the `SAP HANA Cloud` database.
|
||||
|
||||
See a [usage example](/docs/integrations/vectorstores/sap_hanavector).
|
||||
|
||||
```python
|
||||
from langchain_community.vectorstores.hanavector import HanaDB
|
||||
```
|
||||
@@ -20,3 +20,16 @@ from langchain_community.vectorstores import SKLearnVectorStore
|
||||
```
|
||||
|
||||
For a more detailed walkthrough of the SKLearnVectorStore wrapper, see [this notebook](/docs/integrations/vectorstores/sklearn).
|
||||
|
||||
|
||||
## Retriever
|
||||
|
||||
`Support vector machines (SVMs)` are the supervised learning
|
||||
methods used for classification, regression and outliers detection.
|
||||
|
||||
See a [usage example](/docs/integrations/retrievers/svm).
|
||||
|
||||
```python
|
||||
from langchain_community.retrievers import SVMRetriever
|
||||
```
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
There isn't any special setup for it.
|
||||
|
||||
|
||||
|
||||
## Document loader
|
||||
|
||||
See a [usage example](/docs/integrations/document_loaders/slack).
|
||||
@@ -16,6 +15,14 @@ See a [usage example](/docs/integrations/document_loaders/slack).
|
||||
from langchain_community.document_loaders import SlackDirectoryLoader
|
||||
```
|
||||
|
||||
## Toolkit
|
||||
|
||||
See a [usage example](/docs/integrations/toolkits/slack).
|
||||
|
||||
```python
|
||||
from langchain_community.agent_toolkits import SlackToolkit
|
||||
```
|
||||
|
||||
## Chat loader
|
||||
|
||||
See a [usage example](/docs/integrations/chat_loaders/slack).
|
||||
|
||||
@@ -7,8 +7,8 @@ This page covers how to use the `Snowflake` ecosystem within `LangChain`.
|
||||
|
||||
## Embedding models
|
||||
|
||||
Snowflake offers their open weight `arctic` line of embedding models for free
|
||||
on [Hugging Face](https://huggingface.co/Snowflake/snowflake-arctic-embed-l).
|
||||
Snowflake offers their open-weight `arctic` line of embedding models for free
|
||||
on [Hugging Face](https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5). The most recent model, snowflake-arctic-embed-m-v1.5 feature [matryoshka embedding](https://arxiv.org/abs/2205.13147) which allows for effective vector truncation.
|
||||
You can use these models via the
|
||||
[HuggingFaceEmbeddings](/docs/integrations/text_embedding/huggingfacehub) connector:
|
||||
|
||||
@@ -19,7 +19,7 @@ pip install langchain-community sentence-transformers
|
||||
```python
|
||||
from langchain_huggingface import HuggingFaceEmbeddings
|
||||
|
||||
model = HuggingFaceEmbeddings(model_name="snowflake/arctic-embed-l")
|
||||
model = HuggingFaceEmbeddings(model_name="snowflake/arctic-embed-m-v1.5")
|
||||
```
|
||||
|
||||
## Document loader
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# TiDB
|
||||
|
||||
> [TiDB Cloud](https://tidbcloud.com/), is a comprehensive Database-as-a-Service (DBaaS) solution,
|
||||
> [TiDB Cloud](https://www.pingcap.com/tidb-serverless), is a comprehensive Database-as-a-Service (DBaaS) solution,
|
||||
> that provides dedicated and serverless options. `TiDB Serverless` is now integrating
|
||||
> a built-in vector search into the MySQL landscape. With this enhancement, you can seamlessly
|
||||
> develop AI applications using `TiDB Serverless` without the need for a new database or additional
|
||||
> technical stacks. Be among the first to experience it by joining the [waitlist for the private beta](https://tidb.cloud/ai).
|
||||
> technical stacks. Create a free TiDB Serverless cluster and start using the vector search feature at https://pingcap.com/ai.
|
||||
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
@@ -8,11 +8,21 @@ ecosystem within LangChain.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
If you are using a loader that runs locally, use the following steps to get `unstructured` and
|
||||
its dependencies running locally.
|
||||
If you are using a loader that runs locally, use the following steps to get `unstructured` and its
|
||||
dependencies running.
|
||||
|
||||
- Install the Python SDK with `pip install unstructured`.
|
||||
- You can install document specific dependencies with extras, i.e. `pip install "unstructured[docx]"`.
|
||||
- For the smallest installation footprint and to take advantage of features not available in the
|
||||
open-source `unstructured` package, install the Python SDK with `pip install unstructured-client`
|
||||
along with `pip install langchain-unstructured` to use the `UnstructuredLoader` and partition
|
||||
remotely against the Unstructured API. This loader lives
|
||||
in a LangChain partner repo instead of the `langchain-community` repo and you will need an
|
||||
`api_key`, which you can generate a free key [here](https://unstructured.io/api-key/).
|
||||
- Unstructured's documentation for the sdk can be found here:
|
||||
https://docs.unstructured.io/api-reference/api-services/sdk
|
||||
|
||||
- To run everything locally, install the open-source python package with `pip install unstructured`
|
||||
along with `pip install langchain-community` and use the same `UnstructuredLoader` as mentioned above.
|
||||
- You can install document specific dependencies with extras, e.g. `pip install "unstructured[docx]"`.
|
||||
- To install the dependencies for all document types, use `pip install "unstructured[all-docs]"`.
|
||||
- Install the following system dependencies if they are not already available on your system with e.g. `brew install` for Mac.
|
||||
Depending on what document types you're parsing, you may not need all of these.
|
||||
@@ -22,16 +32,11 @@ its dependencies running locally.
|
||||
- `qpdf` (PDFs)
|
||||
- `libreoffice` (MS Office docs)
|
||||
- `pandoc` (EPUBs)
|
||||
- When running locally, Unstructured also recommends using Docker [by following this
|
||||
guide](https://docs.unstructured.io/open-source/installation/docker-installation) to ensure all
|
||||
system dependencies are installed correctly.
|
||||
|
||||
When running locally, Unstructured also recommends using Docker [by following this guide](https://docs.unstructured.io/open-source/installation/docker-installation)
|
||||
to ensure all system dependencies are installed correctly.
|
||||
|
||||
If you want to get up and running with less set up, you can
|
||||
simply run `pip install unstructured` and use `UnstructuredAPIFileLoader` or
|
||||
`UnstructuredAPIFileIOLoader`. That will process your document using the hosted Unstructured API.
|
||||
|
||||
|
||||
The `Unstructured API` requires API keys to make requests.
|
||||
The Unstructured API requires API keys to make requests.
|
||||
You can request an API key [here](https://unstructured.io/api-key-hosted) and start using it today!
|
||||
Checkout the README [here](https://github.com/Unstructured-IO/unstructured-api) here to get started making API calls.
|
||||
We'd love to hear your feedback, let us know how it goes in our [community slack](https://join.slack.com/t/unstructuredw-kbe4326/shared_invite/zt-1x7cgo0pg-PTptXWylzPQF9xZolzCnwQ).
|
||||
@@ -42,30 +47,21 @@ Check out the instructions
|
||||
|
||||
## Data Loaders
|
||||
|
||||
The primary usage of the `Unstructured` is in data loaders.
|
||||
The primary usage of `Unstructured` is in data loaders.
|
||||
|
||||
### UnstructuredAPIFileIOLoader
|
||||
### UnstructuredLoader
|
||||
|
||||
See a [usage example](/docs/integrations/document_loaders/unstructured_file#unstructured-api).
|
||||
See a [usage example](/docs/integrations/document_loaders/unstructured_file) to see how you can use
|
||||
this loader for both partitioning locally and remotely with the serverless Unstructured API.
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders import UnstructuredAPIFileIOLoader
|
||||
```
|
||||
|
||||
### UnstructuredAPIFileLoader
|
||||
|
||||
See a [usage example](/docs/integrations/document_loaders/unstructured_file#unstructured-api).
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders import UnstructuredAPIFileLoader
|
||||
from langchain_unstructured import UnstructuredLoader
|
||||
```
|
||||
|
||||
### UnstructuredCHMLoader
|
||||
|
||||
`CHM` means `Microsoft Compiled HTML Help`.
|
||||
|
||||
See a usage example in the API documentation.
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders import UnstructuredCHMLoader
|
||||
```
|
||||
@@ -119,15 +115,6 @@ See a [usage example](/docs/integrations/document_loaders/google_drive#passing-i
|
||||
from langchain_community.document_loaders import UnstructuredFileIOLoader
|
||||
```
|
||||
|
||||
### UnstructuredFileLoader
|
||||
|
||||
See a [usage example](/docs/integrations/document_loaders/unstructured_file).
|
||||
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders import UnstructuredFileLoader
|
||||
```
|
||||
|
||||
### UnstructuredHTMLLoader
|
||||
|
||||
See a [usage example](/docs/how_to/document_loader_html).
|
||||
|
||||
23
docs/docs/integrations/providers/yi.mdx
Normal file
23
docs/docs/integrations/providers/yi.mdx
Normal file
@@ -0,0 +1,23 @@
|
||||
# 01.AI
|
||||
|
||||
>[01.AI](https://www.lingyiwanwu.com/en), founded by Dr. Kai-Fu Lee, is a global company at the forefront of AI 2.0. They offer cutting-edge large language models, including the Yi series, which range from 6B to hundreds of billions of parameters. 01.AI also provides multimodal models, an open API platform, and open-source options like Yi-34B/9B/6B and Yi-VL.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
Register and get an API key from either the China site [here](https://platform.lingyiwanwu.com/apikeys) or the global site [here](https://platform.01.ai/apikeys).
|
||||
|
||||
## LLMs
|
||||
|
||||
See a [usage example](/docs/integrations/llms/yi).
|
||||
|
||||
```python
|
||||
from langchain_community.llms import YiLLM
|
||||
```
|
||||
|
||||
## Chat models
|
||||
|
||||
See a [usage example](/docs/integrations/chat/yi).
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatYi
|
||||
```
|
||||
@@ -0,0 +1,246 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# SAP HANA Cloud Vector Engine\n",
|
||||
"\n",
|
||||
"For more information on how to setup the SAP HANA vetor store, take a look at the [documentation](/docs/integrations/vectorstores/sap_hanavector.ipynb).\n",
|
||||
"\n",
|
||||
"We use the same setup here:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# Use OPENAI_API_KEY env variable\n",
|
||||
"# os.environ[\"OPENAI_API_KEY\"] = \"Your OpenAI API key\"\n",
|
||||
"from hdbcli import dbapi\n",
|
||||
"\n",
|
||||
"# Use connection settings from the environment\n",
|
||||
"connection = dbapi.connect(\n",
|
||||
" address=os.environ.get(\"HANA_DB_ADDRESS\"),\n",
|
||||
" port=os.environ.get(\"HANA_DB_PORT\"),\n",
|
||||
" user=os.environ.get(\"HANA_DB_USER\"),\n",
|
||||
" password=os.environ.get(\"HANA_DB_PASSWORD\"),\n",
|
||||
" autocommit=True,\n",
|
||||
" sslValidateCertificate=False,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To be able to self query with good performance we create additional metadata fields\n",
|
||||
"for our vectorstore table in HANA:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create custom table with attribute\n",
|
||||
"cur = connection.cursor()\n",
|
||||
"cur.execute(\"DROP TABLE LANGCHAIN_DEMO_SELF_QUERY\", ignoreErrors=True)\n",
|
||||
"cur.execute(\n",
|
||||
" (\n",
|
||||
" \"\"\"CREATE TABLE \"LANGCHAIN_DEMO_SELF_QUERY\" (\n",
|
||||
" \"name\" NVARCHAR(100), \"is_active\" BOOLEAN, \"id\" INTEGER, \"height\" DOUBLE,\n",
|
||||
" \"VEC_TEXT\" NCLOB, \n",
|
||||
" \"VEC_META\" NCLOB, \n",
|
||||
" \"VEC_VECTOR\" REAL_VECTOR\n",
|
||||
" )\"\"\"\n",
|
||||
" )\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's add some documents."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores.hanavector import HanaDB\n",
|
||||
"from langchain_core.documents import Document\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings()\n",
|
||||
"\n",
|
||||
"# Prepare some test documents\n",
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"First\",\n",
|
||||
" metadata={\"name\": \"adam\", \"is_active\": True, \"id\": 1, \"height\": 10.0},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Second\",\n",
|
||||
" metadata={\"name\": \"bob\", \"is_active\": False, \"id\": 2, \"height\": 5.7},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Third\",\n",
|
||||
" metadata={\"name\": \"jane\", \"is_active\": True, \"id\": 3, \"height\": 2.4},\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"db = HanaDB(\n",
|
||||
" connection=connection,\n",
|
||||
" embedding=embeddings,\n",
|
||||
" table_name=\"LANGCHAIN_DEMO_SELF_QUERY\",\n",
|
||||
" specific_metadata_columns=[\"name\", \"is_active\", \"id\", \"height\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Delete already existing documents from the table\n",
|
||||
"db.delete(filter={})\n",
|
||||
"db.add_documents(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Self querying\n",
|
||||
"\n",
|
||||
"Now for the main act: here is how to construct a SelfQueryRetriever for HANA vectorstore:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains.query_constructor.base import AttributeInfo\n",
|
||||
"from langchain.retrievers.self_query.base import SelfQueryRetriever\n",
|
||||
"from langchain_community.query_constructors.hanavector import HanaTranslator\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo\")\n",
|
||||
"\n",
|
||||
"metadata_field_info = [\n",
|
||||
" AttributeInfo(\n",
|
||||
" name=\"name\",\n",
|
||||
" description=\"The name of the person\",\n",
|
||||
" type=\"string\",\n",
|
||||
" ),\n",
|
||||
" AttributeInfo(\n",
|
||||
" name=\"is_active\",\n",
|
||||
" description=\"Whether the person is active\",\n",
|
||||
" type=\"boolean\",\n",
|
||||
" ),\n",
|
||||
" AttributeInfo(\n",
|
||||
" name=\"id\",\n",
|
||||
" description=\"The ID of the person\",\n",
|
||||
" type=\"integer\",\n",
|
||||
" ),\n",
|
||||
" AttributeInfo(\n",
|
||||
" name=\"height\",\n",
|
||||
" description=\"The height of the person\",\n",
|
||||
" type=\"float\",\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"document_content_description = \"A collection of persons\"\n",
|
||||
"\n",
|
||||
"hana_translator = HanaTranslator()\n",
|
||||
"\n",
|
||||
"retriever = SelfQueryRetriever.from_llm(\n",
|
||||
" llm,\n",
|
||||
" db,\n",
|
||||
" document_content_description,\n",
|
||||
" metadata_field_info,\n",
|
||||
" structured_query_translator=hana_translator,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's use this retriever to prepare a (self) query for a person:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_prompt = \"Which person is not active?\"\n",
|
||||
"\n",
|
||||
"docs = retriever.invoke(input=query_prompt)\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content, \" \", doc.metadata)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can also take a look at how the query is being constructed:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains.query_constructor.base import (\n",
|
||||
" StructuredQueryOutputParser,\n",
|
||||
" get_query_constructor_prompt,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"prompt = get_query_constructor_prompt(\n",
|
||||
" document_content_description,\n",
|
||||
" metadata_field_info,\n",
|
||||
")\n",
|
||||
"output_parser = StructuredQueryOutputParser.from_components()\n",
|
||||
"query_constructor = prompt | llm | output_parser\n",
|
||||
"\n",
|
||||
"sq = query_constructor.invoke(input=query_prompt)\n",
|
||||
"\n",
|
||||
"print(\"Structured query: \", sq)\n",
|
||||
"\n",
|
||||
"print(\"Translated for hana vector store: \", hana_translator.visit_structured_query(sq))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.10.14"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -8,7 +8,17 @@
|
||||
"# Sentence Transformers on Hugging Face\n",
|
||||
"\n",
|
||||
">[Hugging Face sentence-transformers](https://huggingface.co/sentence-transformers) is a Python framework for state-of-the-art sentence, text and image embeddings.\n",
|
||||
">You can use these embedding models from the `HuggingFaceEmbeddings` class."
|
||||
">You can use these embedding models from the `HuggingFaceEmbeddings` class.\n",
|
||||
"\n",
|
||||
":::caution\n",
|
||||
"\n",
|
||||
"Running sentence-transformers locally can be affected by your operating system and other global factors. It is recommended for experienced users only.\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"You'll need to install the `langchain_huggingface` package as a dependency:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -18,12 +28,20 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain-huggingface"
|
||||
"%pip install -qU langchain-huggingface"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8fb16f74",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 5,
|
||||
"id": "ff9be586",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -31,7 +49,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[-0.0383385568857193, 0.12346469610929489, -0.028642987832427025, 0.05365273728966713, 0.00884537026...\n"
|
||||
"[-0.038338568061590195, 0.12346471101045609, -0.028642969205975533, 0.05365273356437683, 0.008845377...\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -49,7 +67,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 6,
|
||||
"id": "bb5e74c0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -57,7 +75,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[[-0.038338493555784225, 0.12346471846103668, -0.028642840683460236, 0.05365276336669922, 0.00884535...\n"
|
||||
"[[-0.038338497281074524, 0.12346471846103668, -0.028642890974879265, 0.05365274101495743, 0.00884535...\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -66,13 +84,25 @@
|
||||
"print(str(doc_result)[:100] + \"...\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1e6525cb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Troubleshooting\n",
|
||||
"\n",
|
||||
"If you are having issues with the `accelerate` package not being found or failing to import, installing/upgrading it may help:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d18544f5",
|
||||
"id": "bbae70f7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": [
|
||||
"%pip install -qU accelerate"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -91,7 +121,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
"version": "3.10.5"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
||||
@@ -6,23 +6,28 @@
|
||||
"source": [
|
||||
"# Cassandra Database\n",
|
||||
"\n",
|
||||
"Apache Cassandra® is a widely used database for storing transactional application data. The introduction of functions and tooling in Large Language Models has opened up some exciting use cases for existing data in Generative AI applications. The Cassandra Database toolkit enables AI engineers to efficiently integrate Agents with Cassandra data, offering the following features: \n",
|
||||
" - Fast data access through optimized queries. Most queries should run in single-digit ms or less. \n",
|
||||
" - Schema introspection to enhance LLM reasoning capabilities \n",
|
||||
" - Compatibility with various Cassandra deployments, including Apache Cassandra®, DataStax Enterprise™, and DataStax Astra™ \n",
|
||||
" - Currently, the toolkit is limited to SELECT queries and schema introspection operations. (Safety first)\n",
|
||||
">`Apache Cassandra®` is a widely used database for storing transactional application data. The introduction of functions and >tooling in Large Language Models has opened up some exciting use cases for existing data in Generative AI applications. \n",
|
||||
"\n",
|
||||
">The `Cassandra Database` toolkit enables AI engineers to integrate agents with Cassandra data efficiently, offering \n",
|
||||
">the following features: \n",
|
||||
"> - Fast data access through optimized queries. Most queries should run in single-digit ms or less.\n",
|
||||
"> - Schema introspection to enhance LLM reasoning capabilities\n",
|
||||
"> - Compatibility with various Cassandra deployments, including Apache Cassandra®, DataStax Enterprise™, and DataStax Astra™\n",
|
||||
"> - Currently, the toolkit is limited to SELECT queries and schema introspection operations. (Safety first)\n",
|
||||
"\n",
|
||||
"For more information on creating a Cassandra DB agent see the [CQL agent cookbook](https://github.com/langchain-ai/langchain/blob/master/cookbook/cql_agent.ipynb)\n",
|
||||
"\n",
|
||||
"## Quick Start\n",
|
||||
" - Install the cassio library\n",
|
||||
" - Install the `cassio` library\n",
|
||||
" - Set environment variables for the Cassandra database you are connecting to\n",
|
||||
" - Initialize CassandraDatabase\n",
|
||||
" - Pass the tools to your agent with toolkit.get_tools()\n",
|
||||
" - Initialize `CassandraDatabase`\n",
|
||||
" - Pass the tools to your agent with `toolkit.get_tools()`\n",
|
||||
" - Sit back and watch it do all your work for you\n",
|
||||
"\n",
|
||||
"## Theory of Operation\n",
|
||||
"Cassandra Query Language (CQL) is the primary *human-centric* way of interacting with a Cassandra database. While offering some flexibility when generating queries, it requires knowledge of Cassandra data modeling best practices. LLM function calling gives an agent the ability to reason and then choose a tool to satisfy the request. Agents using LLMs should reason using Cassandra-specific logic when choosing the appropriate toolkit or chain of toolkits. This reduces the randomness introduced when LLMs are forced to provide a top-down solution. Do you want an LLM to have complete unfettered access to your database? Yeah. Probably not. To accomplish this, we provide a prompt for use when constructing questions for the agent: \n",
|
||||
"\n",
|
||||
"```json\n",
|
||||
"`Cassandra Query Language (CQL)` is the primary *human-centric* way of interacting with a Cassandra database. While offering some flexibility when generating queries, it requires knowledge of Cassandra data modeling best practices. LLM function calling gives an agent the ability to reason and then choose a tool to satisfy the request. Agents using LLMs should reason using Cassandra-specific logic when choosing the appropriate toolkit or chain of toolkits. This reduces the randomness introduced when LLMs are forced to provide a top-down solution. Do you want an LLM to have complete unfettered access to your database? Yeah. Probably not. To accomplish this, we provide a prompt for use when constructing questions for the agent: \n",
|
||||
"\n",
|
||||
"You are an Apache Cassandra expert query analysis bot with the following features \n",
|
||||
"and rules:\n",
|
||||
" - You will take a question from the end user about finding specific \n",
|
||||
@@ -38,6 +43,7 @@
|
||||
"\n",
|
||||
"The following is an example of a query path in JSON format:\n",
|
||||
"\n",
|
||||
"```json\n",
|
||||
" {\n",
|
||||
" \"query_paths\": [\n",
|
||||
" {\n",
|
||||
@@ -448,13 +454,6 @@
|
||||
"\n",
|
||||
"print(response[\"output\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"For a deepdive on creating a Cassandra DB agent see the [CQL agent cookbook](https://github.com/langchain-ai/langchain/blob/master/cookbook/cql_agent.ipynb)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -473,7 +472,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"\n",
|
||||
"Let's add a dummy function to `action.py`.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"```python\n",
|
||||
"@action\n",
|
||||
"def get_weather_forecast(city: str, days: int, scale: str = \"celsius\") -> str:\n",
|
||||
" \"\"\"\n",
|
||||
@@ -63,7 +63,7 @@
|
||||
"\n",
|
||||
"We then start the server:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"```bash\n",
|
||||
"action-server start\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
@@ -193,7 +193,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.16"
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "93b35dd0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"sidebar_class_name: hidden\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3f34700b",
|
||||
@@ -7,6 +17,14 @@
|
||||
"source": [
|
||||
"# ChatGPT Plugins\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
":::warning Deprecated\n",
|
||||
"\n",
|
||||
"OpenAI has [deprecated plugins](https://openai.com/index/chatgpt-plugins/).\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"This example shows how to use ChatGPT Plugins within LangChain abstractions.\n",
|
||||
"\n",
|
||||
"Note 1: This currently only works for plugins with no auth.\n",
|
||||
|
||||
@@ -7,58 +7,44 @@
|
||||
"source": [
|
||||
"# DuckDuckGo Search\n",
|
||||
"\n",
|
||||
"This notebook goes over how to use the duck-duck-go search component."
|
||||
"This guide shows over how to use the DuckDuckGo search component.\n",
|
||||
"\n",
|
||||
"## Usage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"execution_count": null,
|
||||
"id": "21e46d4d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet duckduckgo-search langchain-community"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "ac4910f8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.tools import DuckDuckGoSearchRun"
|
||||
"%pip install -qU duckduckgo-search langchain-community"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "84b8f773",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search = DuckDuckGoSearchRun()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "068991a6",
|
||||
"id": "ac4910f8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"Life After the Presidency How Tall is Obama? Books and Grammy Hobbies Movies About Obama Quotes 1961-present Who Is Barack Obama? Barack Obama was the 44 th president of the United States... facts you never knew about Barack Obama is that his immediate family spread out across three continents. Barack, who led America from 2009 to 2017, comes from a large family of seven living half-siblings. His father, Barack Obama Sr., met his mother, Ann Dunham, in 1960 and married her a year after. With a tear running from his eye, President Barack Obama recalls the 20 first-graders killed in 2012 at Sandy Hook Elementary School, while speaking in the East Room of the White House in ... Former first Lady Rosalynn Carter was laid to rest at her family's home in Plains, Ga. on Nov. 29 following three days of memorials across her home state. She passed away on Nov. 19, aged 96 ... Here are 28 of President Obama's biggest accomplishments as President of the United States. 1 - Rescued the country from the Great Recession, cutting the unemployment rate from 10% to 4.7% over ...\""
|
||||
"'When Ann Dunham and Barack Obama Sr. tied the knot, they kept the news to themselves. \"Nobody was invited,\" Neil Abercrombie, a college friend of Obama Sr., told Time in 2008. The wedding came as ... As the head of the government of the United States, the president is arguably the most powerful government official in the world. The president is elected to a four-year term via an electoral college system. Since the Twenty-second Amendment was adopted in 1951, the American presidency has been Most common names of U.S. presidents 1789-2021. Published by. Aaron O\\'Neill , Jul 4, 2024. The most common first name for a U.S. president is James, followed by John and then William. Six U.S ... Obama\\'s personal charisma, stirring oratory, and his campaign promise to bring change to the established political system resonated with many Democrats, especially young and minority voters. On January 3, 2008, Obama won a surprise victory in the first major nominating contest, the Iowa caucus, over Sen. Hillary Clinton, who was the overwhelming favorite to win the nomination. Former President Barack Obama released a letter about President Biden\\'s decision to drop out of the 2024 presidential race. Notably, Obama did not name or endorse Vice President Kamala Harris.'"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"search.run(\"Obama's first name?\")"
|
||||
"from langchain_community.tools import DuckDuckGoSearchRun\n",
|
||||
"\n",
|
||||
"search = DuckDuckGoSearchRun()\n",
|
||||
"\n",
|
||||
"search.invoke(\"Obama's first name?\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -71,43 +57,27 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 3,
|
||||
"id": "95635444",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.tools import DuckDuckGoSearchResults"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "0133d103",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search = DuckDuckGoSearchResults()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "439efc06",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'[snippet: 1:12. Former President Barack Obama, in a CNN interview that aired Thursday night, said he does not believe President Joe Biden will face a serious primary challenge during his 2024 reelection ..., title: Five takeaways from Barack Obama\\'s CNN interview on Biden ... - Yahoo, link: https://www.usatoday.com/story/news/politics/2023/06/23/five-takeaways-from-barack-obama-cnn-interview/70349112007/], [snippet: Democratic institutions in the United States and around the world have grown \"creaky,\" former President Barack Obama warned in an exclusive CNN interview Thursday, and it remains incumbent on ..., title: Obama warns democratic institutions are \\'creaky\\' but Trump ... - CNN, link: https://www.cnn.com/2023/06/22/politics/barack-obama-interview-cnntv/index.html], [snippet: Barack Obama was the 44 th president of the United States and the first Black commander-in-chief. He served two terms, from 2009 until 2017. The son of parents from Kenya and Kansas, Obama was ..., title: Barack Obama: Biography, 44th U.S. President, Politician, link: https://www.biography.com/political-figures/barack-obama], [snippet: Aug. 2, 2023, 5:00 PM PDT. By Mike Memoli and Kristen Welker. WASHINGTON — During a trip to the White House in June, former President Barack Obama made it clear to his former running mate that ..., title: Obama privately told Biden he would do whatever it takes to help in 2024, link: https://www.nbcnews.com/politics/white-house/obama-privately-told-biden-whatever-takes-help-2024-rcna97865], [snippet: Natalie Bookey-Baker, a vice president at the Obama Foundation who worked for then-first lady Michelle Obama in the White House, said about 2,500 alumni are expected. They are veterans of Obama ..., title: Barack Obama team reunion this week in Chicago; 2,500 alumni expected ..., link: https://chicago.suntimes.com/politics/2023/10/29/23937504/barack-obama-michelle-obama-david-axelrod-pod-save-america-jon-batiste-jen-psaki-reunion-obamaworld]'"
|
||||
"\"[snippet: Why Obama Hasn't Endorsed Harris. The former president has positioned himself as an impartial elder statesman above intraparty machinations and was neutral during the 2020 Democratic primaries., title: Why Obama Hasn't Endorsed Harris - The New York Times, link: https://www.nytimes.com/2024/07/21/us/politics/why-obama-hasnt-endorsed-harris.html], [snippet: Former President Barack Obama released a letter about President Biden's decision to drop out of the 2024 presidential race. Notably, Obama did not name or endorse Vice President Kamala Harris., title: Read Obama's full statement on Biden dropping out - CBS News, link: https://www.cbsnews.com/news/barack-obama-biden-dropping-out-2024-presidential-race-full-statement/], [snippet: USA TODAY. 0:04. 0:48. Former President Barack Obama recently said, in private, that President Joe Biden's chances at a successful presidential run in 2024 have declined and that he needs to ..., title: Did Obama tell Biden to quit? What the former president said - USA TODAY, link: https://www.usatoday.com/story/news/politics/elections/2024/07/18/did-obama-tell-biden-to-quit-what-the-former-president-said/74458139007/], [snippet: Many of the marquee names in Democratic politics began quickly lining up behind Vice President Kamala Harris on Sunday, but one towering presence in the party held back: Barack Obama. The former president has not yet endorsed Harris; in fact, he did not mention her once in an affectionate — if tautly written — tribute to President Joe Biden that was posted on Medium shortly after Biden ..., title: Why Obama Hasn't Endorsed Harris - Yahoo News, link: https://news.yahoo.com/news/why-obama-hasn-t-endorsed-114259092.html]\""
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"search.run(\"Obama\")"
|
||||
"from langchain_community.tools import DuckDuckGoSearchResults\n",
|
||||
"\n",
|
||||
"search = DuckDuckGoSearchResults()\n",
|
||||
"\n",
|
||||
"search.invoke(\"Obama\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -115,38 +85,30 @@
|
||||
"id": "e17ccfe7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can also just search for news articles. Use the keyword ``backend=\"news\"``"
|
||||
"You can also just search for news articles. Use the keyword `backend=\"news\"`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 4,
|
||||
"id": "21afe28d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search = DuckDuckGoSearchResults(backend=\"news\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "2a4beeb9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'[snippet: 1:12. Former President Barack Obama, in a CNN interview that aired Thursday night, said he does not believe President Joe Biden will face a serious primary challenge during his 2024 reelection ..., title: Five takeaways from Barack Obama\\'s CNN interview on Biden ... - Yahoo, link: https://www.usatoday.com/story/news/politics/2023/06/23/five-takeaways-from-barack-obama-cnn-interview/70349112007/], [snippet: Democratic institutions in the United States and around the world have grown \"creaky,\" former President Barack Obama warned in an exclusive CNN interview Thursday, and it remains incumbent on ..., title: Obama warns democratic institutions are \\'creaky\\' but Trump ... - CNN, link: https://www.cnn.com/2023/06/22/politics/barack-obama-interview-cnntv/index.html], [snippet: Barack Obama was the 44 th president of the United States and the first Black commander-in-chief. He served two terms, from 2009 until 2017. The son of parents from Kenya and Kansas, Obama was ..., title: Barack Obama: Biography, 44th U.S. President, Politician, link: https://www.biography.com/political-figures/barack-obama], [snippet: Natalie Bookey-Baker, a vice president at the Obama Foundation who worked for then-first lady Michelle Obama in the White House, said about 2,500 alumni are expected. They are veterans of Obama ..., title: Barack Obama team reunion this week in Chicago; 2,500 alumni expected ..., link: https://chicago.suntimes.com/politics/2023/10/29/23937504/barack-obama-michelle-obama-david-axelrod-pod-save-america-jon-batiste-jen-psaki-reunion-obamaworld], [snippet: Aug. 2, 2023, 5:00 PM PDT. By Mike Memoli and Kristen Welker. WASHINGTON — During a trip to the White House in June, former President Barack Obama made it clear to his former running mate that ..., title: Obama privately told Biden he would do whatever it takes to help in 2024, link: https://www.nbcnews.com/politics/white-house/obama-privately-told-biden-whatever-takes-help-2024-rcna97865]'"
|
||||
"'[snippet: Users on X have been widely comparing the boost of support felt for Kamala Harris\\' campaign to Barack Obama\\'s in 2008., title: Surging Support For Kamala Harris Compared To Obama-Era Energy, link: https://www.msn.com/en-us/news/politics/surging-support-for-kamala-harris-compared-to-obama-era-energy/ar-BB1qzdC0, date: 2024-07-24T18:27:01+00:00, source: Newsweek on MSN.com], [snippet: Harris tried to emulate Obama\\'s coalition in 2020 and failed. She may have a better shot at reaching young, Black, and Latino voters this time around., title: Harris May Follow Obama\\'s Path to the White House After All, link: https://www.msn.com/en-us/news/politics/harris-may-follow-obama-s-path-to-the-white-house-after-all/ar-BB1qv9d4, date: 2024-07-23T22:42:00+00:00, source: Intelligencer on MSN.com], [snippet: The Republican presidential candidate said in an interview on Fox News that he \"wouldn\\'t be worried\" about Michelle Obama running., title: Donald Trump Responds to Michelle Obama Threat, link: https://www.msn.com/en-us/news/politics/donald-trump-responds-to-michelle-obama-threat/ar-BB1qqtu5, date: 2024-07-22T18:26:00+00:00, source: Newsweek on MSN.com], [snippet: H eading into the weekend at his vacation home in Rehoboth Beach, Del., President Biden was reportedly stewing over Barack Obama\\'s role in the orchestrated campaign to force him, title: Opinion | Barack Obama Strikes Again, link: https://www.msn.com/en-us/news/politics/opinion-barack-obama-strikes-again/ar-BB1qrfiy, date: 2024-07-22T21:28:00+00:00, source: The Wall Street Journal on MSN.com]'"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"search.run(\"Obama\")"
|
||||
"search = DuckDuckGoSearchResults(backend=\"news\")\n",
|
||||
"\n",
|
||||
"search.invoke(\"Obama\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -154,59 +116,45 @@
|
||||
"id": "5f7c0129",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can also directly pass a custom ``DuckDuckGoSearchAPIWrapper`` to ``DuckDuckGoSearchResults``. Therefore, you have much more control over the search results."
|
||||
"You can also directly pass a custom `DuckDuckGoSearchAPIWrapper` to `DuckDuckGoSearchResults` to provide more control over the search results."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 5,
|
||||
"id": "c7ab3b55",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.utilities import DuckDuckGoSearchAPIWrapper\n",
|
||||
"\n",
|
||||
"wrapper = DuckDuckGoSearchAPIWrapper(region=\"de-de\", time=\"d\", max_results=2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "adce16e1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search = DuckDuckGoSearchResults(api_wrapper=wrapper, source=\"news\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "b7e77c54",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'[snippet: When Obama left office in January 2017, a CNN poll showed him with a 60% approval rating, landing him near the top of the list of presidential approval ratings upon leaving office., title: Opinion: The real reason Trump is attacking Obamacare | CNN, link: https://www.cnn.com/2023/12/04/opinions/trump-obamacare-obama-repeal-health-care-obeidallah/index.html], [snippet: Buchempfehlung von Barack Obama. Der gut zweistündige Netflix-Film basiert auf dem gleichnamigen Roman \"Leave the World Behind\" des hochgelobten US-Autors Rumaan Alam. 2020 landete er damit unter den Finalisten des \"National Book Awards\". In Deutschland ist das Buch, das auch Barack Obama auf seiner einflussreichen Lese-Empfehlungsliste hatte ..., title: Neu bei Netflix \"Leave The World Behind\": Kritik zum ... - Prisma, link: https://www.prisma.de/news/filme/Neu-bei-Netflix-Leave-The-World-Behind-Kritik-zum-ungewoehnlichen-Endzeit-Film-mit-Julia-Roberts,46563944]'"
|
||||
"'[snippet: So war das zum Beispiel bei Barack Obama, als er sich für Joe Biden als Kandidat für die Vizepräsidentschaft entschied. USA-Expertin Rachel Tausendfreund erklärt, wie sich Kamala Harris als ..., title: Interview: „Kamala Harris sieht die Welt eher wie Barack Obama - nicht ..., link: https://www.handelsblatt.com/politik/interview-kamala-harris-sieht-die-welt-eher-wie-barack-obama-nicht-wie-joe-biden/100054923.html], [snippet: Disput um Klimapolitik Der seltsame Moment, als Kamala Harris sogar Obama vor Gericht zerrte. Teilen. 0. Chip Somodevilla/Getty Images US-Vizepräsidentin Kamala Harris (links) und Ex-Präsident ..., title: Der seltsame Moment, als Kamala Harris sogar Obama vor Gericht zerrte, link: https://www.focus.de/earth/analyse/disput-um-klimapolitik-der-seltsame-moment-als-kamala-harris-sogar-obama-vor-gericht-zerrte_id_260165157.html], [snippet: Kamala Harris «Auf den Spuren von Obama»: Harris\\' erste Rede überzeugt Experten. Kamala Harris hat ihre erste Rede als Präsidentschaftskandidatin gehalten. Zwei Experten sind sich einig: Sie ..., title: Kamala Harris\\' erste Wahlkampfrede überzeugt Experten, link: https://www.20min.ch/story/kamala-harris-auf-den-spuren-von-obama-harris-erste-rede-ueberzeugt-experten-103154550], [snippet: Harris hat ihre erste Rede als Präsidentschaftskandidatin gehalten. Experten sind sich einig: Sie hat das Potenzial, ein Feuer zu entfachen wie Obama., title: \"Auf Spuren von Obama\": Harris-Rede überzeugt Experten, link: https://www.heute.at/s/auf-spuren-von-obama-harris-rede-ueberzeugt-experten-120049557]'"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"search.run(\"Obama\")"
|
||||
"from langchain_community.utilities import DuckDuckGoSearchAPIWrapper\n",
|
||||
"\n",
|
||||
"wrapper = DuckDuckGoSearchAPIWrapper(region=\"de-de\", time=\"d\", max_results=2)\n",
|
||||
"\n",
|
||||
"search = DuckDuckGoSearchResults(api_wrapper=wrapper, source=\"news\")\n",
|
||||
"\n",
|
||||
"search.invoke(\"Obama\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b133e3c1",
|
||||
"cell_type": "markdown",
|
||||
"id": "f3df6b8b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": [
|
||||
"## Related\n",
|
||||
"\n",
|
||||
"- [How to use a chat model to call tools](https://python.langchain.com/v0.2/docs/how_to/tool_calling/)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -225,7 +173,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
"version": "3.10.5"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
||||
@@ -45,21 +45,10 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": null,
|
||||
"id": "377bc723",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Document(page_content='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\\nTonight, 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\\nOne of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \\n\\nAnd 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.', metadata={'source': 'state_of_the_union.txt'})"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores import ScaNN\n",
|
||||
@@ -95,15 +84,15 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": null,
|
||||
"id": "fc27ad51",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains import RetrievalQA\n",
|
||||
"from langchain_community.chat_models import google_palm\n",
|
||||
"from langchain_community.chat_models.google_palm import ChatGooglePalm\n",
|
||||
"\n",
|
||||
"palm_client = google_palm.ChatGooglePalm(google_api_key=\"YOUR_GOOGLE_PALM_API_KEY\")\n",
|
||||
"palm_client = ChatGooglePalm(google_api_key=\"YOUR_GOOGLE_PALM_API_KEY\")\n",
|
||||
"\n",
|
||||
"qa = RetrievalQA.from_chain_type(\n",
|
||||
" llm=palm_client,\n",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"source": [
|
||||
"# TiDB Vector\n",
|
||||
"\n",
|
||||
"> [TiDB Cloud](https://tidbcloud.com/), is a comprehensive Database-as-a-Service (DBaaS) solution, that provides dedicated and serverless options. TiDB Serverless is now integrating a built-in vector search into the MySQL landscape. With this enhancement, you can seamlessly develop AI applications using TiDB Serverless without the need for a new database or additional technical stacks. Be among the first to experience it by joining the waitlist for the private beta at https://tidb.cloud/ai.\n",
|
||||
"> [TiDB Cloud](https://www.pingcap.com/tidb-serverless), is a comprehensive Database-as-a-Service (DBaaS) solution, that provides dedicated and serverless options. TiDB Serverless is now integrating a built-in vector search into the MySQL landscape. With this enhancement, you can seamlessly develop AI applications using TiDB Serverless without the need for a new database or additional technical stacks. Create a free TiDB Serverless cluster and start using the vector search feature at https://pingcap.com/ai.\n",
|
||||
"\n",
|
||||
"This notebook provides a detailed guide on utilizing the TiDB Vector functionality, showcasing its features and practical applications."
|
||||
]
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"VDMS supports:\n",
|
||||
"* K nearest neighbor search\n",
|
||||
"* Euclidean distance (L2) and inner product (IP)\n",
|
||||
"* Libraries for indexing and computing distances: TileDBDense, TileDBSparse, FaissFlat (Default), FaissIVFFlat\n",
|
||||
"* Libraries for indexing and computing distances: TileDBDense, TileDBSparse, FaissFlat (Default), FaissIVFFlat, Flinng\n",
|
||||
"* Embeddings for text, images, and video\n",
|
||||
"* Vector and metadata searches\n",
|
||||
"\n",
|
||||
"VDMS has server and client components. To setup the server, see the [installation instructions](https://github.com/IntelLabs/vdms/blob/master/INSTALL.md) or use the [docker image](https://hub.docker.com/r/intellabs/vdms).\n",
|
||||
@@ -40,7 +41,7 @@
|
||||
],
|
||||
"source": [
|
||||
"# Pip install necessary package\n",
|
||||
"%pip install --upgrade --quiet pip sentence-transformers vdms \"unstructured-inference==0.6.6\";"
|
||||
"%pip install --upgrade --quiet pip vdms sentence-transformers langchain-huggingface > /dev/null"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -62,7 +63,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"e6061b270eef87de5319a6c5af709b36badcad8118069a8f6b577d2e01ad5e2d\n"
|
||||
"b26917ffac236673ef1d035ab9c91fe999e29c9eb24aa6c7103d7baa6bf2f72d\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -92,6 +93,9 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import time\n",
|
||||
"import warnings\n",
|
||||
"\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"from langchain_community.document_loaders.text import TextLoader\n",
|
||||
"from langchain_community.vectorstores import VDMS\n",
|
||||
@@ -290,7 +294,7 @@
|
||||
"source": [
|
||||
"# add data\n",
|
||||
"collection_name = \"my_collection_faiss_L2\"\n",
|
||||
"db = VDMS.from_documents(\n",
|
||||
"db_FaissFlat = VDMS.from_documents(\n",
|
||||
" docs,\n",
|
||||
" client=vdms_client,\n",
|
||||
" ids=ids,\n",
|
||||
@@ -301,7 +305,7 @@
|
||||
"# Query (No metadata filtering)\n",
|
||||
"k = 3\n",
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"returned_docs = db.similarity_search(query, k=k, filter=None)\n",
|
||||
"returned_docs = db_FaissFlat.similarity_search(query, k=k, filter=None)\n",
|
||||
"print_results(returned_docs, score=False)"
|
||||
]
|
||||
},
|
||||
@@ -392,25 +396,24 @@
|
||||
"k = 3\n",
|
||||
"constraints = {\"page_number\": [\">\", 30], \"president_included\": [\"==\", True]}\n",
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"returned_docs = db.similarity_search(query, k=k, filter=constraints)\n",
|
||||
"returned_docs = db_FaissFlat.similarity_search(query, k=k, filter=constraints)\n",
|
||||
"print_results(returned_docs, score=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a5984766",
|
||||
"id": "92ab3370",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Similarity Search using TileDBDense and Euclidean Distance\n",
|
||||
"### Similarity Search using Faiss IVFFlat and Inner Product (IP) Distance\n",
|
||||
"\n",
|
||||
"In this section, we add the documents to VDMS using TileDB Dense indexing and L2 as the distance metric for similarity search. We search for three documents (`k=3`) related to the query `What did the president say about Ketanji Brown Jackson` and also return the score along with the document.\n",
|
||||
"\n"
|
||||
"In this section, we add the documents to VDMS using Faiss IndexIVFFlat indexing and IP as the distance metric for similarity search. We search for three documents (`k=3`) related to the query `What did the president say about Ketanji Brown Jackson` and also return the score along with the document.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "3001ba6e",
|
||||
"id": "78f502cf",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -419,7 +422,7 @@
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.2032090425491333\n",
|
||||
"Score:\t1.2032090425\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tTonight. 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",
|
||||
@@ -437,7 +440,7 @@
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.495247483253479\n",
|
||||
"Score:\t1.4952471256\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tAs Frances Haugen, who is here with us tonight, has shown, we must hold social media platforms accountable for the national experiment they’re conducting on our children for profit. \n",
|
||||
@@ -463,7 +466,224 @@
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.5008409023284912\n",
|
||||
"Score:\t1.5008399487\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tA 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",
|
||||
"Metadata:\n",
|
||||
"\tid:\t33\n",
|
||||
"\tpage_number:\t33\n",
|
||||
"\tpresident_included:\tFalse\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"db_FaissIVFFlat = VDMS.from_documents(\n",
|
||||
" docs,\n",
|
||||
" client=vdms_client,\n",
|
||||
" ids=ids,\n",
|
||||
" collection_name=\"my_collection_FaissIVFFlat_IP\",\n",
|
||||
" embedding=embedding,\n",
|
||||
" engine=\"FaissIVFFlat\",\n",
|
||||
" distance_strategy=\"IP\",\n",
|
||||
")\n",
|
||||
"# Query\n",
|
||||
"k = 3\n",
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"docs_with_score = db_FaissIVFFlat.similarity_search_with_score(query, k=k, filter=None)\n",
|
||||
"print_results(docs_with_score)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e66d9125",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Similarity Search using FLINNG and IP Distance\n",
|
||||
"\n",
|
||||
"In this section, we add the documents to VDMS using Filters to Identify Near-Neighbor Groups (FLINNG) indexing and IP as the distance metric for similarity search. We search for three documents (`k=3`) related to the query `What did the president say about Ketanji Brown Jackson` and also return the score along with the document."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "add81beb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.2032090425\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tTonight. 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",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.4952471256\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tAs Frances Haugen, who is here with us tonight, has shown, we must hold social media platforms accountable for the national experiment they’re conducting on our children for profit. \n",
|
||||
"\n",
|
||||
"It’s time to strengthen privacy protections, ban targeted advertising to children, demand tech companies stop collecting personal data on our children. \n",
|
||||
"\n",
|
||||
"And let’s get all Americans the mental health services they need. More people they can turn to for help, and full parity between physical and mental health care. \n",
|
||||
"\n",
|
||||
"Third, support our veterans. \n",
|
||||
"\n",
|
||||
"Veterans are the best of us. \n",
|
||||
"\n",
|
||||
"I’ve always believed that we have a sacred obligation to equip all those we send to war and care for them and their families when they come home. \n",
|
||||
"\n",
|
||||
"My administration is providing assistance with job training and housing, and now helping lower-income veterans get VA care debt-free. \n",
|
||||
"\n",
|
||||
"Our troops in Iraq and Afghanistan faced many dangers.\n",
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t37\n",
|
||||
"\tpage_number:\t37\n",
|
||||
"\tpresident_included:\tFalse\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.5008399487\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tA 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",
|
||||
"Metadata:\n",
|
||||
"\tid:\t33\n",
|
||||
"\tpage_number:\t33\n",
|
||||
"\tpresident_included:\tFalse\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"db_Flinng = VDMS.from_documents(\n",
|
||||
" docs,\n",
|
||||
" client=vdms_client,\n",
|
||||
" ids=ids,\n",
|
||||
" collection_name=\"my_collection_Flinng_IP\",\n",
|
||||
" embedding=embedding,\n",
|
||||
" engine=\"Flinng\",\n",
|
||||
" distance_strategy=\"IP\",\n",
|
||||
")\n",
|
||||
"# Query\n",
|
||||
"k = 3\n",
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"docs_with_score = db_Flinng.similarity_search_with_score(query, k=k, filter=None)\n",
|
||||
"print_results(docs_with_score)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a5984766",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Similarity Search using TileDBDense and Euclidean Distance\n",
|
||||
"\n",
|
||||
"In this section, we add the documents to VDMS using TileDB Dense indexing and L2 as the distance metric for similarity search. We search for three documents (`k=3`) related to the query `What did the president say about Ketanji Brown Jackson` and also return the score along with the document.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "3001ba6e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.2032090425\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tTonight. 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",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.4952471256\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tAs Frances Haugen, who is here with us tonight, has shown, we must hold social media platforms accountable for the national experiment they’re conducting on our children for profit. \n",
|
||||
"\n",
|
||||
"It’s time to strengthen privacy protections, ban targeted advertising to children, demand tech companies stop collecting personal data on our children. \n",
|
||||
"\n",
|
||||
"And let’s get all Americans the mental health services they need. More people they can turn to for help, and full parity between physical and mental health care. \n",
|
||||
"\n",
|
||||
"Third, support our veterans. \n",
|
||||
"\n",
|
||||
"Veterans are the best of us. \n",
|
||||
"\n",
|
||||
"I’ve always believed that we have a sacred obligation to equip all those we send to war and care for them and their families when they come home. \n",
|
||||
"\n",
|
||||
"My administration is providing assistance with job training and housing, and now helping lower-income veterans get VA care debt-free. \n",
|
||||
"\n",
|
||||
"Our troops in Iraq and Afghanistan faced many dangers.\n",
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t37\n",
|
||||
"\tpage_number:\t37\n",
|
||||
"\tpresident_included:\tFalse\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.5008399487\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tA 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",
|
||||
@@ -505,114 +725,6 @@
|
||||
"print_results(docs_with_score)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "92ab3370",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Similarity Search using Faiss IVFFlat and Euclidean Distance\n",
|
||||
"\n",
|
||||
"In this section, we add the documents to VDMS using Faiss IndexIVFFlat indexing and L2 as the distance metric for similarity search. We search for three documents (`k=3`) related to the query `What did the president say about Ketanji Brown Jackson` and also return the score along with the document.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "78f502cf",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.2032090425491333\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tTonight. 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",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.495247483253479\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tAs Frances Haugen, who is here with us tonight, has shown, we must hold social media platforms accountable for the national experiment they’re conducting on our children for profit. \n",
|
||||
"\n",
|
||||
"It’s time to strengthen privacy protections, ban targeted advertising to children, demand tech companies stop collecting personal data on our children. \n",
|
||||
"\n",
|
||||
"And let’s get all Americans the mental health services they need. More people they can turn to for help, and full parity between physical and mental health care. \n",
|
||||
"\n",
|
||||
"Third, support our veterans. \n",
|
||||
"\n",
|
||||
"Veterans are the best of us. \n",
|
||||
"\n",
|
||||
"I’ve always believed that we have a sacred obligation to equip all those we send to war and care for them and their families when they come home. \n",
|
||||
"\n",
|
||||
"My administration is providing assistance with job training and housing, and now helping lower-income veterans get VA care debt-free. \n",
|
||||
"\n",
|
||||
"Our troops in Iraq and Afghanistan faced many dangers.\n",
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t37\n",
|
||||
"\tpage_number:\t37\n",
|
||||
"\tpresident_included:\tFalse\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.5008409023284912\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tA 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",
|
||||
"Metadata:\n",
|
||||
"\tid:\t33\n",
|
||||
"\tpage_number:\t33\n",
|
||||
"\tpresident_included:\tFalse\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"db_FaissIVFFlat = VDMS.from_documents(\n",
|
||||
" docs,\n",
|
||||
" client=vdms_client,\n",
|
||||
" ids=ids,\n",
|
||||
" collection_name=\"my_collection_FaissIVFFlat_L2\",\n",
|
||||
" embedding=embedding,\n",
|
||||
" engine=\"FaissIVFFlat\",\n",
|
||||
" distance_strategy=\"L2\",\n",
|
||||
")\n",
|
||||
"# Query\n",
|
||||
"k = 3\n",
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"docs_with_score = db_FaissIVFFlat.similarity_search_with_score(query, k=k, filter=None)\n",
|
||||
"print_results(docs_with_score)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9ed3ec50",
|
||||
@@ -622,12 +734,12 @@
|
||||
"\n",
|
||||
"While building toward a real application, you want to go beyond adding data, and also update and delete data.\n",
|
||||
"\n",
|
||||
"Here is a basic example showing how to do so. First, we will update the metadata for the document most relevant to the query."
|
||||
"Here is a basic example showing how to do so. First, we will update the metadata for the document most relevant to the query by adding a date. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 11,
|
||||
"id": "81a02810",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -638,7 +750,7 @@
|
||||
"Original metadata: \n",
|
||||
"\t{'id': '32', 'page_number': 32, 'president_included': True, 'source': '../../how_to/state_of_the_union.txt'}\n",
|
||||
"new metadata: \n",
|
||||
"\t{'id': '32', 'page_number': 32, 'president_included': True, 'source': '../../how_to/state_of_the_union.txt', 'new_value': 'hello world'}\n",
|
||||
"\t{'id': '32', 'page_number': 32, 'president_included': True, 'source': '../../how_to/state_of_the_union.txt', 'last_date_read': {'_date': '2024-05-01T14:30:00'}}\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"UPDATED ENTRY (id=32):\n",
|
||||
@@ -655,8 +767,8 @@
|
||||
"id:\n",
|
||||
"\t32\n",
|
||||
"\n",
|
||||
"new_value:\n",
|
||||
"\thello world\n",
|
||||
"last_date_read:\n",
|
||||
"\t2024-05-01T14:30:00+00:00\n",
|
||||
"\n",
|
||||
"page_number:\n",
|
||||
"\t32\n",
|
||||
@@ -672,19 +784,26 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"doc = db.similarity_search(query)[0]\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"doc = db_FaissFlat.similarity_search(query)[0]\n",
|
||||
"print(f\"Original metadata: \\n\\t{doc.metadata}\")\n",
|
||||
"\n",
|
||||
"# update the metadata for a document\n",
|
||||
"doc.metadata[\"new_value\"] = \"hello world\"\n",
|
||||
"# Update the metadata for a document by adding last datetime document read\n",
|
||||
"datetime_str = datetime(2024, 5, 1, 14, 30, 0).isoformat()\n",
|
||||
"doc.metadata[\"last_date_read\"] = {\"_date\": datetime_str}\n",
|
||||
"print(f\"new metadata: \\n\\t{doc.metadata}\")\n",
|
||||
"print(f\"{DELIMITER}\\n\")\n",
|
||||
"\n",
|
||||
"# Update document in VDMS\n",
|
||||
"id_to_update = doc.metadata[\"id\"]\n",
|
||||
"db.update_document(collection_name, id_to_update, doc)\n",
|
||||
"response, response_array = db.get(\n",
|
||||
" collection_name, constraints={\"id\": [\"==\", id_to_update]}\n",
|
||||
"db_FaissFlat.update_document(collection_name, id_to_update, doc)\n",
|
||||
"response, response_array = db_FaissFlat.get(\n",
|
||||
" collection_name,\n",
|
||||
" constraints={\n",
|
||||
" \"id\": [\"==\", id_to_update],\n",
|
||||
" \"last_date_read\": [\">=\", {\"_date\": \"2024-05-01T00:00:00\"}],\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Display Results\n",
|
||||
@@ -702,7 +821,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 12,
|
||||
"id": "95537fe8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -716,11 +835,13 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Documents before deletion: \", db.count(collection_name))\n",
|
||||
"print(\"Documents before deletion: \", db_FaissFlat.count(collection_name))\n",
|
||||
"\n",
|
||||
"id_to_remove = ids[-1]\n",
|
||||
"db.delete(collection_name=collection_name, ids=[id_to_remove])\n",
|
||||
"print(f\"Documents after deletion (id={id_to_remove}): {db.count(collection_name)}\")"
|
||||
"db_FaissFlat.delete(collection_name=collection_name, ids=[id_to_remove])\n",
|
||||
"print(\n",
|
||||
" f\"Documents after deletion (id={id_to_remove}): {db_FaissFlat.count(collection_name)}\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -739,7 +860,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 13,
|
||||
"id": "1db4d6ed",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -758,7 +879,7 @@
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tnew_value:\thello world\n",
|
||||
"\tlast_date_read:\t2024-05-01T14:30:00+00:00\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n"
|
||||
@@ -767,7 +888,7 @@
|
||||
],
|
||||
"source": [
|
||||
"embedding_vector = embedding.embed_query(query)\n",
|
||||
"returned_docs = db.similarity_search_by_vector(embedding_vector)\n",
|
||||
"returned_docs = db_FaissFlat.similarity_search_by_vector(embedding_vector)\n",
|
||||
"\n",
|
||||
"# Print Results\n",
|
||||
"print_document_details(returned_docs[0])"
|
||||
@@ -787,7 +908,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 14,
|
||||
"id": "2bc0313b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -795,7 +916,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Returned entry:\n",
|
||||
"Deleted entry:\n",
|
||||
"\n",
|
||||
"blob:\n",
|
||||
"\tTrue\n",
|
||||
@@ -838,18 +959,18 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response, response_array = db.get(\n",
|
||||
"response, response_array = db_FaissFlat.get(\n",
|
||||
" collection_name,\n",
|
||||
" limit=1,\n",
|
||||
" include=[\"metadata\", \"embeddings\"],\n",
|
||||
" constraints={\"id\": [\"==\", \"2\"]},\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Returned entry:\")\n",
|
||||
"print_response([response[0][\"FindDescriptor\"][\"entities\"][0]])\n",
|
||||
"\n",
|
||||
"# Delete id=2\n",
|
||||
"db.delete(collection_name=collection_name, ids=[\"2\"])"
|
||||
"db_FaissFlat.delete(collection_name=collection_name, ids=[\"2\"])\n",
|
||||
"\n",
|
||||
"print(\"Deleted entry:\")\n",
|
||||
"print_response([response[0][\"FindDescriptor\"][\"entities\"][0]])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -869,7 +990,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 15,
|
||||
"id": "120f55eb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -888,7 +1009,7 @@
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tnew_value:\thello world\n",
|
||||
"\tlast_date_read:\t2024-05-01T14:30:00+00:00\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n"
|
||||
@@ -896,7 +1017,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"retriever = db.as_retriever()\n",
|
||||
"retriever = db_FaissFlat.as_retriever()\n",
|
||||
"relevant_docs = retriever.invoke(query)[0]\n",
|
||||
"\n",
|
||||
"print_document_details(relevant_docs)"
|
||||
@@ -914,7 +1035,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 16,
|
||||
"id": "f00be6d0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -933,7 +1054,7 @@
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tnew_value:\thello world\n",
|
||||
"\tlast_date_read:\t2024-05-01T14:30:00+00:00\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n"
|
||||
@@ -941,7 +1062,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"retriever = db.as_retriever(search_type=\"mmr\")\n",
|
||||
"retriever = db_FaissFlat.as_retriever(search_type=\"mmr\")\n",
|
||||
"relevant_docs = retriever.invoke(query)[0]\n",
|
||||
"\n",
|
||||
"print_document_details(relevant_docs)"
|
||||
@@ -957,7 +1078,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 17,
|
||||
"id": "ab911470",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -967,7 +1088,7 @@
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.2032092809677124\n",
|
||||
"Score:\t1.2032091618\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tTonight. 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",
|
||||
@@ -980,13 +1101,13 @@
|
||||
"\n",
|
||||
"Metadata:\n",
|
||||
"\tid:\t32\n",
|
||||
"\tnew_value:\thello world\n",
|
||||
"\tlast_date_read:\t2024-05-01T14:30:00+00:00\n",
|
||||
"\tpage_number:\t32\n",
|
||||
"\tpresident_included:\tTrue\n",
|
||||
"\tsource:\t../../how_to/state_of_the_union.txt\n",
|
||||
"--------------------------------------------------\n",
|
||||
"\n",
|
||||
"Score:\t1.507053256034851\n",
|
||||
"Score:\t1.50705266\n",
|
||||
"\n",
|
||||
"Content:\n",
|
||||
"\tBut cancer from prolonged exposure to burn pits ravaged Heath’s lungs and body. \n",
|
||||
@@ -1022,7 +1143,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"mmr_resp = db.max_marginal_relevance_search_with_score(query, k=2, fetch_k=10)\n",
|
||||
"mmr_resp = db_FaissFlat.max_marginal_relevance_search_with_score(query, k=2, fetch_k=10)\n",
|
||||
"print_results(mmr_resp)"
|
||||
]
|
||||
},
|
||||
@@ -1037,7 +1158,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 18,
|
||||
"id": "874e7af9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -1051,11 +1172,11 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Documents before deletion: \", db.count(collection_name))\n",
|
||||
"print(\"Documents before deletion: \", db_FaissFlat.count(collection_name))\n",
|
||||
"\n",
|
||||
"db.delete(collection_name=collection_name)\n",
|
||||
"db_FaissFlat.delete(collection_name=collection_name)\n",
|
||||
"\n",
|
||||
"print(\"Documents after deletion: \", db.count(collection_name))"
|
||||
"print(\"Documents after deletion: \", db_FaissFlat.count(collection_name))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1068,7 +1189,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 19,
|
||||
"id": "08931796",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -1097,7 +1218,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0386ea81",
|
||||
"id": "a60725a6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
@@ -1119,7 +1240,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -87,6 +87,14 @@ CHAT_MODEL_FEAT_TABLE = {
|
||||
"package": "langchain-huggingface",
|
||||
"link": "/docs/integrations/chat/huggingface/",
|
||||
},
|
||||
"ChatNVIDIA": {
|
||||
"tool_calling": True,
|
||||
"json_mode": False,
|
||||
"local": True,
|
||||
"multimodal": False,
|
||||
"package": "langchain-nvidia-ai-endpoints",
|
||||
"link": "/docs/integrations/chat/nvidia_ai_endpoints/",
|
||||
},
|
||||
"ChatOllama": {
|
||||
"tool_calling": True,
|
||||
"local": True,
|
||||
|
||||
181
docs/scripts/tool_feat_table.py
Normal file
181
docs/scripts/tool_feat_table.py
Normal file
@@ -0,0 +1,181 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SEARCH_TOOL_FEAT_TABLE = {
|
||||
"Exa Search": {
|
||||
"pricing": "1000 free searches/month",
|
||||
"available_data": "URL, Author, Title, Published Date",
|
||||
"link": "/docs/integrations/tools/exa_search",
|
||||
},
|
||||
"Bing Search": {
|
||||
"pricing": "Paid",
|
||||
"available_data": "URL, Snippet, Title",
|
||||
"link": "/docs/integrations/tools/bing_search",
|
||||
},
|
||||
"DuckDuckgoSearch": {
|
||||
"pricing": "Free",
|
||||
"available_data": "URL, Snippet, Title",
|
||||
"link": "/docs/integrations/tools/ddg",
|
||||
},
|
||||
"Brave Search": {
|
||||
"pricing": "Free",
|
||||
"available_data": "URL, Snippet, Title",
|
||||
"link": "/docs/integrations/tools/brave_search",
|
||||
},
|
||||
"Google Search": {
|
||||
"pricing": "Paid",
|
||||
"available_data": "URL, Snippet, Title",
|
||||
"link": "/docs/integrations/tools/google_search",
|
||||
},
|
||||
"Google Serper": {
|
||||
"pricing": "Free",
|
||||
"available_data": "URL, Snippet, Title, Search Rank, Site Links",
|
||||
"link": "/docs/integrations/tools/google_serper",
|
||||
},
|
||||
"Mojeek Search": {
|
||||
"pricing": "Paid",
|
||||
"available_data": "URL, Snippet, Title",
|
||||
"link": "/docs/integrations/tools/mojeek_search",
|
||||
},
|
||||
"SearxNG Search": {
|
||||
"pricing": "Free",
|
||||
"available_data": "URL, Snippet, Title, Category",
|
||||
"link": "/docs/integrations/tools/searx_search",
|
||||
},
|
||||
"You.com Search": {
|
||||
"pricing": "Free for 60 days",
|
||||
"available_data": "URL, Title, Page Content",
|
||||
"link": "/docs/integrations/tools/you",
|
||||
},
|
||||
"SearchApi": {
|
||||
"pricing": "100 Free Searches on Sign Up",
|
||||
"available_data": "URL, Snippet, Title, Search Rank, Site Links, Authors",
|
||||
"link": "/docs/integrations/tools/searchapi",
|
||||
},
|
||||
"SerpAPI": {
|
||||
"pricing": "100 Free Searches/Month",
|
||||
"available_data": "Answer",
|
||||
"link": "/docs/integrations/tools/serpapi",
|
||||
},
|
||||
}
|
||||
|
||||
CODE_INTERPRETER_TOOL_FEAT_TABLE = {
|
||||
"Bearly Code Interpreter": {
|
||||
"langauges": "Python",
|
||||
"sandbox_lifetime": "Resets on Execution",
|
||||
"upload": True,
|
||||
"return_results": "Text",
|
||||
"link": "/docs/integrations/tools/bearly",
|
||||
},
|
||||
"Riza Code Interpreter": {
|
||||
"langauges": "Python, JavaScript, PHP, Ruby",
|
||||
"sandbox_lifetime": "Resets on Execution",
|
||||
"upload": False,
|
||||
"return_results": "Text",
|
||||
"link": "/docs/integrations/tools/riza",
|
||||
},
|
||||
"E2B Data Analysis": {
|
||||
"langauges": "Python. In beta: JavaScript, R, Java",
|
||||
"sandbox_lifetime": "24 Hours",
|
||||
"upload": True,
|
||||
"return_results": "Text, Images, Videos",
|
||||
"link": "/docs/integrations/tools/e2b_data_analysis",
|
||||
},
|
||||
"Azure Container Apps dynamic sessions": {
|
||||
"langauges": "Python",
|
||||
"sandbox_lifetime": "1 Hour",
|
||||
"upload": True,
|
||||
"return_results": "Text, Images",
|
||||
"link": "/docs/integrations/tools/azure_dynamic_sessions",
|
||||
},
|
||||
}
|
||||
|
||||
TOOLS_TEMPLATE = """\
|
||||
---
|
||||
sidebar_position: 0
|
||||
sidebar_class_name: hidden
|
||||
keywords: [compatibility]
|
||||
custom_edit_url:
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
# Tools
|
||||
|
||||
## Search Tools
|
||||
|
||||
The following table shows tools that execute online searches in some shape or form:
|
||||
|
||||
{search_table}
|
||||
|
||||
## Code Interpreter Tools
|
||||
|
||||
The following table shows tools that can be used as code interpreters:
|
||||
|
||||
{code_interpreter_table}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def get_search_tools_table() -> str:
|
||||
"""Get the table of search tools."""
|
||||
header = ["tool", "pricing", "available_data"]
|
||||
title = ["Tool", "Free/Paid", "Return Data"]
|
||||
rows = [title, [":-"] + [":-:"] * (len(title) - 1)]
|
||||
for search_tool, feats in sorted(SEARCH_TOOL_FEAT_TABLE.items()):
|
||||
# Fields are in the order of the header
|
||||
row = [
|
||||
f"[{search_tool}]({feats['link']})",
|
||||
]
|
||||
for h in header[1:]:
|
||||
row.append(feats.get(h))
|
||||
rows.append(row)
|
||||
return "\n".join(["|".join(row) for row in rows])
|
||||
|
||||
|
||||
def get_code_interpreter_table() -> str:
|
||||
"""Get the table of search tools."""
|
||||
header = [
|
||||
"tool",
|
||||
"langauges",
|
||||
"sandbox_lifetime",
|
||||
"upload",
|
||||
"return_results",
|
||||
]
|
||||
title = [
|
||||
"Tool",
|
||||
"Supported Languages",
|
||||
"Sandbox Lifetime",
|
||||
"Supports File Uploads",
|
||||
"Return Types",
|
||||
]
|
||||
rows = [title, [":-"] + [":-:"] * (len(title) - 1)]
|
||||
for search_tool, feats in sorted(CODE_INTERPRETER_TOOL_FEAT_TABLE.items()):
|
||||
# Fields are in the order of the header
|
||||
row = [
|
||||
f"[{search_tool}]({feats['link']})",
|
||||
]
|
||||
for h in header[1:]:
|
||||
value = feats.get(h)
|
||||
if h == "upload":
|
||||
if value is True:
|
||||
row.append("✅")
|
||||
else:
|
||||
row.append("❌")
|
||||
else:
|
||||
row.append(value)
|
||||
rows.append(row)
|
||||
return "\n".join(["|".join(row) for row in rows])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
output_dir = Path(sys.argv[1])
|
||||
output_integrations_dir = output_dir / "integrations"
|
||||
output_integrations_dir_tools = output_integrations_dir / "tools"
|
||||
output_integrations_dir_tools.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
tools_page = TOOLS_TEMPLATE.format(
|
||||
search_table=get_search_tools_table(),
|
||||
code_interpreter_table=get_code_interpreter_table(),
|
||||
)
|
||||
with open(output_integrations_dir / "tools" / "index.mdx", "w") as f:
|
||||
f.write(tools_page)
|
||||
@@ -243,8 +243,8 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "generated-index",
|
||||
slug: "integrations/tools",
|
||||
type: "doc",
|
||||
id: "integrations/tools/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ import CodeBlock from "@theme-original/CodeBlock";
|
||||
* @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 {string} [nvidiaParams] - Parameters for Nvidia NIM model. Defaults to `model="meta/llama3-70b-instruct"`
|
||||
* @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.
|
||||
@@ -23,6 +24,7 @@ import CodeBlock from "@theme-original/CodeBlock";
|
||||
* @property {boolean} [hideGoogle] - Whether or not to hide Google VertexAI chat model.
|
||||
* @property {boolean} [hideTogether] - Whether or not to hide Together chat model.
|
||||
* @property {boolean} [hideAzure] - Whether or not to hide Microsoft Azure OpenAI chat model.
|
||||
* @property {boolean} [hideNvidia] - Whether or not to hide NVIDIA NIM model.
|
||||
* @property {string} [customVarName] - Custom variable name for the model. Defaults to `model`.
|
||||
*/
|
||||
|
||||
@@ -40,6 +42,7 @@ export default function ChatModelTabs(props) {
|
||||
googleParams,
|
||||
togetherParams,
|
||||
azureParams,
|
||||
nvidiaParams,
|
||||
hideOpenai,
|
||||
hideAnthropic,
|
||||
hideCohere,
|
||||
@@ -49,6 +52,7 @@ export default function ChatModelTabs(props) {
|
||||
hideGoogle,
|
||||
hideTogether,
|
||||
hideAzure,
|
||||
hideNvidia,
|
||||
customVarName,
|
||||
} = props;
|
||||
|
||||
@@ -69,6 +73,7 @@ export default function ChatModelTabs(props) {
|
||||
const azureParamsOrDefault =
|
||||
azureParams ??
|
||||
`\n azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],\n azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],\n openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],\n`;
|
||||
const nvidiaParamsOrDefault = nvidiaParams ?? `model="meta/llama3-70b-instruct"`
|
||||
|
||||
const llmVarName = customVarName ?? "model";
|
||||
|
||||
@@ -118,6 +123,15 @@ export default function ChatModelTabs(props) {
|
||||
default: false,
|
||||
shouldHide: hideCohere,
|
||||
},
|
||||
{
|
||||
value: "NVIDIA",
|
||||
label: "NVIDIA",
|
||||
text: `from langchain import ChatNVIDIA\n\n${llmVarName} = ChatNVIDIA(${nvidiaParamsOrDefault})`,
|
||||
apiKeyName: "NVIDIA_API_KEY",
|
||||
packageName: "langchain-nvidia-ai-endpoints",
|
||||
default: false,
|
||||
shouldHide: hideNvidia,
|
||||
},
|
||||
{
|
||||
value: "FireworksAI",
|
||||
label: "FireworksAI",
|
||||
|
||||
BIN
docs/static/img/tool_call.png
vendored
Normal file
BIN
docs/static/img/tool_call.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
BIN
docs/static/img/tool_calling_flow.png
vendored
Normal file
BIN
docs/static/img/tool_calling_flow.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/static/img/tool_invocation.png
vendored
Normal file
BIN
docs/static/img/tool_invocation.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
BIN
docs/static/img/tool_results.png
vendored
Normal file
BIN
docs/static/img/tool_results.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
@@ -78,8 +78,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
# section of the configuration file raise errors.
|
||||
#
|
||||
# https://github.com/tophat/syrupy
|
||||
# --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite.
|
||||
addopts = "--snapshot-warn-unused --strict-markers --strict-config --durations=5"
|
||||
addopts = "--strict-markers --strict-config --durations=5"
|
||||
# Registering custom markers.
|
||||
# https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers
|
||||
markers = [
|
||||
|
||||
1520
libs/cli/poetry.lock
generated
1520
libs/cli/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langchain-cli"
|
||||
version = "0.0.25"
|
||||
version = "0.0.26"
|
||||
description = "CLI for interacting with LangChain"
|
||||
authors = ["Erick Friis <erick@langchain.dev>"]
|
||||
readme = "README.md"
|
||||
|
||||
@@ -86,7 +86,7 @@ tree-sitter>=0.20.2,<0.21
|
||||
tree-sitter-languages>=1.8.0,<2
|
||||
upstash-redis>=1.1.0,<2
|
||||
upstash-ratelimit>=1.1.0,<2
|
||||
vdms==0.0.20
|
||||
vdms>=0.0.20
|
||||
xata>=1.0.0a7,<2
|
||||
xmltodict>=0.13.0,<0.14
|
||||
nanopq==0.2.1
|
||||
|
||||
@@ -51,6 +51,7 @@ if TYPE_CHECKING:
|
||||
from langchain_community.chat_models.deepinfra import (
|
||||
ChatDeepInfra,
|
||||
)
|
||||
from langchain_community.chat_models.edenai import ChatEdenAI
|
||||
from langchain_community.chat_models.ernie import (
|
||||
ErnieBotChat,
|
||||
)
|
||||
@@ -164,13 +165,15 @@ if TYPE_CHECKING:
|
||||
from langchain_community.chat_models.yandex import (
|
||||
ChatYandexGPT,
|
||||
)
|
||||
from langchain_community.chat_models.yi import (
|
||||
ChatYi,
|
||||
)
|
||||
from langchain_community.chat_models.yuan2 import (
|
||||
ChatYuan2,
|
||||
)
|
||||
from langchain_community.chat_models.zhipuai import (
|
||||
ChatZhipuAI,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AzureChatOpenAI",
|
||||
"BedrockChat",
|
||||
@@ -182,6 +185,7 @@ __all__ = [
|
||||
"ChatOctoAI",
|
||||
"ChatDatabricks",
|
||||
"ChatDeepInfra",
|
||||
"ChatEdenAI",
|
||||
"ChatEverlyAI",
|
||||
"ChatFireworks",
|
||||
"ChatFriendli",
|
||||
@@ -223,6 +227,7 @@ __all__ = [
|
||||
"QianfanChatEndpoint",
|
||||
"SolarChat",
|
||||
"VolcEngineMaasChat",
|
||||
"ChatYi",
|
||||
]
|
||||
|
||||
|
||||
@@ -237,6 +242,7 @@ _module_lookup = {
|
||||
"ChatDatabricks": "langchain_community.chat_models.databricks",
|
||||
"ChatDeepInfra": "langchain_community.chat_models.deepinfra",
|
||||
"ChatEverlyAI": "langchain_community.chat_models.everlyai",
|
||||
"ChatEdenAI": "langchain_community.chat_models.edenai",
|
||||
"ChatFireworks": "langchain_community.chat_models.fireworks",
|
||||
"ChatFriendli": "langchain_community.chat_models.friendli",
|
||||
"ChatGooglePalm": "langchain_community.chat_models.google_palm",
|
||||
@@ -278,6 +284,7 @@ _module_lookup = {
|
||||
"VolcEngineMaasChat": "langchain_community.chat_models.volcengine_maas",
|
||||
"ChatPremAI": "langchain_community.chat_models.premai",
|
||||
"ChatLlamaCpp": "langchain_community.chat_models.llamacpp",
|
||||
"ChatYi": "langchain_community.chat_models.yi",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
import json
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, List, Mapping, Optional, Type
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
import requests
|
||||
from langchain_core.callbacks import (
|
||||
AsyncCallbackManagerForLLMRun,
|
||||
CallbackManagerForLLMRun,
|
||||
)
|
||||
from langchain_core.language_models import LanguageModelInput
|
||||
from langchain_core.language_models.chat_models import (
|
||||
BaseChatModel,
|
||||
agenerate_from_stream,
|
||||
@@ -24,14 +37,27 @@ from langchain_core.messages import (
|
||||
HumanMessageChunk,
|
||||
SystemMessage,
|
||||
SystemMessageChunk,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.output_parsers.openai_tools import (
|
||||
make_invalid_tool_call,
|
||||
parse_tool_call,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.pydantic_v1 import Field, SecretStr, root_validator
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, SecretStr, root_validator
|
||||
from langchain_core.runnables import Runnable
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_core.utils import (
|
||||
convert_to_secret_str,
|
||||
get_from_dict_or_env,
|
||||
get_pydantic_field_names,
|
||||
)
|
||||
from langchain_core.utils.function_calling import convert_to_openai_tool
|
||||
|
||||
from langchain_community.chat_models.llamacpp import (
|
||||
_lc_invalid_tool_call_to_openai_tool_call,
|
||||
_lc_tool_call_to_openai_tool_call,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -40,14 +66,33 @@ DEFAULT_API_BASE = "https://api.baichuan-ai.com/v1/chat/completions"
|
||||
|
||||
def _convert_message_to_dict(message: BaseMessage) -> dict:
|
||||
message_dict: Dict[str, Any]
|
||||
content = message.content
|
||||
if isinstance(message, ChatMessage):
|
||||
message_dict = {"role": message.role, "content": message.content}
|
||||
message_dict = {"role": message.role, "content": content}
|
||||
elif isinstance(message, HumanMessage):
|
||||
message_dict = {"role": "user", "content": message.content}
|
||||
message_dict = {"role": "user", "content": content}
|
||||
elif isinstance(message, AIMessage):
|
||||
message_dict = {"role": "assistant", "content": message.content}
|
||||
message_dict = {"role": "assistant", "content": content}
|
||||
if "tool_calls" in message.additional_kwargs:
|
||||
message_dict["tool_calls"] = message.additional_kwargs["tool_calls"]
|
||||
|
||||
elif message.tool_calls or message.invalid_tool_calls:
|
||||
message_dict["tool_calls"] = [
|
||||
_lc_tool_call_to_openai_tool_call(tc) for tc in message.tool_calls
|
||||
] + [
|
||||
_lc_invalid_tool_call_to_openai_tool_call(tc)
|
||||
for tc in message.invalid_tool_calls
|
||||
]
|
||||
elif isinstance(message, ToolMessage):
|
||||
message_dict = {
|
||||
"role": "tool",
|
||||
"tool_call_id": message.tool_call_id,
|
||||
"content": content,
|
||||
"name": message.name or message.additional_kwargs.get("name"),
|
||||
}
|
||||
|
||||
elif isinstance(message, SystemMessage):
|
||||
message_dict = {"role": "system", "content": message.content}
|
||||
message_dict = {"role": "system", "content": content}
|
||||
else:
|
||||
raise TypeError(f"Got unknown type {message}")
|
||||
|
||||
@@ -56,14 +101,43 @@ def _convert_message_to_dict(message: BaseMessage) -> dict:
|
||||
|
||||
def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage:
|
||||
role = _dict["role"]
|
||||
content = _dict.get("content", "")
|
||||
if role == "user":
|
||||
return HumanMessage(content=_dict["content"])
|
||||
return HumanMessage(content=content)
|
||||
elif role == "assistant":
|
||||
return AIMessage(content=_dict.get("content", "") or "")
|
||||
tool_calls = []
|
||||
invalid_tool_calls = []
|
||||
additional_kwargs = {}
|
||||
|
||||
if raw_tool_calls := _dict.get("tool_calls"):
|
||||
additional_kwargs["tool_calls"] = raw_tool_calls
|
||||
for raw_tool_call in raw_tool_calls:
|
||||
try:
|
||||
tool_calls.append(parse_tool_call(raw_tool_call, return_id=True))
|
||||
except Exception as e:
|
||||
invalid_tool_calls.append(
|
||||
make_invalid_tool_call(raw_tool_call, str(e))
|
||||
)
|
||||
|
||||
return AIMessage(
|
||||
content=content,
|
||||
additional_kwargs=additional_kwargs,
|
||||
tool_calls=tool_calls, # type: ignore[arg-type]
|
||||
invalid_tool_calls=invalid_tool_calls,
|
||||
)
|
||||
elif role == "tool":
|
||||
additional_kwargs = {}
|
||||
if "name" in _dict:
|
||||
additional_kwargs["name"] = _dict["name"]
|
||||
return ToolMessage(
|
||||
content=content,
|
||||
tool_call_id=_dict.get("tool_call_id"), # type: ignore[arg-type]
|
||||
additional_kwargs=additional_kwargs,
|
||||
)
|
||||
elif role == "system":
|
||||
return SystemMessage(content=_dict.get("content", ""))
|
||||
return SystemMessage(content=content)
|
||||
else:
|
||||
return ChatMessage(content=_dict["content"], role=role)
|
||||
return ChatMessage(content=content, role=role)
|
||||
|
||||
|
||||
def _convert_delta_to_message_chunk(
|
||||
@@ -226,6 +300,24 @@ class ChatBaichuan(BaseChatModel):
|
||||
},
|
||||
id='run-952509ed-9154-4ff9-b187-e616d7ddfbba-0'
|
||||
)
|
||||
Tool calling:
|
||||
|
||||
.. code-block:: python
|
||||
class get_current_weather(BaseModel):
|
||||
'''Get current weather.'''
|
||||
|
||||
location: str = Field('City or province, such as Shanghai')
|
||||
|
||||
|
||||
llm_with_tools = ChatBaichuan(model='Baichuan3-Turbo').bind_tools([get_current_weather])
|
||||
llm_with_tools.invoke('How is the weather today?')
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[{'name': 'get_current_weather',
|
||||
'args': {'location': 'New York'},
|
||||
'id': '3951017OF8doB0A',
|
||||
'type': 'tool_call'}]
|
||||
|
||||
Response metadata
|
||||
.. code-block:: python
|
||||
@@ -486,6 +578,7 @@ class ChatBaichuan(BaseChatModel):
|
||||
model = parameters.pop("model")
|
||||
with_search_enhance = parameters.pop("with_search_enhance", False)
|
||||
stream = parameters.pop("stream", False)
|
||||
tools = parameters.pop("tools", [])
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
@@ -495,7 +588,9 @@ class ChatBaichuan(BaseChatModel):
|
||||
"temperature": temperature,
|
||||
"with_search_enhance": with_search_enhance,
|
||||
"stream": stream,
|
||||
"tools": tools,
|
||||
}
|
||||
|
||||
return payload
|
||||
|
||||
def _create_headers_parameters(self, **kwargs) -> Dict[str, Any]: # type: ignore[no-untyped-def]
|
||||
@@ -526,3 +621,23 @@ class ChatBaichuan(BaseChatModel):
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "baichuan-chat"
|
||||
|
||||
def bind_tools(
|
||||
self,
|
||||
tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, BaseMessage]:
|
||||
"""Bind tool-like objects to this chat model.
|
||||
|
||||
Args:
|
||||
tools: A list of tool definitions to bind to this chat model.
|
||||
Can be a dictionary, pydantic model, callable, or BaseTool.
|
||||
Pydantic
|
||||
models, callables, and BaseTools will be automatically converted to
|
||||
their schema dictionary representation.
|
||||
**kwargs: Any additional parameters to pass to the
|
||||
:class:`~langchain.runnable.Runnable` constructor.
|
||||
"""
|
||||
|
||||
formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
|
||||
return super().bind(tools=formatted_tools, **kwargs)
|
||||
|
||||
@@ -122,8 +122,8 @@ def _format_edenai_messages(messages: List[BaseMessage]) -> Dict[str, Any]:
|
||||
system = None
|
||||
formatted_messages = []
|
||||
|
||||
human_messages = filter(lambda msg: isinstance(msg, HumanMessage), messages)
|
||||
last_human_message = list(human_messages)[-1] if human_messages else ""
|
||||
human_messages = list(filter(lambda msg: isinstance(msg, HumanMessage), messages))
|
||||
last_human_message = human_messages[-1] if human_messages else ""
|
||||
|
||||
tool_results, other_messages = _extract_edenai_tool_results_from_messages(messages)
|
||||
for i, message in enumerate(other_messages):
|
||||
|
||||
@@ -176,29 +176,36 @@ class ChatMlflow(BaseChatModel):
|
||||
chunk_iter = self._client.predict_stream(endpoint=self.endpoint, inputs=data)
|
||||
first_chunk_role = None
|
||||
for chunk in chunk_iter:
|
||||
choice = chunk["choices"][0]
|
||||
if chunk["choices"]:
|
||||
choice = chunk["choices"][0]
|
||||
|
||||
chunk_delta = choice["delta"]
|
||||
if first_chunk_role is None:
|
||||
first_chunk_role = chunk_delta.get("role")
|
||||
chunk = ChatMlflow._convert_delta_to_message_chunk(
|
||||
chunk_delta, first_chunk_role
|
||||
)
|
||||
chunk_delta = choice["delta"]
|
||||
if first_chunk_role is None:
|
||||
first_chunk_role = chunk_delta.get("role")
|
||||
|
||||
generation_info = {}
|
||||
if finish_reason := choice.get("finish_reason"):
|
||||
generation_info["finish_reason"] = finish_reason
|
||||
if logprobs := choice.get("logprobs"):
|
||||
generation_info["logprobs"] = logprobs
|
||||
chunk_message = ChatMlflow._convert_delta_to_message_chunk(
|
||||
chunk_delta, first_chunk_role
|
||||
)
|
||||
|
||||
chunk = ChatGenerationChunk(
|
||||
message=chunk, generation_info=generation_info or None
|
||||
)
|
||||
generation_info = {}
|
||||
if finish_reason := choice.get("finish_reason"):
|
||||
generation_info["finish_reason"] = finish_reason
|
||||
if logprobs := choice.get("logprobs"):
|
||||
generation_info["logprobs"] = logprobs
|
||||
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(chunk.text, chunk=chunk, logprobs=logprobs)
|
||||
chunk = ChatGenerationChunk(
|
||||
message=chunk_message, generation_info=generation_info or None
|
||||
)
|
||||
|
||||
yield chunk
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(
|
||||
chunk.text, chunk=chunk, logprobs=logprobs
|
||||
)
|
||||
|
||||
yield chunk
|
||||
else:
|
||||
# Handle the case where choices are empty if needed
|
||||
continue
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
import json
|
||||
import re
|
||||
import uuid
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.language_models import LanguageModelInput
|
||||
from langchain_core.language_models.chat_models import (
|
||||
BaseChatModel,
|
||||
generate_from_stream,
|
||||
@@ -14,15 +28,76 @@ from langchain_core.messages import (
|
||||
ChatMessage,
|
||||
HumanMessage,
|
||||
SystemMessage,
|
||||
ToolCall,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.messages.tool import ToolCallChunk
|
||||
from langchain_core.output_parsers.base import OutputParserLike
|
||||
from langchain_core.output_parsers.openai_tools import (
|
||||
JsonOutputKeyToolsParser,
|
||||
PydanticToolsParser,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.pydantic_v1 import Extra
|
||||
from langchain_core.pydantic_v1 import BaseModel, Extra
|
||||
from langchain_core.runnables import Runnable
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_core.utils.function_calling import convert_to_openai_function
|
||||
|
||||
from langchain_community.llms.oci_generative_ai import OCIGenAIBase
|
||||
from langchain_community.llms.utils import enforce_stop_tokens
|
||||
|
||||
CUSTOM_ENDPOINT_PREFIX = "ocid1.generativeaiendpoint"
|
||||
|
||||
JSON_TO_PYTHON_TYPES = {
|
||||
"string": "str",
|
||||
"number": "float",
|
||||
"boolean": "bool",
|
||||
"integer": "int",
|
||||
"array": "List",
|
||||
"object": "Dict",
|
||||
}
|
||||
|
||||
|
||||
def _remove_signature_from_tool_description(name: str, description: str) -> str:
|
||||
"""
|
||||
Removes the `{name}{signature} - ` prefix and Args: section from tool description.
|
||||
The signature is usually present for tools created with the @tool decorator,
|
||||
whereas the Args: section may be present in function doc blocks.
|
||||
"""
|
||||
description = re.sub(rf"^{name}\(.*?\) -(?:> \w+? -)? ", "", description)
|
||||
description = re.sub(r"(?s)(?:\n?\n\s*?)?Args:.*$", "", description)
|
||||
return description
|
||||
|
||||
|
||||
def _format_oci_tool_calls(
|
||||
tool_calls: Optional[List[Any]] = None,
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Formats a OCI GenAI API response into the tool call format used in Langchain.
|
||||
"""
|
||||
if not tool_calls:
|
||||
return []
|
||||
|
||||
formatted_tool_calls = []
|
||||
for tool_call in tool_calls:
|
||||
formatted_tool_calls.append(
|
||||
{
|
||||
"id": uuid.uuid4().hex[:],
|
||||
"function": {
|
||||
"name": tool_call.name,
|
||||
"arguments": json.dumps(tool_call.parameters),
|
||||
},
|
||||
"type": "function",
|
||||
}
|
||||
)
|
||||
return formatted_tool_calls
|
||||
|
||||
|
||||
def _convert_oci_tool_call_to_langchain(tool_call: Any) -> ToolCall:
|
||||
"""Convert a OCI GenAI tool call into langchain_core.messages.ToolCall"""
|
||||
_id = uuid.uuid4().hex[:]
|
||||
return ToolCall(name=tool_call.name, args=tool_call.parameters, id=_id)
|
||||
|
||||
|
||||
class Provider(ABC):
|
||||
@property
|
||||
@@ -35,14 +110,28 @@ class Provider(ABC):
|
||||
@abstractmethod
|
||||
def chat_stream_to_text(self, event_data: Dict) -> str: ...
|
||||
|
||||
@abstractmethod
|
||||
def is_chat_stream_end(self, event_data: Dict) -> bool: ...
|
||||
|
||||
@abstractmethod
|
||||
def chat_generation_info(self, response: Any) -> Dict[str, Any]: ...
|
||||
|
||||
@abstractmethod
|
||||
def chat_stream_generation_info(self, event_data: Dict) -> Dict[str, Any]: ...
|
||||
|
||||
@abstractmethod
|
||||
def get_role(self, message: BaseMessage) -> str: ...
|
||||
|
||||
@abstractmethod
|
||||
def messages_to_oci_params(self, messages: Any) -> Dict[str, Any]: ...
|
||||
def messages_to_oci_params(
|
||||
self, messages: Any, **kwargs: Any
|
||||
) -> Dict[str, Any]: ...
|
||||
|
||||
@abstractmethod
|
||||
def convert_to_oci_tool(
|
||||
self,
|
||||
tool: Union[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
|
||||
) -> Dict[str, Any]: ...
|
||||
|
||||
|
||||
class CohereProvider(Provider):
|
||||
@@ -52,10 +141,15 @@ class CohereProvider(Provider):
|
||||
from oci.generative_ai_inference import models
|
||||
|
||||
self.oci_chat_request = models.CohereChatRequest
|
||||
self.oci_tool = models.CohereTool
|
||||
self.oci_tool_param = models.CohereParameterDefinition
|
||||
self.oci_tool_result = models.CohereToolResult
|
||||
self.oci_tool_call = models.CohereToolCall
|
||||
self.oci_chat_message = {
|
||||
"USER": models.CohereUserMessage,
|
||||
"CHATBOT": models.CohereChatBotMessage,
|
||||
"SYSTEM": models.CohereSystemMessage,
|
||||
"TOOL": models.CohereToolMessage,
|
||||
}
|
||||
self.chat_api_format = models.BaseChatRequest.API_FORMAT_COHERE
|
||||
|
||||
@@ -63,15 +157,54 @@ class CohereProvider(Provider):
|
||||
return response.data.chat_response.text
|
||||
|
||||
def chat_stream_to_text(self, event_data: Dict) -> str:
|
||||
if "text" in event_data and "finishReason" not in event_data:
|
||||
if "text" in event_data:
|
||||
return event_data["text"]
|
||||
else:
|
||||
return ""
|
||||
|
||||
def is_chat_stream_end(self, event_data: Dict) -> bool:
|
||||
return "finishReason" in event_data
|
||||
|
||||
def chat_generation_info(self, response: Any) -> Dict[str, Any]:
|
||||
return {
|
||||
generation_info: Dict[str, Any] = {
|
||||
"documents": response.data.chat_response.documents,
|
||||
"citations": response.data.chat_response.citations,
|
||||
"search_queries": response.data.chat_response.search_queries,
|
||||
"is_search_required": response.data.chat_response.is_search_required,
|
||||
"finish_reason": response.data.chat_response.finish_reason,
|
||||
}
|
||||
if response.data.chat_response.tool_calls:
|
||||
# Only populate tool_calls when 1) present on the response and
|
||||
# 2) has one or more calls.
|
||||
generation_info["tool_calls"] = _format_oci_tool_calls(
|
||||
response.data.chat_response.tool_calls
|
||||
)
|
||||
|
||||
return generation_info
|
||||
|
||||
def chat_stream_generation_info(self, event_data: Dict) -> Dict[str, Any]:
|
||||
generation_info: Dict[str, Any] = {
|
||||
"documents": event_data.get("documents"),
|
||||
"citations": event_data.get("citations"),
|
||||
"finish_reason": event_data.get("finishReason"),
|
||||
}
|
||||
if "toolCalls" in event_data:
|
||||
generation_info["tool_calls"] = []
|
||||
for tool_call in event_data["toolCalls"]:
|
||||
generation_info["tool_calls"].append(
|
||||
{
|
||||
"id": uuid.uuid4().hex[:],
|
||||
"function": {
|
||||
"name": tool_call["name"],
|
||||
"arguments": json.dumps(tool_call["parameters"]),
|
||||
},
|
||||
"type": "function",
|
||||
}
|
||||
)
|
||||
|
||||
generation_info = {k: v for k, v in generation_info.items() if v is not None}
|
||||
|
||||
return generation_info
|
||||
|
||||
def get_role(self, message: BaseMessage) -> str:
|
||||
if isinstance(message, HumanMessage):
|
||||
@@ -80,21 +213,154 @@ class CohereProvider(Provider):
|
||||
return "CHATBOT"
|
||||
elif isinstance(message, SystemMessage):
|
||||
return "SYSTEM"
|
||||
elif isinstance(message, ToolMessage):
|
||||
return "TOOL"
|
||||
else:
|
||||
raise ValueError(f"Got unknown type {message}")
|
||||
|
||||
def messages_to_oci_params(self, messages: Sequence[ChatMessage]) -> Dict[str, Any]:
|
||||
oci_chat_history = [
|
||||
self.oci_chat_message[self.get_role(msg)](message=msg.content)
|
||||
for msg in messages[:-1]
|
||||
]
|
||||
def messages_to_oci_params(
|
||||
self, messages: Sequence[ChatMessage], **kwargs: Any
|
||||
) -> Dict[str, Any]:
|
||||
is_force_single_step = kwargs.get("is_force_single_step") or False
|
||||
|
||||
oci_chat_history = []
|
||||
|
||||
for msg in messages[:-1]:
|
||||
if self.get_role(msg) == "USER" or self.get_role(msg) == "SYSTEM":
|
||||
oci_chat_history.append(
|
||||
self.oci_chat_message[self.get_role(msg)](message=msg.content)
|
||||
)
|
||||
elif isinstance(msg, AIMessage):
|
||||
if msg.tool_calls and is_force_single_step:
|
||||
continue
|
||||
tool_calls = (
|
||||
[
|
||||
self.oci_tool_call(name=tc["name"], parameters=tc["args"])
|
||||
for tc in msg.tool_calls
|
||||
]
|
||||
if msg.tool_calls
|
||||
else None
|
||||
)
|
||||
msg_content = msg.content if msg.content else " "
|
||||
oci_chat_history.append(
|
||||
self.oci_chat_message[self.get_role(msg)](
|
||||
message=msg_content, tool_calls=tool_calls
|
||||
)
|
||||
)
|
||||
|
||||
# Get the messages for the current chat turn
|
||||
current_chat_turn_messages = []
|
||||
for message in messages[::-1]:
|
||||
current_chat_turn_messages.append(message)
|
||||
if isinstance(message, HumanMessage):
|
||||
break
|
||||
current_chat_turn_messages = current_chat_turn_messages[::-1]
|
||||
|
||||
oci_tool_results: Union[List[Any], None] = []
|
||||
for message in current_chat_turn_messages:
|
||||
if isinstance(message, ToolMessage):
|
||||
tool_message = message
|
||||
previous_ai_msgs = [
|
||||
message
|
||||
for message in current_chat_turn_messages
|
||||
if isinstance(message, AIMessage) and message.tool_calls
|
||||
]
|
||||
if previous_ai_msgs:
|
||||
previous_ai_msg = previous_ai_msgs[-1]
|
||||
for lc_tool_call in previous_ai_msg.tool_calls:
|
||||
if lc_tool_call["id"] == tool_message.tool_call_id:
|
||||
tool_result = self.oci_tool_result()
|
||||
tool_result.call = self.oci_tool_call(
|
||||
name=lc_tool_call["name"],
|
||||
parameters=lc_tool_call["args"],
|
||||
)
|
||||
tool_result.outputs = [{"output": tool_message.content}]
|
||||
oci_tool_results.append(tool_result)
|
||||
|
||||
if not oci_tool_results:
|
||||
oci_tool_results = None
|
||||
|
||||
message_str = "" if oci_tool_results else messages[-1].content
|
||||
|
||||
oci_params = {
|
||||
"message": messages[-1].content,
|
||||
"message": message_str,
|
||||
"chat_history": oci_chat_history,
|
||||
"tool_results": oci_tool_results,
|
||||
"api_format": self.chat_api_format,
|
||||
}
|
||||
|
||||
return oci_params
|
||||
return {k: v for k, v in oci_params.items() if v is not None}
|
||||
|
||||
def convert_to_oci_tool(
|
||||
self,
|
||||
tool: Union[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Convert a BaseTool instance, JSON schema dict, or BaseModel type to a OCI tool.
|
||||
"""
|
||||
if isinstance(tool, BaseTool):
|
||||
return self.oci_tool(
|
||||
name=tool.name,
|
||||
description=_remove_signature_from_tool_description(
|
||||
tool.name, tool.description
|
||||
),
|
||||
parameter_definitions={
|
||||
p_name: self.oci_tool_param(
|
||||
description=p_def.get("description")
|
||||
if "description" in p_def
|
||||
else "",
|
||||
type=JSON_TO_PYTHON_TYPES.get(
|
||||
p_def.get("type"), p_def.get("type")
|
||||
),
|
||||
is_required="default" not in p_def,
|
||||
)
|
||||
for p_name, p_def in tool.args.items()
|
||||
},
|
||||
)
|
||||
elif isinstance(tool, dict):
|
||||
if not all(k in tool for k in ("title", "description", "properties")):
|
||||
raise ValueError(
|
||||
"Unsupported dict type. Tool must be passed in as a BaseTool instance, JSON schema dict, or BaseModel type." # noqa: E501
|
||||
)
|
||||
return self.oci_tool(
|
||||
name=tool.get("title"),
|
||||
description=tool.get("description"),
|
||||
parameter_definitions={
|
||||
p_name: self.oci_tool_param(
|
||||
description=p_def.get("description"),
|
||||
type=JSON_TO_PYTHON_TYPES.get(
|
||||
p_def.get("type"), p_def.get("type")
|
||||
),
|
||||
is_required="default" not in p_def,
|
||||
)
|
||||
for p_name, p_def in tool.get("properties", {}).items()
|
||||
},
|
||||
)
|
||||
elif (isinstance(tool, type) and issubclass(tool, BaseModel)) or callable(tool):
|
||||
as_json_schema_function = convert_to_openai_function(tool)
|
||||
parameters = as_json_schema_function.get("parameters", {})
|
||||
properties = parameters.get("properties", {})
|
||||
return self.oci_tool(
|
||||
name=as_json_schema_function.get("name"),
|
||||
description=as_json_schema_function.get(
|
||||
"description",
|
||||
as_json_schema_function.get("name"),
|
||||
),
|
||||
parameter_definitions={
|
||||
p_name: self.oci_tool_param(
|
||||
description=p_def.get("description"),
|
||||
type=JSON_TO_PYTHON_TYPES.get(
|
||||
p_def.get("type"), p_def.get("type")
|
||||
),
|
||||
is_required=p_name in parameters.get("required", []),
|
||||
)
|
||||
for p_name, p_def in properties.items()
|
||||
},
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unsupported tool type {type(tool)}. Tool must be passed in as a BaseTool instance, JSON schema dict, or BaseModel type." # noqa: E501
|
||||
)
|
||||
|
||||
|
||||
class MetaProvider(Provider):
|
||||
@@ -116,10 +382,10 @@ class MetaProvider(Provider):
|
||||
return response.data.chat_response.choices[0].message.content[0].text
|
||||
|
||||
def chat_stream_to_text(self, event_data: Dict) -> str:
|
||||
if "message" in event_data:
|
||||
return event_data["message"]["content"][0]["text"]
|
||||
else:
|
||||
return ""
|
||||
return event_data["message"]["content"][0]["text"]
|
||||
|
||||
def is_chat_stream_end(self, event_data: Dict) -> bool:
|
||||
return "message" not in event_data
|
||||
|
||||
def chat_generation_info(self, response: Any) -> Dict[str, Any]:
|
||||
return {
|
||||
@@ -127,6 +393,11 @@ class MetaProvider(Provider):
|
||||
"time_created": str(response.data.chat_response.time_created),
|
||||
}
|
||||
|
||||
def chat_stream_generation_info(self, event_data: Dict) -> Dict[str, Any]:
|
||||
return {
|
||||
"finish_reason": event_data["finishReason"],
|
||||
}
|
||||
|
||||
def get_role(self, message: BaseMessage) -> str:
|
||||
# meta only supports alternating user/assistant roles
|
||||
if isinstance(message, HumanMessage):
|
||||
@@ -138,7 +409,9 @@ class MetaProvider(Provider):
|
||||
else:
|
||||
raise ValueError(f"Got unknown type {message}")
|
||||
|
||||
def messages_to_oci_params(self, messages: List[BaseMessage]) -> Dict[str, Any]:
|
||||
def messages_to_oci_params(
|
||||
self, messages: List[BaseMessage], **kwargs: Any
|
||||
) -> Dict[str, Any]:
|
||||
oci_messages = [
|
||||
self.oci_chat_message[self.get_role(msg)](
|
||||
content=[self.oci_chat_message_content(text=msg.content)]
|
||||
@@ -153,6 +426,12 @@ class MetaProvider(Provider):
|
||||
|
||||
return oci_params
|
||||
|
||||
def convert_to_oci_tool(
|
||||
self,
|
||||
tool: Union[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
|
||||
) -> Dict[str, Any]:
|
||||
raise NotImplementedError("Tools not supported for Meta models")
|
||||
|
||||
|
||||
class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
"""ChatOCIGenAI chat model integration.
|
||||
@@ -247,8 +526,8 @@ class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]],
|
||||
kwargs: Dict[str, Any],
|
||||
stream: bool,
|
||||
**kwargs: Any,
|
||||
) -> Dict[str, Any]:
|
||||
try:
|
||||
from oci.generative_ai_inference import models
|
||||
@@ -258,8 +537,10 @@ class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
"Could not import oci python package. "
|
||||
"Please make sure you have the oci package installed."
|
||||
) from ex
|
||||
oci_params = self._provider.messages_to_oci_params(messages)
|
||||
oci_params["is_stream"] = stream # self.is_stream
|
||||
|
||||
oci_params = self._provider.messages_to_oci_params(messages, **kwargs)
|
||||
|
||||
oci_params["is_stream"] = stream
|
||||
_model_kwargs = self.model_kwargs or {}
|
||||
|
||||
if stop is not None:
|
||||
@@ -280,6 +561,43 @@ class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
|
||||
return request
|
||||
|
||||
def bind_tools(
|
||||
self,
|
||||
tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, BaseMessage]:
|
||||
formatted_tools = [self._provider.convert_to_oci_tool(tool) for tool in tools]
|
||||
return super().bind(tools=formatted_tools, **kwargs)
|
||||
|
||||
def with_structured_output(
|
||||
self,
|
||||
schema: Union[Dict[Any, Any], Type[BaseModel]],
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]:
|
||||
"""Model wrapper that returns outputs formatted to match the given schema.
|
||||
|
||||
Args:
|
||||
schema: The output schema as a dict or a Pydantic class. If a Pydantic class
|
||||
then the model output will be an object of that class. If a dict then
|
||||
the model output will be a dict.
|
||||
|
||||
Returns:
|
||||
A Runnable that takes any ChatModel input and returns either a dict or
|
||||
Pydantic class as output.
|
||||
"""
|
||||
llm = self.bind_tools([schema], **kwargs)
|
||||
if isinstance(schema, type) and issubclass(schema, BaseModel):
|
||||
output_parser: OutputParserLike = PydanticToolsParser(
|
||||
tools=[schema], first_tool_only=True
|
||||
)
|
||||
else:
|
||||
key_name = getattr(self._provider.convert_to_oci_tool(schema), "name")
|
||||
output_parser = JsonOutputKeyToolsParser(
|
||||
key_name=key_name, first_tool_only=True
|
||||
)
|
||||
|
||||
return llm | output_parser
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
@@ -313,7 +631,7 @@ class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
)
|
||||
return generate_from_stream(stream_iter)
|
||||
|
||||
request = self._prepare_request(messages, stop, kwargs, stream=False)
|
||||
request = self._prepare_request(messages, stop=stop, stream=False, **kwargs)
|
||||
response = self.client.chat(request)
|
||||
|
||||
content = self._provider.chat_response_to_text(response)
|
||||
@@ -330,11 +648,22 @@ class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
"content-length": response.headers["content-length"],
|
||||
}
|
||||
|
||||
if "tool_calls" in generation_info:
|
||||
tool_calls = [
|
||||
_convert_oci_tool_call_to_langchain(tool_call)
|
||||
for tool_call in response.data.chat_response.tool_calls
|
||||
]
|
||||
else:
|
||||
tool_calls = []
|
||||
|
||||
message = AIMessage(
|
||||
content=content,
|
||||
additional_kwargs=generation_info,
|
||||
tool_calls=tool_calls,
|
||||
)
|
||||
return ChatResult(
|
||||
generations=[
|
||||
ChatGeneration(
|
||||
message=AIMessage(content=content), generation_info=generation_info
|
||||
)
|
||||
ChatGeneration(message=message, generation_info=generation_info)
|
||||
],
|
||||
llm_output=llm_output,
|
||||
)
|
||||
@@ -346,12 +675,42 @@ class ChatOCIGenAI(BaseChatModel, OCIGenAIBase):
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
request = self._prepare_request(messages, stop, kwargs, stream=True)
|
||||
request = self._prepare_request(messages, stop=stop, stream=True, **kwargs)
|
||||
response = self.client.chat(request)
|
||||
|
||||
for event in response.data.events():
|
||||
delta = self._provider.chat_stream_to_text(json.loads(event.data))
|
||||
chunk = ChatGenerationChunk(message=AIMessageChunk(content=delta))
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(delta, chunk=chunk)
|
||||
yield chunk
|
||||
event_data = json.loads(event.data)
|
||||
if not self._provider.is_chat_stream_end(event_data): # still streaming
|
||||
delta = self._provider.chat_stream_to_text(event_data)
|
||||
chunk = ChatGenerationChunk(message=AIMessageChunk(content=delta))
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(delta, chunk=chunk)
|
||||
yield chunk
|
||||
else: # stream end
|
||||
generation_info = self._provider.chat_stream_generation_info(event_data)
|
||||
tool_call_chunks = []
|
||||
if tool_calls := generation_info.get("tool_calls"):
|
||||
content = self._provider.chat_stream_to_text(event_data)
|
||||
try:
|
||||
tool_call_chunks = [
|
||||
ToolCallChunk(
|
||||
name=tool_call["function"].get("name"),
|
||||
args=tool_call["function"].get("arguments"),
|
||||
id=tool_call.get("id"),
|
||||
index=tool_call.get("index"),
|
||||
)
|
||||
for tool_call in tool_calls
|
||||
]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
content = ""
|
||||
message = AIMessageChunk(
|
||||
content=content,
|
||||
additional_kwargs=generation_info,
|
||||
tool_call_chunks=tool_call_chunks,
|
||||
)
|
||||
yield ChatGenerationChunk(
|
||||
message=message,
|
||||
generation_info=generation_info,
|
||||
)
|
||||
|
||||
339
libs/community/langchain_community/chat_models/yi.py
Normal file
339
libs/community/langchain_community/chat_models/yi.py
Normal file
@@ -0,0 +1,339 @@
|
||||
import json
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, List, Mapping, Optional, Type
|
||||
|
||||
import requests
|
||||
from langchain_core.callbacks import (
|
||||
AsyncCallbackManagerForLLMRun,
|
||||
CallbackManagerForLLMRun,
|
||||
)
|
||||
from langchain_core.language_models.chat_models import (
|
||||
BaseChatModel,
|
||||
agenerate_from_stream,
|
||||
generate_from_stream,
|
||||
)
|
||||
from langchain_core.messages import (
|
||||
AIMessage,
|
||||
AIMessageChunk,
|
||||
BaseMessage,
|
||||
BaseMessageChunk,
|
||||
ChatMessage,
|
||||
ChatMessageChunk,
|
||||
HumanMessage,
|
||||
HumanMessageChunk,
|
||||
SystemMessage,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.pydantic_v1 import Field, SecretStr
|
||||
from langchain_core.utils import (
|
||||
convert_to_secret_str,
|
||||
get_from_dict_or_env,
|
||||
get_pydantic_field_names,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_API_BASE_CN = "https://api.lingyiwanwu.com/v1/chat/completions"
|
||||
DEFAULT_API_BASE_GLOBAL = "https://api.01.ai/v1/chat/completions"
|
||||
|
||||
|
||||
def _convert_message_to_dict(message: BaseMessage) -> dict:
|
||||
message_dict: Dict[str, Any]
|
||||
if isinstance(message, ChatMessage):
|
||||
message_dict = {"role": message.role, "content": message.content}
|
||||
elif isinstance(message, HumanMessage):
|
||||
message_dict = {"role": "user", "content": message.content}
|
||||
elif isinstance(message, AIMessage):
|
||||
message_dict = {"role": "assistant", "content": message.content}
|
||||
elif isinstance(message, SystemMessage):
|
||||
message_dict = {"role": "assistant", "content": message.content}
|
||||
else:
|
||||
raise TypeError(f"Got unknown type {message}")
|
||||
|
||||
return message_dict
|
||||
|
||||
|
||||
def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage:
|
||||
role = _dict["role"]
|
||||
if role == "user":
|
||||
return HumanMessage(content=_dict["content"])
|
||||
elif role == "assistant":
|
||||
return AIMessage(content=_dict.get("content", "") or "")
|
||||
elif role == "system":
|
||||
return AIMessage(content=_dict["content"])
|
||||
else:
|
||||
return ChatMessage(content=_dict["content"], role=role)
|
||||
|
||||
|
||||
def _convert_delta_to_message_chunk(
|
||||
_dict: Mapping[str, Any], default_class: Type[BaseMessageChunk]
|
||||
) -> BaseMessageChunk:
|
||||
role: str = _dict["role"]
|
||||
content = _dict.get("content") or ""
|
||||
|
||||
if role == "user" or default_class == HumanMessageChunk:
|
||||
return HumanMessageChunk(content=content)
|
||||
elif role == "assistant" or default_class == AIMessageChunk:
|
||||
return AIMessageChunk(content=content)
|
||||
elif role or default_class == ChatMessageChunk:
|
||||
return ChatMessageChunk(content=content, role=role)
|
||||
else:
|
||||
return default_class(content=content, type=role)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def aconnect_httpx_sse(
|
||||
client: Any, method: str, url: str, **kwargs: Any
|
||||
) -> AsyncIterator:
|
||||
from httpx_sse import EventSource
|
||||
|
||||
async with client.stream(method, url, **kwargs) as response:
|
||||
yield EventSource(response)
|
||||
|
||||
|
||||
class ChatYi(BaseChatModel):
|
||||
"""Yi chat models API."""
|
||||
|
||||
@property
|
||||
def lc_secrets(self) -> Dict[str, str]:
|
||||
return {
|
||||
"yi_api_key": "YI_API_KEY",
|
||||
}
|
||||
|
||||
@property
|
||||
def lc_serializable(self) -> bool:
|
||||
return True
|
||||
|
||||
yi_api_base: str = Field(default=DEFAULT_API_BASE_CN)
|
||||
yi_api_key: SecretStr = Field(alias="api_key")
|
||||
region: str = Field(default="cn") # 默认使用中国区
|
||||
streaming: bool = False
|
||||
request_timeout: int = Field(default=60, alias="timeout")
|
||||
model: str = "yi-large"
|
||||
temperature: Optional[float] = Field(default=0.7)
|
||||
top_p: float = 0.7
|
||||
model_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
class Config:
|
||||
allow_population_by_field_name = True
|
||||
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
kwargs["yi_api_key"] = convert_to_secret_str(
|
||||
get_from_dict_or_env(
|
||||
kwargs,
|
||||
["yi_api_key", "api_key"],
|
||||
"YI_API_KEY",
|
||||
)
|
||||
)
|
||||
if kwargs.get("yi_api_base") is None:
|
||||
region = kwargs.get("region", "cn").lower()
|
||||
if region == "global":
|
||||
kwargs["yi_api_base"] = DEFAULT_API_BASE_GLOBAL
|
||||
else:
|
||||
kwargs["yi_api_base"] = DEFAULT_API_BASE_CN
|
||||
|
||||
all_required_field_names = get_pydantic_field_names(self.__class__)
|
||||
extra = kwargs.get("model_kwargs", {})
|
||||
for field_name in list(kwargs):
|
||||
if field_name in extra:
|
||||
raise ValueError(f"Found {field_name} supplied twice.")
|
||||
if field_name not in all_required_field_names:
|
||||
extra[field_name] = kwargs.pop(field_name)
|
||||
|
||||
invalid_model_kwargs = all_required_field_names.intersection(extra.keys())
|
||||
if invalid_model_kwargs:
|
||||
raise ValueError(
|
||||
f"Parameters {invalid_model_kwargs} should be specified explicitly. "
|
||||
f"Instead they were passed in as part of `model_kwargs` parameter."
|
||||
)
|
||||
|
||||
kwargs["model_kwargs"] = extra
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"stream": self.streaming,
|
||||
}
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
if self.streaming:
|
||||
stream_iter = self._stream(
|
||||
messages=messages, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
return generate_from_stream(stream_iter)
|
||||
|
||||
res = self._chat(messages, **kwargs)
|
||||
if res.status_code != 200:
|
||||
raise ValueError(f"Error from Yi api response: {res}")
|
||||
response = res.json()
|
||||
return self._create_chat_result(response)
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
res = self._chat(messages, stream=True, **kwargs)
|
||||
if res.status_code != 200:
|
||||
raise ValueError(f"Error from Yi api response: {res}")
|
||||
default_chunk_class = AIMessageChunk
|
||||
for chunk in res.iter_lines():
|
||||
chunk = chunk.decode("utf-8").strip("\r\n")
|
||||
parts = chunk.split("data: ", 1)
|
||||
chunk = parts[1] if len(parts) > 1 else None
|
||||
if chunk is None:
|
||||
continue
|
||||
if chunk == "[DONE]":
|
||||
break
|
||||
response = json.loads(chunk)
|
||||
for m in response.get("choices"):
|
||||
chunk = _convert_delta_to_message_chunk(
|
||||
m.get("delta"), default_chunk_class
|
||||
)
|
||||
default_chunk_class = chunk.__class__
|
||||
cg_chunk = ChatGenerationChunk(message=chunk)
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(chunk.content, chunk=cg_chunk)
|
||||
yield cg_chunk
|
||||
|
||||
async def _agenerate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||
stream: Optional[bool] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
should_stream = stream if stream is not None else self.streaming
|
||||
if should_stream:
|
||||
stream_iter = self._astream(
|
||||
messages, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
return await agenerate_from_stream(stream_iter)
|
||||
|
||||
headers = self._create_headers_parameters(**kwargs)
|
||||
payload = self._create_payload_parameters(messages, **kwargs)
|
||||
|
||||
import httpx
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
headers=headers, timeout=self.request_timeout
|
||||
) as client:
|
||||
response = await client.post(self.yi_api_base, json=payload)
|
||||
response.raise_for_status()
|
||||
return self._create_chat_result(response.json())
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[ChatGenerationChunk]:
|
||||
headers = self._create_headers_parameters(**kwargs)
|
||||
payload = self._create_payload_parameters(messages, stream=True, **kwargs)
|
||||
import httpx
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
headers=headers, timeout=self.request_timeout
|
||||
) as client:
|
||||
async with aconnect_httpx_sse(
|
||||
client, "POST", self.yi_api_base, json=payload
|
||||
) as event_source:
|
||||
async for sse in event_source.aiter_sse():
|
||||
chunk = json.loads(sse.data)
|
||||
if len(chunk["choices"]) == 0:
|
||||
continue
|
||||
choice = chunk["choices"][0]
|
||||
chunk = _convert_delta_to_message_chunk(
|
||||
choice["delta"], AIMessageChunk
|
||||
)
|
||||
finish_reason = choice.get("finish_reason", None)
|
||||
|
||||
generation_info = (
|
||||
{"finish_reason": finish_reason}
|
||||
if finish_reason is not None
|
||||
else None
|
||||
)
|
||||
chunk = ChatGenerationChunk(
|
||||
message=chunk, generation_info=generation_info
|
||||
)
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
yield chunk
|
||||
if finish_reason is not None:
|
||||
break
|
||||
|
||||
def _chat(self, messages: List[BaseMessage], **kwargs: Any) -> requests.Response:
|
||||
payload = self._create_payload_parameters(messages, **kwargs)
|
||||
url = self.yi_api_base
|
||||
headers = self._create_headers_parameters(**kwargs)
|
||||
|
||||
res = requests.post(
|
||||
url=url,
|
||||
timeout=self.request_timeout,
|
||||
headers=headers,
|
||||
json=payload,
|
||||
stream=self.streaming,
|
||||
)
|
||||
return res
|
||||
|
||||
def _create_payload_parameters(
|
||||
self, messages: List[BaseMessage], **kwargs: Any
|
||||
) -> Dict[str, Any]:
|
||||
parameters = {**self._default_params, **kwargs}
|
||||
temperature = parameters.pop("temperature", 0.7)
|
||||
top_p = parameters.pop("top_p", 0.7)
|
||||
model = parameters.pop("model")
|
||||
stream = parameters.pop("stream", False)
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [_convert_message_to_dict(m) for m in messages],
|
||||
"top_p": top_p,
|
||||
"temperature": temperature,
|
||||
"stream": stream,
|
||||
}
|
||||
return payload
|
||||
|
||||
def _create_headers_parameters(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
parameters = {**self._default_params, **kwargs}
|
||||
default_headers = parameters.pop("headers", {})
|
||||
api_key = ""
|
||||
if self.yi_api_key:
|
||||
api_key = self.yi_api_key.get_secret_value()
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
**default_headers,
|
||||
}
|
||||
return headers
|
||||
|
||||
def _create_chat_result(self, response: Mapping[str, Any]) -> ChatResult:
|
||||
generations = []
|
||||
for c in response["choices"]:
|
||||
message = _convert_dict_to_message(c["message"])
|
||||
gen = ChatGeneration(message=message)
|
||||
generations.append(gen)
|
||||
|
||||
token_usage = response["usage"]
|
||||
llm_output = {"token_usage": token_usage, "model": self.model}
|
||||
return ChatResult(generations=generations, llm_output=llm_output)
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "yi-chat"
|
||||
@@ -26,8 +26,12 @@ class FlashrankRerank(BaseDocumentCompressor):
|
||||
"""Flashrank client to use for compressing documents"""
|
||||
top_n: int = 3
|
||||
"""Number of documents to return."""
|
||||
score_threshold: float = 0.0
|
||||
"""Minimum relevance threshold to return."""
|
||||
model: Optional[str] = None
|
||||
"""Model to use for reranking."""
|
||||
prefix_metadata: str = ""
|
||||
"""Prefix for flashrank_rerank metadata keys"""
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
@@ -69,11 +73,14 @@ class FlashrankRerank(BaseDocumentCompressor):
|
||||
final_results = []
|
||||
|
||||
for r in rerank_response:
|
||||
metadata = r["meta"]
|
||||
metadata["relevance_score"] = r["score"]
|
||||
doc = Document(
|
||||
page_content=r["text"],
|
||||
metadata=metadata,
|
||||
)
|
||||
final_results.append(doc)
|
||||
if r["score"] >= self.score_threshold:
|
||||
doc = Document(
|
||||
page_content=r["text"],
|
||||
metadata={
|
||||
self.prefix_metadata + "id": r["id"],
|
||||
self.prefix_metadata + "relevance_score": r["score"],
|
||||
**r["meta"],
|
||||
},
|
||||
)
|
||||
final_results.append(doc)
|
||||
return final_results
|
||||
|
||||
@@ -411,6 +411,9 @@ if TYPE_CHECKING:
|
||||
from langchain_community.document_loaders.scrapfly import (
|
||||
ScrapflyLoader,
|
||||
)
|
||||
from langchain_community.document_loaders.scrapingant import (
|
||||
ScrapingAntLoader,
|
||||
)
|
||||
from langchain_community.document_loaders.sharepoint import (
|
||||
SharePointLoader,
|
||||
)
|
||||
@@ -666,6 +669,7 @@ _module_lookup = {
|
||||
"S3DirectoryLoader": "langchain_community.document_loaders.s3_directory",
|
||||
"S3FileLoader": "langchain_community.document_loaders.s3_file",
|
||||
"ScrapflyLoader": "langchain_community.document_loaders.scrapfly",
|
||||
"ScrapingAntLoader": "langchain_community.document_loaders.scrapingant",
|
||||
"SQLDatabaseLoader": "langchain_community.document_loaders.sql_database",
|
||||
"SRTLoader": "langchain_community.document_loaders.srt",
|
||||
"SeleniumURLLoader": "langchain_community.document_loaders.url_selenium",
|
||||
@@ -870,6 +874,7 @@ __all__ = [
|
||||
"S3DirectoryLoader",
|
||||
"S3FileLoader",
|
||||
"ScrapflyLoader",
|
||||
"ScrapingAntLoader",
|
||||
"SQLDatabaseLoader",
|
||||
"SRTLoader",
|
||||
"SeleniumURLLoader",
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""ScrapingAnt Web Extractor."""
|
||||
|
||||
import logging
|
||||
from typing import Iterator, List, Optional
|
||||
|
||||
from langchain_core.document_loaders import BaseLoader
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.utils import get_from_env
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
class ScrapingAntLoader(BaseLoader):
|
||||
"""Turn an url to LLM accessible markdown with `ScrapingAnt`.
|
||||
|
||||
For further details, visit: https://docs.scrapingant.com/python-client
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
urls: List[str],
|
||||
*,
|
||||
api_key: Optional[str] = None,
|
||||
scrape_config: Optional[dict] = None,
|
||||
continue_on_failure: bool = True,
|
||||
) -> None:
|
||||
"""Initialize client.
|
||||
|
||||
Args:
|
||||
urls: List of urls to scrape.
|
||||
api_key: The ScrapingAnt API key. If not specified must have env var
|
||||
SCRAPINGANT_API_KEY set.
|
||||
scrape_config: The scraping config from ScrapingAntClient.markdown_request
|
||||
continue_on_failure: Whether to continue if scraping an url fails.
|
||||
"""
|
||||
try:
|
||||
from scrapingant_client import ScrapingAntClient
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"`scrapingant-client` package not found,"
|
||||
" run `pip install scrapingant-client`"
|
||||
)
|
||||
if not urls:
|
||||
raise ValueError("URLs must be provided.")
|
||||
api_key = api_key or get_from_env("api_key", "SCRAPINGANT_API_KEY")
|
||||
self.client = ScrapingAntClient(token=api_key)
|
||||
self.urls = urls
|
||||
self.scrape_config = scrape_config
|
||||
self.continue_on_failure = continue_on_failure
|
||||
|
||||
def lazy_load(self) -> Iterator[Document]:
|
||||
"""Fetch data from ScrapingAnt."""
|
||||
|
||||
scrape_config = self.scrape_config if self.scrape_config is not None else {}
|
||||
for url in self.urls:
|
||||
try:
|
||||
result = self.client.markdown_request(url=url, **scrape_config)
|
||||
yield Document(
|
||||
page_content=result.markdown,
|
||||
metadata={"url": result.url},
|
||||
)
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
logger.error(f"Error fetching data from {url}, exception: {e}")
|
||||
else:
|
||||
raise e
|
||||
@@ -1,14 +1,23 @@
|
||||
"""Loader that uses unstructured to load files."""
|
||||
|
||||
import collections
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import IO, Any, Callable, Dict, Iterator, List, Optional, Sequence, Union
|
||||
from typing import IO, Any, Callable, Iterator, List, Optional, Sequence, Union
|
||||
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
from langchain_core.documents import Document
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from langchain_community.document_loaders.base import BaseLoader
|
||||
|
||||
Element: TypeAlias = Any
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
def satisfies_min_unstructured_version(min_version: str) -> bool:
|
||||
"""Check if the installed `Unstructured` version exceeds the minimum version
|
||||
@@ -41,8 +50,8 @@ class UnstructuredBaseLoader(BaseLoader, ABC):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mode: str = "single",
|
||||
post_processors: Optional[List[Callable]] = None,
|
||||
mode: str = "single", # deprecated
|
||||
post_processors: Optional[List[Callable[[str], str]]] = None,
|
||||
**unstructured_kwargs: Any,
|
||||
):
|
||||
"""Initialize with file path."""
|
||||
@@ -53,32 +62,41 @@ class UnstructuredBaseLoader(BaseLoader, ABC):
|
||||
"unstructured package not found, please install it with "
|
||||
"`pip install unstructured`"
|
||||
)
|
||||
|
||||
# `single` - elements are combined into one (default)
|
||||
# `elements` - maintain individual elements
|
||||
# `paged` - elements are combined by page
|
||||
_valid_modes = {"single", "elements", "paged"}
|
||||
if mode not in _valid_modes:
|
||||
raise ValueError(
|
||||
f"Got {mode} for `mode`, but should be one of `{_valid_modes}`"
|
||||
)
|
||||
self.mode = mode
|
||||
|
||||
if not satisfies_min_unstructured_version("0.5.4"):
|
||||
if "strategy" in unstructured_kwargs:
|
||||
unstructured_kwargs.pop("strategy")
|
||||
|
||||
self._check_if_both_mode_and_chunking_strategy_are_by_page(
|
||||
mode, unstructured_kwargs
|
||||
)
|
||||
self.mode = mode
|
||||
self.unstructured_kwargs = unstructured_kwargs
|
||||
self.post_processors = post_processors or []
|
||||
|
||||
@abstractmethod
|
||||
def _get_elements(self) -> List:
|
||||
def _get_elements(self) -> List[Element]:
|
||||
"""Get elements."""
|
||||
|
||||
@abstractmethod
|
||||
def _get_metadata(self) -> dict:
|
||||
"""Get metadata."""
|
||||
def _get_metadata(self) -> dict[str, Any]:
|
||||
"""Get file_path metadata if available."""
|
||||
|
||||
def _post_process_elements(self, elements: list) -> list:
|
||||
"""Applies post processing functions to extracted unstructured elements.
|
||||
Post processing functions are str -> str callables are passed
|
||||
in using the post_processors kwarg when the loader is instantiated."""
|
||||
def _post_process_elements(self, elements: List[Element]) -> List[Element]:
|
||||
"""Apply post processing functions to extracted unstructured elements.
|
||||
|
||||
Post processing functions are str -> str callables passed
|
||||
in using the post_processors kwarg when the loader is instantiated.
|
||||
"""
|
||||
for element in elements:
|
||||
for post_processor in self.post_processors:
|
||||
element.apply(post_processor)
|
||||
@@ -97,18 +115,25 @@ class UnstructuredBaseLoader(BaseLoader, ABC):
|
||||
metadata.update(element.metadata.to_dict())
|
||||
if hasattr(element, "category"):
|
||||
metadata["category"] = element.category
|
||||
if element.to_dict().get("element_id"):
|
||||
metadata["element_id"] = element.to_dict().get("element_id")
|
||||
yield Document(page_content=str(element), metadata=metadata)
|
||||
elif self.mode == "paged":
|
||||
text_dict: Dict[int, str] = {}
|
||||
meta_dict: Dict[int, Dict] = {}
|
||||
logger.warning(
|
||||
"`mode='paged'` is deprecated in favor of the 'by_page' chunking"
|
||||
" strategy. Learn more about chunking here:"
|
||||
" https://docs.unstructured.io/open-source/core-functionality/chunking"
|
||||
)
|
||||
text_dict: dict[int, str] = {}
|
||||
meta_dict: dict[int, dict[str, Any]] = {}
|
||||
|
||||
for idx, element in enumerate(elements):
|
||||
for element in elements:
|
||||
metadata = self._get_metadata()
|
||||
if hasattr(element, "metadata"):
|
||||
metadata.update(element.metadata.to_dict())
|
||||
page_number = metadata.get("page_number", 1)
|
||||
|
||||
# Check if this page_number already exists in docs_dict
|
||||
# Check if this page_number already exists in text_dict
|
||||
if page_number not in text_dict:
|
||||
# If not, create new entry with initial text and metadata
|
||||
text_dict[page_number] = str(element) + "\n\n"
|
||||
@@ -128,18 +153,37 @@ class UnstructuredBaseLoader(BaseLoader, ABC):
|
||||
else:
|
||||
raise ValueError(f"mode of {self.mode} not supported.")
|
||||
|
||||
def _check_if_both_mode_and_chunking_strategy_are_by_page(
|
||||
self, mode: str, unstructured_kwargs: dict[str, Any]
|
||||
) -> None:
|
||||
if (
|
||||
mode == "paged"
|
||||
and unstructured_kwargs.get("chunking_strategy") == "by_page"
|
||||
):
|
||||
raise ValueError(
|
||||
"Only one of `chunking_strategy='by_page'` or `mode='paged'` may be"
|
||||
" set. `chunking_strategy` is preferred."
|
||||
)
|
||||
|
||||
|
||||
@deprecated(
|
||||
since="0.2.8",
|
||||
removal="0.4.0",
|
||||
alternative_import="langchain_unstructured.UnstructuredLoader",
|
||||
)
|
||||
class UnstructuredFileLoader(UnstructuredBaseLoader):
|
||||
"""Load files using `Unstructured`.
|
||||
|
||||
The file loader uses the
|
||||
unstructured partition function and will automatically detect the file
|
||||
type. You can run the loader in one of two modes: "single" and "elements".
|
||||
If you use "single" mode, the document will be returned as a single
|
||||
langchain Document object. If you use "elements" mode, the unstructured
|
||||
library will split the document into elements such as Title and NarrativeText.
|
||||
You can pass in additional unstructured kwargs after mode to apply
|
||||
different unstructured settings.
|
||||
The file loader uses the unstructured partition function and will automatically
|
||||
detect the file type. You can run the loader in different modes: "single",
|
||||
"elements", and "paged". The default "single" mode will return a single langchain
|
||||
Document object. If you use "elements" mode, the unstructured library will split
|
||||
the document into elements such as Title and NarrativeText and return those as
|
||||
individual langchain Document objects. In addition to these post-processing modes
|
||||
(which are specific to the LangChain Loaders), Unstructured has its own "chunking"
|
||||
parameters for post-processing elements into more useful chunks for uses cases such
|
||||
as Retrieval Augmented Generation (RAG). You can pass in additional unstructured
|
||||
kwargs to configure different unstructured settings.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -152,24 +196,27 @@ class UnstructuredFileLoader(UnstructuredBaseLoader):
|
||||
|
||||
References
|
||||
----------
|
||||
https://unstructured-io.github.io/unstructured/bricks.html#partition
|
||||
https://docs.unstructured.io/open-source/core-functionality/partitioning
|
||||
https://docs.unstructured.io/open-source/core-functionality/chunking
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file_path: Union[str, List[str], Path, List[Path], None],
|
||||
file_path: Union[str, List[str], Path, List[Path]],
|
||||
*,
|
||||
mode: str = "single",
|
||||
**unstructured_kwargs: Any,
|
||||
):
|
||||
"""Initialize with file path."""
|
||||
self.file_path = file_path
|
||||
|
||||
super().__init__(mode=mode, **unstructured_kwargs)
|
||||
|
||||
def _get_elements(self) -> List:
|
||||
def _get_elements(self) -> List[Element]:
|
||||
from unstructured.partition.auto import partition
|
||||
|
||||
if isinstance(self.file_path, list):
|
||||
elements = []
|
||||
elements: List[Element] = []
|
||||
for file in self.file_path:
|
||||
if isinstance(file, Path):
|
||||
file = str(file)
|
||||
@@ -180,35 +227,33 @@ class UnstructuredFileLoader(UnstructuredBaseLoader):
|
||||
self.file_path = str(self.file_path)
|
||||
return partition(filename=self.file_path, **self.unstructured_kwargs)
|
||||
|
||||
def _get_metadata(self) -> dict:
|
||||
def _get_metadata(self) -> dict[str, Any]:
|
||||
return {"source": self.file_path}
|
||||
|
||||
|
||||
def get_elements_from_api(
|
||||
file_path: Union[str, List[str], Path, List[Path], None] = None,
|
||||
file: Union[IO, Sequence[IO], None] = None,
|
||||
api_url: str = "https://api.unstructured.io/general/v0/general",
|
||||
file: Union[IO[bytes], Sequence[IO[bytes]], None] = None,
|
||||
api_url: str = "https://api.unstructuredapp.io/general/v0/general",
|
||||
api_key: str = "",
|
||||
**unstructured_kwargs: Any,
|
||||
) -> List:
|
||||
) -> List[Element]:
|
||||
"""Retrieve a list of elements from the `Unstructured API`."""
|
||||
if is_list := isinstance(file_path, list):
|
||||
file_path = [str(path) for path in file_path]
|
||||
if isinstance(file, collections.abc.Sequence) or is_list:
|
||||
if isinstance(file, Sequence) or is_list:
|
||||
from unstructured.partition.api import partition_multiple_via_api
|
||||
|
||||
_doc_elements = partition_multiple_via_api(
|
||||
filenames=file_path,
|
||||
files=file,
|
||||
filenames=file_path, # type: ignore
|
||||
files=file, # type: ignore
|
||||
api_key=api_key,
|
||||
api_url=api_url,
|
||||
**unstructured_kwargs,
|
||||
)
|
||||
|
||||
elements = []
|
||||
for _elements in _doc_elements:
|
||||
elements.extend(_elements)
|
||||
|
||||
return elements
|
||||
else:
|
||||
from unstructured.partition.api import partition_via_api
|
||||
@@ -222,59 +267,69 @@ def get_elements_from_api(
|
||||
)
|
||||
|
||||
|
||||
class UnstructuredAPIFileLoader(UnstructuredFileLoader):
|
||||
@deprecated(
|
||||
since="0.2.8",
|
||||
removal="0.4.0",
|
||||
alternative_import="langchain_unstructured.UnstructuredLoader",
|
||||
)
|
||||
class UnstructuredAPIFileLoader(UnstructuredBaseLoader):
|
||||
"""Load files using `Unstructured` API.
|
||||
|
||||
By default, the loader makes a call to the hosted Unstructured API.
|
||||
If you are running the unstructured API locally, you can change the
|
||||
API rule by passing in the url parameter when you initialize the loader.
|
||||
The hosted Unstructured API requires an API key. See
|
||||
https://www.unstructured.io/api-key/ if you need to generate a key.
|
||||
By default, the loader makes a call to the hosted Unstructured API. If you are
|
||||
running the unstructured API locally, you can change the API rule by passing in the
|
||||
url parameter when you initialize the loader. The hosted Unstructured API requires
|
||||
an API key. See the links below to learn more about our API offerings and get an
|
||||
API key.
|
||||
|
||||
You can run the loader in one of two modes: "single" and "elements".
|
||||
If you use "single" mode, the document will be returned as a single
|
||||
langchain Document object. If you use "elements" mode, the unstructured
|
||||
library will split the document into elements such as Title and NarrativeText.
|
||||
You can pass in additional unstructured kwargs after mode to apply
|
||||
different unstructured settings.
|
||||
You can run the loader in different modes: "single", "elements", and "paged". The
|
||||
default "single" mode will return a single langchain Document object. If you use
|
||||
"elements" mode, the unstructured library will split the document into elements such
|
||||
as Title and NarrativeText and return those as individual langchain Document
|
||||
objects. In addition to these post-processing modes (which are specific to the
|
||||
LangChain Loaders), Unstructured has its own "chunking" parameters for
|
||||
post-processing elements into more useful chunks for uses cases such as Retrieval
|
||||
Augmented Generation (RAG). You can pass in additional unstructured kwargs to
|
||||
configure different unstructured settings.
|
||||
|
||||
Examples
|
||||
```python
|
||||
from langchain_community.document_loaders import UnstructuredAPIFileLoader
|
||||
|
||||
loader = UnstructuredFileAPILoader(
|
||||
loader = UnstructuredAPIFileLoader(
|
||||
"example.pdf", mode="elements", strategy="fast", api_key="MY_API_KEY",
|
||||
)
|
||||
docs = loader.load()
|
||||
|
||||
References
|
||||
----------
|
||||
https://unstructured-io.github.io/unstructured/bricks.html#partition
|
||||
https://www.unstructured.io/api-key/
|
||||
https://github.com/Unstructured-IO/unstructured-api
|
||||
https://docs.unstructured.io/api-reference/api-services/sdk
|
||||
https://docs.unstructured.io/api-reference/api-services/overview
|
||||
https://docs.unstructured.io/open-source/core-functionality/partitioning
|
||||
https://docs.unstructured.io/open-source/core-functionality/chunking
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file_path: Union[str, List[str], None] = "",
|
||||
file_path: Union[str, List[str]],
|
||||
*,
|
||||
mode: str = "single",
|
||||
url: str = "https://api.unstructured.io/general/v0/general",
|
||||
url: str = "https://api.unstructuredapp.io/general/v0/general",
|
||||
api_key: str = "",
|
||||
**unstructured_kwargs: Any,
|
||||
):
|
||||
"""Initialize with file path."""
|
||||
|
||||
validate_unstructured_version(min_unstructured_version="0.10.15")
|
||||
|
||||
self.file_path = file_path
|
||||
self.url = url
|
||||
self.api_key = api_key
|
||||
self.api_key = os.getenv("UNSTRUCTURED_API_KEY") or api_key
|
||||
|
||||
super().__init__(file_path=file_path, mode=mode, **unstructured_kwargs)
|
||||
super().__init__(mode=mode, **unstructured_kwargs)
|
||||
|
||||
def _get_metadata(self) -> dict:
|
||||
def _get_metadata(self) -> dict[str, Any]:
|
||||
return {"source": self.file_path}
|
||||
|
||||
def _get_elements(self) -> List:
|
||||
def _get_elements(self) -> List[Element]:
|
||||
return get_elements_from_api(
|
||||
file_path=self.file_path,
|
||||
api_key=self.api_key,
|
||||
@@ -282,18 +337,36 @@ class UnstructuredAPIFileLoader(UnstructuredFileLoader):
|
||||
**self.unstructured_kwargs,
|
||||
)
|
||||
|
||||
def _post_process_elements(self, elements: List[Element]) -> List[Element]:
|
||||
"""Apply post processing functions to extracted unstructured elements.
|
||||
|
||||
Post processing functions are str -> str callables passed
|
||||
in using the post_processors kwarg when the loader is instantiated.
|
||||
"""
|
||||
for element in elements:
|
||||
for post_processor in self.post_processors:
|
||||
element.apply(post_processor)
|
||||
return elements
|
||||
|
||||
|
||||
@deprecated(
|
||||
since="0.2.8",
|
||||
removal="0.4.0",
|
||||
alternative_import="langchain_unstructured.UnstructuredLoader",
|
||||
)
|
||||
class UnstructuredFileIOLoader(UnstructuredBaseLoader):
|
||||
"""Load files using `Unstructured`.
|
||||
"""Load file-like objects opened in read mode using `Unstructured`.
|
||||
|
||||
The file loader
|
||||
uses the unstructured partition function and will automatically detect the file
|
||||
type. You can run the loader in one of two modes: "single" and "elements".
|
||||
If you use "single" mode, the document will be returned as a single
|
||||
langchain Document object. If you use "elements" mode, the unstructured
|
||||
library will split the document into elements such as Title and NarrativeText.
|
||||
You can pass in additional unstructured kwargs after mode to apply
|
||||
different unstructured settings.
|
||||
The file loader uses the unstructured partition function and will automatically
|
||||
detect the file type. You can run the loader in different modes: "single",
|
||||
"elements", and "paged". The default "single" mode will return a single langchain
|
||||
Document object. If you use "elements" mode, the unstructured library will split
|
||||
the document into elements such as Title and NarrativeText and return those as
|
||||
individual langchain Document objects. In addition to these post-processing modes
|
||||
(which are specific to the LangChain Loaders), Unstructured has its own "chunking"
|
||||
parameters for post-processing elements into more useful chunks for uses cases
|
||||
such as Retrieval Augmented Generation (RAG). You can pass in additional
|
||||
unstructured kwargs to configure different unstructured settings.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -308,12 +381,14 @@ class UnstructuredFileIOLoader(UnstructuredBaseLoader):
|
||||
|
||||
References
|
||||
----------
|
||||
https://unstructured-io.github.io/unstructured/bricks.html#partition
|
||||
https://docs.unstructured.io/open-source/core-functionality/partitioning
|
||||
https://docs.unstructured.io/open-source/core-functionality/chunking
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file: Union[IO, Sequence[IO]],
|
||||
file: IO[bytes],
|
||||
*,
|
||||
mode: str = "single",
|
||||
**unstructured_kwargs: Any,
|
||||
):
|
||||
@@ -321,72 +396,114 @@ class UnstructuredFileIOLoader(UnstructuredBaseLoader):
|
||||
self.file = file
|
||||
super().__init__(mode=mode, **unstructured_kwargs)
|
||||
|
||||
def _get_elements(self) -> List:
|
||||
def _get_elements(self) -> List[Element]:
|
||||
from unstructured.partition.auto import partition
|
||||
|
||||
return partition(file=self.file, **self.unstructured_kwargs)
|
||||
|
||||
def _get_metadata(self) -> dict:
|
||||
def _get_metadata(self) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
def _post_process_elements(self, elements: List[Element]) -> List[Element]:
|
||||
"""Apply post processing functions to extracted unstructured elements.
|
||||
|
||||
class UnstructuredAPIFileIOLoader(UnstructuredFileIOLoader):
|
||||
"""Load files using `Unstructured` API.
|
||||
Post processing functions are str -> str callables passed
|
||||
in using the post_processors kwarg when the loader is instantiated.
|
||||
"""
|
||||
for element in elements:
|
||||
for post_processor in self.post_processors:
|
||||
element.apply(post_processor)
|
||||
return elements
|
||||
|
||||
By default, the loader makes a call to the hosted Unstructured API.
|
||||
If you are running the unstructured API locally, you can change the
|
||||
API rule by passing in the url parameter when you initialize the loader.
|
||||
The hosted Unstructured API requires an API key. See
|
||||
https://www.unstructured.io/api-key/ if you need to generate a key.
|
||||
|
||||
You can run the loader in one of two modes: "single" and "elements".
|
||||
If you use "single" mode, the document will be returned as a single
|
||||
langchain Document object. If you use "elements" mode, the unstructured
|
||||
library will split the document into elements such as Title and NarrativeText.
|
||||
You can pass in additional unstructured kwargs after mode to apply
|
||||
different unstructured settings.
|
||||
@deprecated(
|
||||
since="0.2.8",
|
||||
removal="0.4.0",
|
||||
alternative_import="langchain_unstructured.UnstructuredLoader",
|
||||
)
|
||||
class UnstructuredAPIFileIOLoader(UnstructuredBaseLoader):
|
||||
"""Send file-like objects with `unstructured-client` sdk to the Unstructured API.
|
||||
|
||||
By default, the loader makes a call to the hosted Unstructured API. If you are
|
||||
running the unstructured API locally, you can change the API rule by passing in the
|
||||
url parameter when you initialize the loader. The hosted Unstructured API requires
|
||||
an API key. See the links below to learn more about our API offerings and get an
|
||||
API key.
|
||||
|
||||
You can run the loader in different modes: "single", "elements", and "paged". The
|
||||
default "single" mode will return a single langchain Document object. If you use
|
||||
"elements" mode, the unstructured library will split the document into elements
|
||||
such as Title and NarrativeText and return those as individual langchain Document
|
||||
objects. In addition to these post-processing modes (which are specific to the
|
||||
LangChain Loaders), Unstructured has its own "chunking" parameters for
|
||||
post-processing elements into more useful chunks for uses cases such as Retrieval
|
||||
Augmented Generation (RAG). You can pass in additional unstructured kwargs to
|
||||
configure different unstructured settings.
|
||||
|
||||
Examples
|
||||
--------
|
||||
from langchain_community.document_loaders import UnstructuredAPIFileLoader
|
||||
|
||||
with open("example.pdf", "rb") as f:
|
||||
loader = UnstructuredFileAPILoader(
|
||||
loader = UnstructuredAPIFileIOLoader(
|
||||
f, mode="elements", strategy="fast", api_key="MY_API_KEY",
|
||||
)
|
||||
docs = loader.load()
|
||||
|
||||
References
|
||||
----------
|
||||
https://unstructured-io.github.io/unstructured/bricks.html#partition
|
||||
https://www.unstructured.io/api-key/
|
||||
https://github.com/Unstructured-IO/unstructured-api
|
||||
https://docs.unstructured.io/api-reference/api-services/sdk
|
||||
https://docs.unstructured.io/api-reference/api-services/overview
|
||||
https://docs.unstructured.io/open-source/core-functionality/partitioning
|
||||
https://docs.unstructured.io/open-source/core-functionality/chunking
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file: Union[IO, Sequence[IO]],
|
||||
file: Union[IO[bytes], Sequence[IO[bytes]]],
|
||||
*,
|
||||
mode: str = "single",
|
||||
url: str = "https://api.unstructured.io/general/v0/general",
|
||||
url: str = "https://api.unstructuredapp.io/general/v0/general",
|
||||
api_key: str = "",
|
||||
**unstructured_kwargs: Any,
|
||||
):
|
||||
"""Initialize with file path."""
|
||||
|
||||
if isinstance(file, collections.abc.Sequence):
|
||||
if isinstance(file, Sequence):
|
||||
validate_unstructured_version(min_unstructured_version="0.6.3")
|
||||
if file:
|
||||
validate_unstructured_version(min_unstructured_version="0.6.2")
|
||||
validate_unstructured_version(min_unstructured_version="0.6.2")
|
||||
|
||||
self.file = file
|
||||
self.url = url
|
||||
self.api_key = api_key
|
||||
self.api_key = os.getenv("UNSTRUCTURED_API_KEY") or api_key
|
||||
|
||||
super().__init__(file=file, mode=mode, **unstructured_kwargs)
|
||||
super().__init__(mode=mode, **unstructured_kwargs)
|
||||
|
||||
def _get_elements(self) -> List:
|
||||
return get_elements_from_api(
|
||||
file=self.file,
|
||||
api_key=self.api_key,
|
||||
api_url=self.url,
|
||||
**self.unstructured_kwargs,
|
||||
)
|
||||
def _get_elements(self) -> List[Element]:
|
||||
if self.unstructured_kwargs.get("metadata_filename"):
|
||||
return get_elements_from_api(
|
||||
file=self.file,
|
||||
file_path=self.unstructured_kwargs.pop("metadata_filename"),
|
||||
api_key=self.api_key,
|
||||
api_url=self.url,
|
||||
**self.unstructured_kwargs,
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"If partitioning a file via api,"
|
||||
" metadata_filename must be specified as well.",
|
||||
)
|
||||
|
||||
def _get_metadata(self) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
def _post_process_elements(self, elements: List[Element]) -> List[Element]:
|
||||
"""Apply post processing functions to extracted unstructured elements.
|
||||
|
||||
Post processing functions are str -> str callables passed
|
||||
in using the post_processors kwarg when the loader is instantiated.
|
||||
"""
|
||||
for element in elements:
|
||||
for post_processor in self.post_processors:
|
||||
element.apply(post_processor)
|
||||
return elements
|
||||
|
||||
@@ -25,19 +25,34 @@ BAICHUAN_API_URL: str = "http://api.baichuan-ai.com/v1/embeddings"
|
||||
class BaichuanTextEmbeddings(BaseModel, Embeddings):
|
||||
"""Baichuan Text Embedding models.
|
||||
|
||||
To use, you should set the environment variable ``BAICHUAN_API_KEY`` to
|
||||
your API key or pass it as a named parameter to the constructor.
|
||||
Setup:
|
||||
To use, you should set the environment variable ``BAICHUAN_API_KEY`` to
|
||||
your API key or pass it as a named parameter to the constructor.
|
||||
|
||||
Example:
|
||||
.. code-block:: bash
|
||||
|
||||
export BAICHUAN_API_KEY="your-api-key"
|
||||
|
||||
Instantiate:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.embeddings import BaichuanTextEmbeddings
|
||||
|
||||
baichuan = BaichuanTextEmbeddings(baichuan_api_key="my-api-key")
|
||||
"""
|
||||
embeddings = BaichuanTextEmbeddings()
|
||||
|
||||
Embed:
|
||||
.. code-block:: python
|
||||
|
||||
# embed the documents
|
||||
vectors = embeddings.embed_documents([text1, text2, ...])
|
||||
|
||||
# embed the query
|
||||
vectors = embeddings.embed_query(text)
|
||||
""" # noqa: E501
|
||||
|
||||
session: Any #: :meta private:
|
||||
model_name: str = Field(default="Baichuan-Text-Embedding", alias="model")
|
||||
"""The model used to embed the documents."""
|
||||
baichuan_api_key: Optional[SecretStr] = Field(default=None, alias="api_key")
|
||||
"""Automatically inferred from env var `BAICHUAN_API_KEY` if not provided."""
|
||||
chunk_size: int = 16
|
||||
|
||||
@@ -48,8 +48,11 @@ def embed_with_retry(embeddings: DashScopeEmbeddings, **kwargs: Any) -> Any:
|
||||
result = []
|
||||
i = 0
|
||||
input_data = kwargs["input"]
|
||||
while i < len(input_data):
|
||||
kwargs["input"] = input_data[i : i + 25]
|
||||
input_len = len(input_data) if isinstance(input_data, list) else 1
|
||||
while i < input_len:
|
||||
kwargs["input"] = (
|
||||
input_data[i : i + 25] if isinstance(input_data, list) else input_data
|
||||
)
|
||||
resp = embeddings.client.call(**kwargs)
|
||||
if resp.status_code == 200:
|
||||
result += resp.output["embeddings"]
|
||||
|
||||
@@ -640,12 +640,6 @@ def _import_yuan2() -> Type[BaseLLM]:
|
||||
return Yuan2
|
||||
|
||||
|
||||
def _import_you() -> Type[BaseLLM]:
|
||||
from langchain_community.llms.you import You
|
||||
|
||||
return You
|
||||
|
||||
|
||||
def _import_volcengine_maas() -> Type[BaseLLM]:
|
||||
from langchain_community.llms.volcengine_maas import VolcEngineMaasLLM
|
||||
|
||||
@@ -658,6 +652,18 @@ def _import_sparkllm() -> Type[BaseLLM]:
|
||||
return SparkLLM
|
||||
|
||||
|
||||
def _import_you() -> Type[BaseLLM]:
|
||||
from langchain_community.llms.you import You
|
||||
|
||||
return You
|
||||
|
||||
|
||||
def _import_yi() -> Type[BaseLLM]:
|
||||
from langchain_community.llms.yi import YiLLM
|
||||
|
||||
return YiLLM
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if name == "AI21":
|
||||
return _import_ai21()
|
||||
@@ -853,18 +859,20 @@ def __getattr__(name: str) -> Any:
|
||||
return _import_yandex_gpt()
|
||||
elif name == "Yuan2":
|
||||
return _import_yuan2()
|
||||
elif name == "You":
|
||||
return _import_you()
|
||||
elif name == "VolcEngineMaasLLM":
|
||||
return _import_volcengine_maas()
|
||||
elif name == "SparkLLM":
|
||||
return _import_sparkllm()
|
||||
elif name == "YiLLM":
|
||||
return _import_yi()
|
||||
elif name == "You":
|
||||
return _import_you()
|
||||
elif name == "type_to_cls_dict":
|
||||
# for backwards compatibility
|
||||
type_to_cls_dict: Dict[str, Type[BaseLLM]] = {
|
||||
k: v() for k, v in get_type_to_cls_dict().items()
|
||||
}
|
||||
return type_to_cls_dict
|
||||
elif name == "SparkLLM":
|
||||
return _import_sparkllm()
|
||||
else:
|
||||
raise AttributeError(f"Could not find: {name}")
|
||||
|
||||
@@ -967,8 +975,9 @@ __all__ = [
|
||||
"Writer",
|
||||
"Xinference",
|
||||
"YandexGPT",
|
||||
"You",
|
||||
"Yuan2",
|
||||
"YiLLM",
|
||||
"You",
|
||||
]
|
||||
|
||||
|
||||
@@ -1065,7 +1074,8 @@ def get_type_to_cls_dict() -> Dict[str, Callable[[], Type[BaseLLM]]]:
|
||||
"qianfan_endpoint": _import_baidu_qianfan_endpoint,
|
||||
"yandex_gpt": _import_yandex_gpt,
|
||||
"yuan2": _import_yuan2,
|
||||
"you": _import_you,
|
||||
"VolcEngineMaasLLM": _import_volcengine_maas,
|
||||
"SparkLLM": _import_sparkllm,
|
||||
"yi": _import_yi,
|
||||
"you": _import_you,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, List, Mapping, Optional, Union
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
import aiohttp
|
||||
import requests
|
||||
@@ -132,6 +145,10 @@ class _OllamaCommon(BaseLanguageModel):
|
||||
tokens for authentication.
|
||||
"""
|
||||
|
||||
auth: Union[Callable, Tuple, None] = None
|
||||
"""Additional auth tuple or callable to enable Basic/Digest/Custom HTTP Auth.
|
||||
Expects the same format, type and values as requests.request auth parameter."""
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Dict[str, Any]:
|
||||
"""Get the default parameters for calling Ollama."""
|
||||
@@ -237,6 +254,7 @@ class _OllamaCommon(BaseLanguageModel):
|
||||
"Content-Type": "application/json",
|
||||
**(self.headers if isinstance(self.headers, dict) else {}),
|
||||
},
|
||||
auth=self.auth,
|
||||
json=request_payload,
|
||||
stream=True,
|
||||
timeout=self.timeout,
|
||||
@@ -300,6 +318,7 @@ class _OllamaCommon(BaseLanguageModel):
|
||||
"Content-Type": "application/json",
|
||||
**(self.headers if isinstance(self.headers, dict) else {}),
|
||||
},
|
||||
auth=self.auth,
|
||||
json=request_payload,
|
||||
timeout=self.timeout,
|
||||
) as response:
|
||||
|
||||
104
libs/community/langchain_community/llms/yi.py
Normal file
104
libs/community/langchain_community/llms/yi.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
import requests
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.language_models.llms import LLM
|
||||
from langchain_core.pydantic_v1 import Field, SecretStr
|
||||
from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env
|
||||
|
||||
from langchain_community.llms.utils import enforce_stop_tokens
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class YiLLM(LLM):
|
||||
"""Yi large language models."""
|
||||
|
||||
model: str = "yi-large"
|
||||
temperature: float = 0.3
|
||||
top_p: float = 0.95
|
||||
timeout: int = 60
|
||||
model_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
yi_api_key: Optional[SecretStr] = None
|
||||
region: Literal["auto", "domestic", "international"] = "auto"
|
||||
yi_api_url_domestic: str = "https://api.lingyiwanwu.com/v1/chat/completions"
|
||||
yi_api_url_international: str = "https://api.01.ai/v1/chat/completions"
|
||||
|
||||
def __init__(self, **kwargs: Any):
|
||||
kwargs["yi_api_key"] = convert_to_secret_str(
|
||||
get_from_dict_or_env(kwargs, "yi_api_key", "YI_API_KEY")
|
||||
)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
**self.model_kwargs,
|
||||
}
|
||||
|
||||
def _post(self, request: Any) -> Any:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.yi_api_key.get_secret_value()}", # type: ignore
|
||||
}
|
||||
|
||||
urls = []
|
||||
if self.region == "domestic":
|
||||
urls = [self.yi_api_url_domestic]
|
||||
elif self.region == "international":
|
||||
urls = [self.yi_api_url_international]
|
||||
else: # auto
|
||||
urls = [self.yi_api_url_domestic, self.yi_api_url_international]
|
||||
|
||||
for url in urls:
|
||||
try:
|
||||
response = requests.post(
|
||||
url,
|
||||
headers=headers,
|
||||
json=request,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
parsed_json = json.loads(response.text)
|
||||
return parsed_json["choices"][0]["message"]["content"]
|
||||
elif (
|
||||
response.status_code != 403
|
||||
): # If not a permission error, raise immediately
|
||||
response.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
if url == urls[-1]: # If this is the last URL to try
|
||||
raise ValueError(f"An error has occurred: {e}")
|
||||
else:
|
||||
logger.warning(f"Failed to connect to {url}, trying next URL")
|
||||
continue
|
||||
|
||||
raise ValueError("Failed to connect to all available URLs")
|
||||
|
||||
def _call(
|
||||
self,
|
||||
prompt: str,
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
request = self._default_params
|
||||
request["messages"] = [{"role": "user", "content": prompt}]
|
||||
request.update(kwargs)
|
||||
text = self._post(request)
|
||||
if stop is not None:
|
||||
text = enforce_stop_tokens(text, stop)
|
||||
return text
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
"""Return type of chat_model."""
|
||||
return "yi-llm"
|
||||
@@ -0,0 +1,57 @@
|
||||
# HANA Translator/query constructor
|
||||
from typing import Dict, Tuple, Union
|
||||
|
||||
from langchain_core.structured_query import (
|
||||
Comparator,
|
||||
Comparison,
|
||||
Operation,
|
||||
Operator,
|
||||
StructuredQuery,
|
||||
Visitor,
|
||||
)
|
||||
|
||||
|
||||
class HanaTranslator(Visitor):
|
||||
"""
|
||||
Translate internal query language elements to valid filters params for
|
||||
HANA vectorstore.
|
||||
"""
|
||||
|
||||
allowed_operators = [Operator.AND, Operator.OR]
|
||||
"""Subset of allowed logical operators."""
|
||||
allowed_comparators = [
|
||||
Comparator.EQ,
|
||||
Comparator.NE,
|
||||
Comparator.GT,
|
||||
Comparator.LT,
|
||||
Comparator.GTE,
|
||||
Comparator.LTE,
|
||||
Comparator.IN,
|
||||
Comparator.NIN,
|
||||
# Comparator.CONTAIN,
|
||||
Comparator.LIKE,
|
||||
]
|
||||
|
||||
def _format_func(self, func: Union[Operator, Comparator]) -> str:
|
||||
self._validate_func(func)
|
||||
return f"${func.value}"
|
||||
|
||||
def visit_operation(self, operation: Operation) -> Dict:
|
||||
args = [arg.accept(self) for arg in operation.arguments]
|
||||
return {self._format_func(operation.operator): args}
|
||||
|
||||
def visit_comparison(self, comparison: Comparison) -> Dict:
|
||||
return {
|
||||
comparison.attribute: {
|
||||
self._format_func(comparison.comparator): comparison.value
|
||||
}
|
||||
}
|
||||
|
||||
def visit_structured_query(
|
||||
self, structured_query: StructuredQuery
|
||||
) -> Tuple[str, dict]:
|
||||
if structured_query.filter is None:
|
||||
kwargs = {}
|
||||
else:
|
||||
kwargs = {"filter": structured_query.filter.accept(self)}
|
||||
return structured_query.query, kwargs
|
||||
@@ -122,6 +122,7 @@ class WebResearchRetriever(BaseRetriever):
|
||||
chunk_size=1500, chunk_overlap=150
|
||||
),
|
||||
trust_env: bool = False,
|
||||
allow_dangerous_requests: bool = False,
|
||||
) -> "WebResearchRetriever":
|
||||
"""Initialize from llm using default template.
|
||||
|
||||
@@ -162,6 +163,7 @@ class WebResearchRetriever(BaseRetriever):
|
||||
num_search_results=num_search_results,
|
||||
text_splitter=text_splitter,
|
||||
trust_env=trust_env,
|
||||
allow_dangerous_requests=allow_dangerous_requests,
|
||||
)
|
||||
|
||||
def clean_search_query(self, query: str) -> str:
|
||||
|
||||
@@ -3,17 +3,21 @@ from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Type
|
||||
|
||||
import requests
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import validator
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, HttpUrl, validator
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SpeechToTextInput(BaseModel):
|
||||
query: HttpUrl = Field(description="url of the audio to analyze")
|
||||
|
||||
|
||||
class EdenAiSpeechToTextTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Speech To Text API.
|
||||
|
||||
@@ -23,7 +27,6 @@ class EdenAiSpeechToTextTool(EdenaiTool):
|
||||
To use, you should have
|
||||
the environment variable ``EDENAI_API_KEY`` set with your API token.
|
||||
You can find your token here: https://app.edenai.run/admin/account/settings
|
||||
|
||||
"""
|
||||
|
||||
edenai_api_key: Optional[str] = None
|
||||
@@ -34,6 +37,7 @@ class EdenAiSpeechToTextTool(EdenaiTool):
|
||||
"Useful for when you have to convert audio to text."
|
||||
"Input should be a url to an audio file."
|
||||
)
|
||||
args_schema: Type[BaseModel] = SpeechToTextInput
|
||||
is_async: bool = True
|
||||
|
||||
language: Optional[str] = "en"
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Literal, Optional
|
||||
from typing import Dict, List, Literal, Optional, Type
|
||||
|
||||
import requests
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import Field, root_validator, validator
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, root_validator, validator
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TextToSpeechInput(BaseModel):
|
||||
query: str = Field(description="text to generate audio from")
|
||||
|
||||
|
||||
class EdenAiTextToSpeechTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Text to speech API.
|
||||
for api reference check edenai documentation:
|
||||
@@ -30,6 +34,7 @@ class EdenAiTextToSpeechTool(EdenaiTool):
|
||||
"""the output is a string representing the URL of the audio file,
|
||||
or the path to the downloaded wav file """
|
||||
)
|
||||
args_schema: Type[BaseModel] = TextToSpeechInput
|
||||
|
||||
language: Optional[str] = "en"
|
||||
"""
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, HttpUrl
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ExplicitImageInput(BaseModel):
|
||||
query: HttpUrl = Field(description="url of the image to analyze")
|
||||
|
||||
|
||||
class EdenAiExplicitImageTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Explicit image detection.
|
||||
|
||||
@@ -33,6 +38,7 @@ class EdenAiExplicitImageTool(EdenaiTool):
|
||||
pornography, violence, gore content, etc."""
|
||||
"Input should be the string url of the image ."
|
||||
)
|
||||
args_schema: Type[BaseModel] = ExplicitImageInput
|
||||
|
||||
combine_available: bool = True
|
||||
feature: str = "image"
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, HttpUrl
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ObjectDetectionInput(BaseModel):
|
||||
query: HttpUrl = Field(description="url of the image to analyze")
|
||||
|
||||
|
||||
class EdenAiObjectDetectionTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Object detection API.
|
||||
|
||||
@@ -30,6 +35,7 @@ class EdenAiObjectDetectionTool(EdenaiTool):
|
||||
(with bounding boxes) objects in an image """
|
||||
"Input should be the string url of the image to identify."
|
||||
)
|
||||
args_schema: Type[BaseModel] = ObjectDetectionInput
|
||||
|
||||
show_positions: bool = False
|
||||
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, HttpUrl
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IDParsingInput(BaseModel):
|
||||
query: HttpUrl = Field(description="url of the document to parse")
|
||||
|
||||
|
||||
class EdenAiParsingIDTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Identity parsing API.
|
||||
|
||||
@@ -29,6 +34,7 @@ class EdenAiParsingIDTool(EdenaiTool):
|
||||
"Useful for when you have to extract information from an ID Document "
|
||||
"Input should be the string url of the document to parse."
|
||||
)
|
||||
args_schema: Type[BaseModel] = IDParsingInput
|
||||
|
||||
feature: str = "ocr"
|
||||
subfeature: str = "identity_parser"
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field, HttpUrl
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InvoiceParsingInput(BaseModel):
|
||||
query: HttpUrl = Field(description="url of the document to parse")
|
||||
|
||||
|
||||
class EdenAiParsingInvoiceTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Invoice parsing API.
|
||||
|
||||
@@ -23,7 +28,6 @@ class EdenAiParsingInvoiceTool(EdenaiTool):
|
||||
"""
|
||||
|
||||
name: str = "edenai_invoice_parsing"
|
||||
|
||||
description: str = (
|
||||
"A wrapper around edenai Services invoice parsing. "
|
||||
"""Useful for when you have to extract information from
|
||||
@@ -33,6 +37,7 @@ class EdenAiParsingInvoiceTool(EdenaiTool):
|
||||
in a structured format to automate the invoice processing """
|
||||
"Input should be the string url of the document to parse."
|
||||
)
|
||||
args_schema: Type[BaseModel] = InvoiceParsingInput
|
||||
|
||||
language: Optional[str] = None
|
||||
"""
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForToolRun
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
|
||||
from langchain_community.tools.edenai.edenai_base_tool import EdenaiTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TextModerationInput(BaseModel):
|
||||
query: str = Field(description="Text to moderate")
|
||||
|
||||
|
||||
class EdenAiTextModerationTool(EdenaiTool):
|
||||
"""Tool that queries the Eden AI Explicit text detection.
|
||||
|
||||
@@ -23,7 +28,6 @@ class EdenAiTextModerationTool(EdenaiTool):
|
||||
"""
|
||||
|
||||
name: str = "edenai_explicit_content_detection_text"
|
||||
|
||||
description: str = (
|
||||
"A wrapper around edenai Services explicit content detection for text. "
|
||||
"""Useful for when you have to scan text for offensive,
|
||||
@@ -44,6 +48,7 @@ class EdenAiTextModerationTool(EdenaiTool):
|
||||
"""
|
||||
"Input should be a string."
|
||||
)
|
||||
args_schema: Type[BaseModel] = TextModerationInput
|
||||
|
||||
language: str
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class Requests(BaseModel):
|
||||
url,
|
||||
headers=self.headers,
|
||||
auth=self.auth,
|
||||
verify=self.verify,
|
||||
verify_ssl=self.verify,
|
||||
**kwargs,
|
||||
) as response:
|
||||
yield response
|
||||
@@ -94,7 +94,7 @@ class Requests(BaseModel):
|
||||
url,
|
||||
headers=self.headers,
|
||||
auth=self.auth,
|
||||
verify=self.verify,
|
||||
verify_ssl=self.verify,
|
||||
**kwargs,
|
||||
) as response:
|
||||
yield response
|
||||
|
||||
@@ -29,6 +29,7 @@ from langchain_core.callbacks import (
|
||||
)
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.exceptions import LangChainException
|
||||
from langchain_core.pydantic_v1 import root_validator
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.utils import get_from_env
|
||||
@@ -463,7 +464,7 @@ class AzureSearch(VectorStore):
|
||||
response = self.client.upload_documents(documents=data)
|
||||
# Check if all documents were successfully uploaded
|
||||
if not all(r.succeeded for r in response):
|
||||
raise Exception(response)
|
||||
raise LangChainException(response)
|
||||
# Reset data
|
||||
data = []
|
||||
|
||||
@@ -477,7 +478,7 @@ class AzureSearch(VectorStore):
|
||||
if all(r.succeeded for r in response):
|
||||
return ids
|
||||
else:
|
||||
raise Exception(response)
|
||||
raise LangChainException(response)
|
||||
|
||||
async def aadd_embeddings(
|
||||
self,
|
||||
@@ -521,7 +522,7 @@ class AzureSearch(VectorStore):
|
||||
response = await async_client.upload_documents(documents=data)
|
||||
# Check if all documents were successfully uploaded
|
||||
if not all(r.succeeded for r in response):
|
||||
raise Exception(response)
|
||||
raise LangChainException(response)
|
||||
# Reset data
|
||||
data = []
|
||||
|
||||
@@ -536,7 +537,7 @@ class AzureSearch(VectorStore):
|
||||
if all(r.succeeded for r in response):
|
||||
return ids
|
||||
else:
|
||||
raise Exception(response)
|
||||
raise LangChainException(response)
|
||||
|
||||
def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> bool:
|
||||
"""Delete by vector ID.
|
||||
@@ -1495,7 +1496,7 @@ class AzureSearchVectorStoreRetriever(BaseRetriever):
|
||||
"""Azure Search instance used to find similar documents."""
|
||||
search_type: str = "hybrid"
|
||||
"""Type of search to perform. Options are "similarity", "hybrid",
|
||||
"semantic_hybrid", "similarity_score_threshold", "hybrid_score_threshold",
|
||||
"semantic_hybrid", "similarity_score_threshold", "hybrid_score_threshold",
|
||||
or "semantic_hybrid_score_threshold"."""
|
||||
k: int = 4
|
||||
"""Number of documents to return."""
|
||||
|
||||
@@ -3,9 +3,20 @@ from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Optional, Tuple, Type
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
Type,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
from langchain_core._api import warn_deprecated
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.vectorstores import VST, VectorStore
|
||||
@@ -193,7 +204,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
cls: Type[VST],
|
||||
texts: List[str],
|
||||
embedding: Embeddings,
|
||||
metadatas: Optional[List[dict]] = None,
|
||||
metadatas: Optional[List[Dict]] = None,
|
||||
**kwargs: Any,
|
||||
) -> VST:
|
||||
raise NotImplementedError(
|
||||
@@ -204,7 +215,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
def add_texts(
|
||||
self,
|
||||
texts: Iterable[str],
|
||||
metadatas: Optional[List[dict]] = None,
|
||||
metadatas: Optional[List[Dict]] = None,
|
||||
ids: Optional[List[Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> List[str]:
|
||||
@@ -280,7 +291,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
self,
|
||||
query: str,
|
||||
k: int = 4,
|
||||
filters: Optional[Any] = None,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
*,
|
||||
query_type: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
@@ -290,14 +301,18 @@ class DatabricksVectorSearch(VectorStore):
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filters: Filters to apply to the query. Defaults to None.
|
||||
filter: Filters to apply to the query. Defaults to None.
|
||||
query_type: The type of this query. Supported values are "ANN" and "HYBRID".
|
||||
|
||||
Returns:
|
||||
List of Documents most similar to the embedding.
|
||||
"""
|
||||
docs_with_score = self.similarity_search_with_score(
|
||||
query=query, k=k, filters=filters, query_type=query_type, **kwargs
|
||||
query=query,
|
||||
k=k,
|
||||
filter=filter,
|
||||
query_type=query_type,
|
||||
**kwargs,
|
||||
)
|
||||
return [doc for doc, _ in docs_with_score]
|
||||
|
||||
@@ -305,7 +320,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
self,
|
||||
query: str,
|
||||
k: int = 4,
|
||||
filters: Optional[Any] = None,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
*,
|
||||
query_type: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
@@ -315,7 +330,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filters: Filters to apply to the query. Defaults to None.
|
||||
filter: Filters to apply to the query. Defaults to None.
|
||||
query_type: The type of this query. Supported values are "ANN" and "HYBRID".
|
||||
|
||||
Returns:
|
||||
@@ -328,12 +343,11 @@ class DatabricksVectorSearch(VectorStore):
|
||||
assert self.embeddings is not None, "embedding model is required."
|
||||
query_text = None
|
||||
query_vector = self.embeddings.embed_query(query)
|
||||
|
||||
search_resp = self.index.similarity_search(
|
||||
columns=self.columns,
|
||||
query_text=query_text,
|
||||
query_vector=query_vector,
|
||||
filters=filters,
|
||||
filters=filter or _alias_filters(kwargs),
|
||||
num_results=k,
|
||||
query_type=query_type,
|
||||
)
|
||||
@@ -357,7 +371,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
k: int = 4,
|
||||
fetch_k: int = 20,
|
||||
lambda_mult: float = 0.5,
|
||||
filters: Optional[Any] = None,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
*,
|
||||
query_type: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
@@ -375,7 +389,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
of diversity among the results with 0 corresponding
|
||||
to maximum diversity and 1 to minimum diversity.
|
||||
Defaults to 0.5.
|
||||
filters: Filters to apply to the query. Defaults to None.
|
||||
filter: Filters to apply to the query. Defaults to None.
|
||||
query_type: The type of this query. Supported values are "ANN" and "HYBRID".
|
||||
Returns:
|
||||
List of Documents selected by maximal marginal relevance.
|
||||
@@ -394,7 +408,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
k,
|
||||
fetch_k,
|
||||
lambda_mult=lambda_mult,
|
||||
filters=filters,
|
||||
filter=filter or _alias_filters(kwargs),
|
||||
query_type=query_type,
|
||||
)
|
||||
return docs
|
||||
@@ -405,7 +419,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
k: int = 4,
|
||||
fetch_k: int = 20,
|
||||
lambda_mult: float = 0.5,
|
||||
filters: Optional[Any] = None,
|
||||
filter: Optional[Any] = None,
|
||||
*,
|
||||
query_type: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
@@ -423,7 +437,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
of diversity among the results with 0 corresponding
|
||||
to maximum diversity and 1 to minimum diversity.
|
||||
Defaults to 0.5.
|
||||
filters: Filters to apply to the query. Defaults to None.
|
||||
filter: Filters to apply to the query. Defaults to None.
|
||||
query_type: The type of this query. Supported values are "ANN" and "HYBRID".
|
||||
Returns:
|
||||
List of Documents selected by maximal marginal relevance.
|
||||
@@ -435,12 +449,11 @@ class DatabricksVectorSearch(VectorStore):
|
||||
"`max_marginal_relevance_search` is not supported for index with "
|
||||
"Databricks-managed embeddings."
|
||||
)
|
||||
|
||||
search_resp = self.index.similarity_search(
|
||||
columns=list(set(self.columns + [embedding_column])),
|
||||
query_text=None,
|
||||
query_vector=embedding,
|
||||
filters=filters,
|
||||
filters=filter or _alias_filters(kwargs),
|
||||
num_results=fetch_k,
|
||||
query_type=query_type,
|
||||
)
|
||||
@@ -471,7 +484,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
self,
|
||||
embedding: List[float],
|
||||
k: int = 4,
|
||||
filters: Optional[Any] = None,
|
||||
filter: Optional[Any] = None,
|
||||
*,
|
||||
query_type: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
@@ -481,14 +494,18 @@ class DatabricksVectorSearch(VectorStore):
|
||||
Args:
|
||||
embedding: Embedding to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filters: Filters to apply to the query. Defaults to None.
|
||||
filter: Filters to apply to the query. Defaults to None.
|
||||
query_type: The type of this query. Supported values are "ANN" and "HYBRID".
|
||||
|
||||
Returns:
|
||||
List of Documents most similar to the embedding.
|
||||
"""
|
||||
docs_with_score = self.similarity_search_by_vector_with_score(
|
||||
embedding=embedding, k=k, filters=filters, query_type=query_type, **kwargs
|
||||
embedding=embedding,
|
||||
k=k,
|
||||
filter=filter,
|
||||
query_type=query_type,
|
||||
**kwargs,
|
||||
)
|
||||
return [doc for doc, _ in docs_with_score]
|
||||
|
||||
@@ -496,7 +513,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
self,
|
||||
embedding: List[float],
|
||||
k: int = 4,
|
||||
filters: Optional[Any] = None,
|
||||
filter: Optional[Any] = None,
|
||||
*,
|
||||
query_type: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
@@ -506,7 +523,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
Args:
|
||||
embedding: Embedding to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filters: Filters to apply to the query. Defaults to None.
|
||||
filter: Filters to apply to the query. Defaults to None.
|
||||
query_type: The type of this query. Supported values are "ANN" and "HYBRID".
|
||||
|
||||
Returns:
|
||||
@@ -520,14 +537,14 @@ class DatabricksVectorSearch(VectorStore):
|
||||
search_resp = self.index.similarity_search(
|
||||
columns=self.columns,
|
||||
query_vector=embedding,
|
||||
filters=filters,
|
||||
filters=filter or _alias_filters(kwargs),
|
||||
num_results=k,
|
||||
query_type=query_type,
|
||||
)
|
||||
return self._parse_search_response(search_resp)
|
||||
|
||||
def _parse_search_response(
|
||||
self, search_resp: dict, ignore_cols: Optional[List[str]] = None
|
||||
self, search_resp: Dict, ignore_cols: Optional[List[str]] = None
|
||||
) -> List[Tuple[Document, float]]:
|
||||
"""Parse the search response into a list of Documents with score."""
|
||||
if ignore_cols is None:
|
||||
@@ -552,7 +569,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
docs_with_score.append((doc, score))
|
||||
return docs_with_score
|
||||
|
||||
def _index_schema(self) -> Optional[dict]:
|
||||
def _index_schema(self) -> Optional[Dict]:
|
||||
"""Return the index schema as a dictionary.
|
||||
Return None if no schema found.
|
||||
"""
|
||||
@@ -574,7 +591,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
"""
|
||||
return self._embedding_vector_column().get("embedding_dimension")
|
||||
|
||||
def _embedding_vector_column(self) -> dict:
|
||||
def _embedding_vector_column(self) -> Dict:
|
||||
"""Return the embedding vector column configs as a dictionary.
|
||||
Empty if the index is not a self-managed embedding index.
|
||||
"""
|
||||
@@ -591,7 +608,7 @@ class DatabricksVectorSearch(VectorStore):
|
||||
"""
|
||||
return self._embedding_source_column().get("name")
|
||||
|
||||
def _embedding_source_column(self) -> dict:
|
||||
def _embedding_source_column(self) -> Dict:
|
||||
"""Return the embedding source column configs as a dictionary.
|
||||
Empty if the index is not a Databricks-managed embedding index.
|
||||
"""
|
||||
@@ -629,3 +646,20 @@ class DatabricksVectorSearch(VectorStore):
|
||||
"""Raise ValueError if the required arg with name `arg_name` is None."""
|
||||
if not arg:
|
||||
raise ValueError(f"`{arg_name}` is required for this index.")
|
||||
|
||||
|
||||
def _alias_filters(kwargs: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
The `filters` argument was used in the previous versions. It is now
|
||||
replaced with `filter` for consistency with other vector stores, but
|
||||
we still support `filters` for backward compatibility.
|
||||
"""
|
||||
if "filters" in kwargs:
|
||||
warn_deprecated(
|
||||
since="0.2.11",
|
||||
removal="0.3",
|
||||
message="DatabricksVectorSearch received a key `filters` in search_kwargs. "
|
||||
"`filters` was deprecated since langchain-community 0.2.11 and will "
|
||||
"be removed in 0.3. Please use `filter` instead.",
|
||||
)
|
||||
return kwargs.pop("filters", None)
|
||||
|
||||
@@ -191,7 +191,8 @@ class HanaDB(VectorStore):
|
||||
if column_length is not None and column_length > 0:
|
||||
if rows[0][1] != column_length:
|
||||
raise AttributeError(
|
||||
f"Column {column_name} has the wrong length: {rows[0][1]}"
|
||||
f"Column {column_name} has the wrong length: {rows[0][1]} "
|
||||
f"expected: {column_length}"
|
||||
)
|
||||
else:
|
||||
raise AttributeError(f"Column {column_name} does not exist")
|
||||
@@ -529,10 +530,18 @@ class HanaDB(VectorStore):
|
||||
if special_op in COMPARISONS_TO_SQL:
|
||||
operator = COMPARISONS_TO_SQL[special_op]
|
||||
if isinstance(special_val, bool):
|
||||
query_tuple.append("true" if filter_value else "false")
|
||||
query_tuple.append("true" if special_val else "false")
|
||||
elif isinstance(special_val, float):
|
||||
sql_param = "CAST(? as float)"
|
||||
query_tuple.append(special_val)
|
||||
elif (
|
||||
isinstance(special_val, dict)
|
||||
and "type" in special_val
|
||||
and special_val["type"] == "date"
|
||||
):
|
||||
# Date type
|
||||
sql_param = "CAST(? as DATE)"
|
||||
query_tuple.append(special_val["date"])
|
||||
else:
|
||||
query_tuple.append(special_val)
|
||||
# "$between"
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from copy import deepcopy
|
||||
from typing import (
|
||||
@@ -76,6 +77,41 @@ def _len_check_if_sized(x: Any, y: Any, x_name: str, y_name: str) -> None:
|
||||
return
|
||||
|
||||
|
||||
def _results_to_docs(results: Any) -> List[Document]:
|
||||
return [doc for doc, _ in _results_to_docs_and_scores(results)]
|
||||
|
||||
|
||||
def _results_to_docs_and_scores(results: Any) -> List[Tuple[Document, float]]:
|
||||
final_res: List[Any] = []
|
||||
try:
|
||||
responses, blobs = results[0]
|
||||
if (
|
||||
len(responses) > 0
|
||||
and "FindDescriptor" in responses[0]
|
||||
and "entities" in responses[0]["FindDescriptor"]
|
||||
):
|
||||
result_entities = responses[0]["FindDescriptor"]["entities"]
|
||||
# result_blobs = blobs
|
||||
for ent in result_entities:
|
||||
distance = round(ent["_distance"], 10)
|
||||
txt_contents = ent["content"]
|
||||
for p in INVALID_DOC_METADATA_KEYS:
|
||||
if p in ent:
|
||||
del ent[p]
|
||||
props = {
|
||||
mkey: mval
|
||||
for mkey, mval in ent.items()
|
||||
if mval not in INVALID_METADATA_VALUE
|
||||
}
|
||||
|
||||
final_res.append(
|
||||
(Document(page_content=txt_contents, metadata=props), distance)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warn(f"No results returned. Error while parsing results: {e}")
|
||||
return final_res
|
||||
|
||||
|
||||
def VDMS_Client(host: str = "localhost", port: int = 55555) -> vdms.vdms:
|
||||
"""VDMS client for the VDMS server.
|
||||
|
||||
@@ -122,7 +158,7 @@ class VDMS(VectorStore):
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.embeddings import HuggingFaceEmbeddings
|
||||
from langchain_huggingface import HuggingFaceEmbeddings
|
||||
from langchain_community.vectorstores.vdms import VDMS, VDMS_Client
|
||||
|
||||
vectorstore = VDMS(
|
||||
@@ -143,19 +179,20 @@ class VDMS(VectorStore):
|
||||
distance_strategy: DISTANCE_METRICS = "L2",
|
||||
engine: ENGINES = "FaissFlat",
|
||||
relevance_score_fn: Optional[Callable[[float], float]] = None,
|
||||
embedding_dimensions: Optional[int] = None,
|
||||
) -> None:
|
||||
# Check required parameters
|
||||
self._client = client
|
||||
self.similarity_search_engine = engine
|
||||
self.distance_strategy = distance_strategy
|
||||
self.embedding = embedding
|
||||
self._check_required_inputs(collection_name)
|
||||
self._check_required_inputs(collection_name, embedding_dimensions)
|
||||
|
||||
# Update other parameters
|
||||
self.override_relevance_score_fn = relevance_score_fn
|
||||
|
||||
# Initialize collection
|
||||
self._collection_name = self.__add_set(
|
||||
self._collection_name = self.add_set(
|
||||
collection_name,
|
||||
engine=self.similarity_search_engine,
|
||||
metric=self.distance_strategy,
|
||||
@@ -173,6 +210,14 @@ class VDMS(VectorStore):
|
||||
p_str += " to be an Embeddings object"
|
||||
raise ValueError(p_str)
|
||||
|
||||
def _embed_video(self, paths: List[str], **kwargs: Any) -> List[List[float]]:
|
||||
if self.embedding is not None and hasattr(self.embedding, "embed_video"):
|
||||
return self.embedding.embed_video(paths=paths, **kwargs)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Must provide `embedding` which has attribute `embed_video`"
|
||||
)
|
||||
|
||||
def _embed_image(self, uris: List[str]) -> List[List[float]]:
|
||||
if self.embedding is not None and hasattr(self.embedding, "embed_image"):
|
||||
return self.embedding.embed_image(uris=uris)
|
||||
@@ -225,10 +270,10 @@ class VDMS(VectorStore):
|
||||
if self.override_relevance_score_fn is None:
|
||||
kwargs["normalize_distance"] = True
|
||||
docs_and_scores = self.similarity_search_with_score(
|
||||
query,
|
||||
k,
|
||||
fetch_k,
|
||||
filter,
|
||||
query=query,
|
||||
k=k,
|
||||
fetch_k=fetch_k,
|
||||
filter=filter,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -242,7 +287,7 @@ class VDMS(VectorStore):
|
||||
)
|
||||
return docs_and_rel_scores
|
||||
|
||||
def __add(
|
||||
def add(
|
||||
self,
|
||||
collection_name: str,
|
||||
texts: List[str],
|
||||
@@ -275,7 +320,7 @@ class VDMS(VectorStore):
|
||||
|
||||
return inserted_ids
|
||||
|
||||
def __add_set(
|
||||
def add_set(
|
||||
self,
|
||||
collection_name: str,
|
||||
engine: ENGINES = "FaissFlat",
|
||||
@@ -333,6 +378,12 @@ class VDMS(VectorStore):
|
||||
|
||||
all_queries.append(query)
|
||||
response, response_array = self.__run_vdms_query(all_queries, all_blobs)
|
||||
|
||||
# Update/store indices after deletion
|
||||
query = _add_descriptorset(
|
||||
"FindDescriptorSet", collection_name, storeIndex=True
|
||||
)
|
||||
responseSet, _ = self.__run_vdms_query([query], all_blobs)
|
||||
return "FindDescriptor" in response[0]
|
||||
|
||||
def __get_add_query(
|
||||
@@ -365,7 +416,7 @@ class VDMS(VectorStore):
|
||||
|
||||
if metadata:
|
||||
props.update(metadata)
|
||||
if document:
|
||||
if document not in [None, ""]:
|
||||
props["content"] = document
|
||||
|
||||
for k in props.keys():
|
||||
@@ -515,7 +566,7 @@ class VDMS(VectorStore):
|
||||
|
||||
Args:
|
||||
uris: List of paths to the images to add to the vectorstore.
|
||||
metadatas: Optional list of metadatas associated with the texts.
|
||||
metadatas: Optional list of metadatas associated with the images.
|
||||
ids: Optional list of unique IDs.
|
||||
batch_size (int): Number of concurrent requests to send to the server.
|
||||
add_path: Bool to add image path as metadata
|
||||
@@ -545,7 +596,7 @@ class VDMS(VectorStore):
|
||||
else:
|
||||
metadatas = [_validate_vdms_properties(m) for m in metadatas]
|
||||
|
||||
self.__from(
|
||||
self.add_from(
|
||||
texts=b64_texts,
|
||||
embeddings=embeddings,
|
||||
ids=ids,
|
||||
@@ -555,6 +606,62 @@ class VDMS(VectorStore):
|
||||
)
|
||||
return ids
|
||||
|
||||
def add_videos(
|
||||
self,
|
||||
paths: List[str],
|
||||
texts: Optional[List[str]] = None,
|
||||
metadatas: Optional[List[dict]] = None,
|
||||
ids: Optional[List[str]] = None,
|
||||
batch_size: int = 1,
|
||||
add_path: Optional[bool] = True,
|
||||
**kwargs: Any,
|
||||
) -> List[str]:
|
||||
"""Run videos through the embeddings and add to the vectorstore.
|
||||
|
||||
Videos are added as embeddings (AddDescriptor) instead of separate
|
||||
entity (AddVideo) within VDMS to leverage similarity search capability
|
||||
|
||||
Args:
|
||||
paths: List of paths to the videos to add to the vectorstore.
|
||||
metadatas: Optional list of text associated with the videos.
|
||||
metadatas: Optional list of metadatas associated with the videos.
|
||||
ids: Optional list of unique IDs.
|
||||
batch_size (int): Number of concurrent requests to send to the server.
|
||||
add_path: Bool to add video path as metadata
|
||||
|
||||
Returns:
|
||||
List of ids from adding videos into the vectorstore.
|
||||
"""
|
||||
if texts is None:
|
||||
texts = ["" for _ in paths]
|
||||
|
||||
if add_path and metadatas:
|
||||
for midx, path in enumerate(paths):
|
||||
metadatas[midx]["video_path"] = path
|
||||
elif add_path:
|
||||
metadatas = []
|
||||
for path in paths:
|
||||
metadatas.append({"video_path": path})
|
||||
|
||||
# Populate IDs
|
||||
ids = ids if ids is not None else [str(uuid.uuid4()) for _ in paths]
|
||||
|
||||
# Set embeddings
|
||||
embeddings = self._embed_video(paths=paths, **kwargs)
|
||||
|
||||
if metadatas is None:
|
||||
metadatas = [{} for _ in paths]
|
||||
|
||||
self.add_from(
|
||||
texts=texts,
|
||||
embeddings=embeddings,
|
||||
ids=ids,
|
||||
metadatas=metadatas,
|
||||
batch_size=batch_size,
|
||||
**kwargs,
|
||||
)
|
||||
return ids
|
||||
|
||||
def add_texts(
|
||||
self,
|
||||
texts: Iterable[str],
|
||||
@@ -586,7 +693,7 @@ class VDMS(VectorStore):
|
||||
else:
|
||||
metadatas = [_validate_vdms_properties(m) for m in metadatas]
|
||||
|
||||
inserted_ids = self.__from(
|
||||
inserted_ids = self.add_from(
|
||||
texts=texts,
|
||||
embeddings=embeddings,
|
||||
ids=ids,
|
||||
@@ -596,7 +703,7 @@ class VDMS(VectorStore):
|
||||
)
|
||||
return inserted_ids
|
||||
|
||||
def __from(
|
||||
def add_from(
|
||||
self,
|
||||
texts: List[str],
|
||||
embeddings: List[List[float]],
|
||||
@@ -617,7 +724,7 @@ class VDMS(VectorStore):
|
||||
if metadatas:
|
||||
batch_metadatas = metadatas[start_idx:end_idx]
|
||||
|
||||
result = self.__add(
|
||||
result = self.add(
|
||||
self._collection_name,
|
||||
embeddings=batch_embedding_vectors,
|
||||
texts=batch_texts,
|
||||
@@ -633,7 +740,9 @@ class VDMS(VectorStore):
|
||||
)
|
||||
return inserted_ids
|
||||
|
||||
def _check_required_inputs(self, collection_name: str) -> None:
|
||||
def _check_required_inputs(
|
||||
self, collection_name: str, embedding_dimensions: Union[int, None]
|
||||
) -> None:
|
||||
# Check connection to client
|
||||
if not self._client.is_connected():
|
||||
raise ValueError(
|
||||
@@ -656,7 +765,29 @@ class VDMS(VectorStore):
|
||||
if self.embedding is None:
|
||||
raise ValueError("Must provide embedding function")
|
||||
|
||||
self.embedding_dimension = len(self._embed_query("This is a sample sentence."))
|
||||
if embedding_dimensions is not None:
|
||||
self.embedding_dimension = embedding_dimensions
|
||||
elif self.embedding is not None and hasattr(self.embedding, "embed_query"):
|
||||
self.embedding_dimension = len(
|
||||
self._embed_query("This is a sample sentence.")
|
||||
)
|
||||
elif self.embedding is not None and (
|
||||
hasattr(self.embedding, "embed_image")
|
||||
or hasattr(self.embedding, "embed_video")
|
||||
):
|
||||
if hasattr(self.embedding, "model"):
|
||||
try:
|
||||
self.embedding_dimension = (
|
||||
self.embedding.model.token_embedding.embedding_dim
|
||||
)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"Embedding dimension needed. Please define embedding_dimensions"
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Embedding dimension needed. Please define embedding_dimensions"
|
||||
)
|
||||
|
||||
# Check for properties
|
||||
current_props = self.__get_properties(collection_name)
|
||||
@@ -727,7 +858,7 @@ class VDMS(VectorStore):
|
||||
)
|
||||
response, response_array = self.__run_vdms_query([query], all_blobs)
|
||||
|
||||
if normalize:
|
||||
if normalize and command_str in response[0]:
|
||||
max_dist = response[0][command_str]["entities"][-1]["_distance"]
|
||||
|
||||
return response, response_array, max_dist
|
||||
@@ -769,14 +900,21 @@ class VDMS(VectorStore):
|
||||
results=results,
|
||||
)
|
||||
response, response_array = self.__run_vdms_query([query])
|
||||
ids_of_interest = [
|
||||
ent["id"] for ent in response[0][command_str]["entities"]
|
||||
]
|
||||
if command_str in response[0] and response[0][command_str]["returned"] > 0:
|
||||
ids_of_interest = [
|
||||
ent["id"] for ent in response[0][command_str]["entities"]
|
||||
]
|
||||
else:
|
||||
return [], []
|
||||
|
||||
# (2) Find top fetch_k results
|
||||
response, response_array, max_dist = self.get_k_candidates(
|
||||
setname, fetch_k, results, all_blobs, normalize=normalize_distance
|
||||
)
|
||||
if command_str not in response[0] or (
|
||||
command_str in response[0] and response[0][command_str]["returned"] == 0
|
||||
):
|
||||
return [], []
|
||||
|
||||
# (3) Intersection of (1) & (2) using ids
|
||||
new_entities: List[Dict] = []
|
||||
@@ -792,7 +930,7 @@ class VDMS(VectorStore):
|
||||
print(p_str) # noqa: T201
|
||||
|
||||
if normalize_distance:
|
||||
max_dist = 1.0 if max_dist == 0 else max_dist
|
||||
max_dist = 1.0 if max_dist in [0, np.inf] else max_dist
|
||||
for ent_idx, ent in enumerate(response[0][command_str]["entities"]):
|
||||
ent["_distance"] = ent["_distance"] / max_dist
|
||||
response[0][command_str]["entities"][ent_idx]["_distance"] = ent[
|
||||
@@ -946,7 +1084,7 @@ class VDMS(VectorStore):
|
||||
among selected documents.
|
||||
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
query (str): Query to look up. Text or path for image or video.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
fetch_k: Number of Documents to fetch to pass to MMR algorithm.
|
||||
lambda_mult: Number between 0 and 1 that determines the degree
|
||||
@@ -963,7 +1101,20 @@ class VDMS(VectorStore):
|
||||
"For MMR search, you must specify an embedding function on" "creation."
|
||||
)
|
||||
|
||||
embedding_vector: List[float] = self._embed_query(query)
|
||||
# embedding_vector: List[float] = self._embed_query(query)
|
||||
embedding_vector: List[float]
|
||||
if not os.path.isfile(query) and hasattr(self.embedding, "embed_query"):
|
||||
embedding_vector = self._embed_query(query)
|
||||
elif os.path.isfile(query) and hasattr(self.embedding, "embed_image"):
|
||||
embedding_vector = self._embed_image(uris=[query])[0]
|
||||
elif os.path.isfile(query) and hasattr(self.embedding, "embed_video"):
|
||||
embedding_vector = self._embed_video(paths=[query])[0]
|
||||
else:
|
||||
error_msg = f"Could not generate embedding for query '{query}'."
|
||||
error_msg += "If using path for image or video, verify embedding model "
|
||||
error_msg += "has callable functions 'embed_image' or 'embed_video'."
|
||||
raise ValueError(error_msg)
|
||||
|
||||
docs = self.max_marginal_relevance_search_by_vector(
|
||||
embedding_vector,
|
||||
k,
|
||||
@@ -1006,19 +1157,27 @@ class VDMS(VectorStore):
|
||||
include=["metadatas", "documents", "distances", "embeddings"],
|
||||
)
|
||||
|
||||
embedding_list = [list(_bytes2embedding(result)) for result in results[0][1]]
|
||||
if len(results[0][1]) == 0:
|
||||
# No results returned
|
||||
return []
|
||||
else:
|
||||
embedding_list = [
|
||||
list(_bytes2embedding(result)) for result in results[0][1]
|
||||
]
|
||||
|
||||
mmr_selected = maximal_marginal_relevance(
|
||||
np.array(embedding, dtype=np.float32),
|
||||
embedding_list,
|
||||
k=k,
|
||||
lambda_mult=lambda_mult,
|
||||
)
|
||||
mmr_selected = maximal_marginal_relevance(
|
||||
np.array(embedding, dtype=np.float32),
|
||||
embedding_list,
|
||||
k=k,
|
||||
lambda_mult=lambda_mult,
|
||||
)
|
||||
|
||||
candidates = _results_to_docs(results)
|
||||
candidates = _results_to_docs(results)
|
||||
|
||||
selected_results = [r for i, r in enumerate(candidates) if i in mmr_selected]
|
||||
return selected_results
|
||||
selected_results = [
|
||||
r for i, r in enumerate(candidates) if i in mmr_selected
|
||||
]
|
||||
return selected_results
|
||||
|
||||
def max_marginal_relevance_search_with_score(
|
||||
self,
|
||||
@@ -1034,7 +1193,7 @@ class VDMS(VectorStore):
|
||||
among selected documents.
|
||||
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
query (str): Query to look up. Text or path for image or video.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
fetch_k: Number of Documents to fetch to pass to MMR algorithm.
|
||||
lambda_mult: Number between 0 and 1 that determines the degree
|
||||
@@ -1051,7 +1210,18 @@ class VDMS(VectorStore):
|
||||
"For MMR search, you must specify an embedding function on" "creation."
|
||||
)
|
||||
|
||||
embedding = self._embed_query(query)
|
||||
if not os.path.isfile(query) and hasattr(self.embedding, "embed_query"):
|
||||
embedding = self._embed_query(query)
|
||||
elif os.path.isfile(query) and hasattr(self.embedding, "embed_image"):
|
||||
embedding = self._embed_image(uris=[query])[0]
|
||||
elif os.path.isfile(query) and hasattr(self.embedding, "embed_video"):
|
||||
embedding = self._embed_video(paths=[query])[0]
|
||||
else:
|
||||
error_msg = f"Could not generate embedding for query '{query}'."
|
||||
error_msg += "If using path for image or video, verify embedding model "
|
||||
error_msg += "has callable functions 'embed_image' or 'embed_video'."
|
||||
raise ValueError(error_msg)
|
||||
|
||||
docs = self.max_marginal_relevance_search_with_score_by_vector(
|
||||
embedding,
|
||||
k,
|
||||
@@ -1094,21 +1264,27 @@ class VDMS(VectorStore):
|
||||
include=["metadatas", "documents", "distances", "embeddings"],
|
||||
)
|
||||
|
||||
embedding_list = [list(_bytes2embedding(result)) for result in results[0][1]]
|
||||
if len(results[0][1]) == 0:
|
||||
# No results returned
|
||||
return []
|
||||
else:
|
||||
embedding_list = [
|
||||
list(_bytes2embedding(result)) for result in results[0][1]
|
||||
]
|
||||
|
||||
mmr_selected = maximal_marginal_relevance(
|
||||
np.array(embedding, dtype=np.float32),
|
||||
embedding_list,
|
||||
k=k,
|
||||
lambda_mult=lambda_mult,
|
||||
)
|
||||
mmr_selected = maximal_marginal_relevance(
|
||||
np.array(embedding, dtype=np.float32),
|
||||
embedding_list,
|
||||
k=k,
|
||||
lambda_mult=lambda_mult,
|
||||
)
|
||||
|
||||
candidates = _results_to_docs_and_scores(results)
|
||||
candidates = _results_to_docs_and_scores(results)
|
||||
|
||||
selected_results = [
|
||||
(r, s) for i, (r, s) in enumerate(candidates) if i in mmr_selected
|
||||
]
|
||||
return selected_results
|
||||
selected_results = [
|
||||
(r, s) for i, (r, s) in enumerate(candidates) if i in mmr_selected
|
||||
]
|
||||
return selected_results
|
||||
|
||||
def query_collection_embeddings(
|
||||
self,
|
||||
@@ -1162,7 +1338,7 @@ class VDMS(VectorStore):
|
||||
"""Run similarity search with VDMS.
|
||||
|
||||
Args:
|
||||
query (str): Query text to search for.
|
||||
query (str): Query to look up. Text or path for image or video.
|
||||
k (int): Number of results to return. Defaults to 3.
|
||||
fetch_k (int): Number of candidates to fetch for knn (>= k).
|
||||
filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None.
|
||||
@@ -1171,7 +1347,7 @@ class VDMS(VectorStore):
|
||||
List[Document]: List of documents most similar to the query text.
|
||||
"""
|
||||
docs_and_scores = self.similarity_search_with_score(
|
||||
query, k, fetch_k, filter=filter, **kwargs
|
||||
query, k=k, fetch_k=fetch_k, filter=filter, **kwargs
|
||||
)
|
||||
return [doc for doc, _ in docs_and_scores]
|
||||
|
||||
@@ -1213,7 +1389,7 @@ class VDMS(VectorStore):
|
||||
"""Run similarity search with VDMS with distance.
|
||||
|
||||
Args:
|
||||
query (str): Query text to search for.
|
||||
query (str): Query to look up. Text or path for image or video.
|
||||
k (int): Number of results to return. Defaults to 3.
|
||||
fetch_k (int): Number of candidates to fetch for knn (>= k).
|
||||
filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None.
|
||||
@@ -1226,7 +1402,18 @@ class VDMS(VectorStore):
|
||||
if self.embedding is None:
|
||||
raise ValueError("Must provide embedding function")
|
||||
else:
|
||||
query_embedding: List[float] = self._embed_query(query)
|
||||
if not os.path.isfile(query) and hasattr(self.embedding, "embed_query"):
|
||||
query_embedding: List[float] = self._embed_query(query)
|
||||
elif os.path.isfile(query) and hasattr(self.embedding, "embed_image"):
|
||||
query_embedding = self._embed_image(uris=[query])[0]
|
||||
elif os.path.isfile(query) and hasattr(self.embedding, "embed_video"):
|
||||
query_embedding = self._embed_video(paths=[query])[0]
|
||||
else:
|
||||
error_msg = f"Could not generate embedding for query '{query}'."
|
||||
error_msg += "If using path for image or video, verify embedding model "
|
||||
error_msg += "has callable functions 'embed_image' or 'embed_video'."
|
||||
raise ValueError(error_msg)
|
||||
|
||||
results = self.query_collection_embeddings(
|
||||
query_embeddings=[query_embedding],
|
||||
n_results=k,
|
||||
@@ -1256,10 +1443,10 @@ class VDMS(VectorStore):
|
||||
|
||||
Returns:
|
||||
List[Tuple[Document, float]]: List of documents most similar to
|
||||
the query text and cosine distance in float for each.
|
||||
Lower score represents more similarity.
|
||||
the query text. Lower score represents more similarity.
|
||||
"""
|
||||
kwargs["normalize_distance"] = True
|
||||
|
||||
# kwargs["normalize_distance"] = True
|
||||
|
||||
results = self.query_collection_embeddings(
|
||||
query_embeddings=[embedding],
|
||||
@@ -1308,37 +1495,6 @@ class VDMS(VectorStore):
|
||||
# VDMS UTILITY
|
||||
|
||||
|
||||
def _results_to_docs(results: Any) -> List[Document]:
|
||||
return [doc for doc, _ in _results_to_docs_and_scores(results)]
|
||||
|
||||
|
||||
def _results_to_docs_and_scores(results: Any) -> List[Tuple[Document, float]]:
|
||||
final_res: List[Any] = []
|
||||
responses, blobs = results[0]
|
||||
if (
|
||||
"FindDescriptor" in responses[0]
|
||||
and "entities" in responses[0]["FindDescriptor"]
|
||||
):
|
||||
result_entities = responses[0]["FindDescriptor"]["entities"]
|
||||
# result_blobs = blobs
|
||||
for ent in result_entities:
|
||||
distance = ent["_distance"]
|
||||
txt_contents = ent["content"]
|
||||
for p in INVALID_DOC_METADATA_KEYS:
|
||||
if p in ent:
|
||||
del ent[p]
|
||||
props = {
|
||||
mkey: mval
|
||||
for mkey, mval in ent.items()
|
||||
if mval not in INVALID_METADATA_VALUE
|
||||
}
|
||||
|
||||
final_res.append(
|
||||
(Document(page_content=txt_contents, metadata=props), distance)
|
||||
)
|
||||
return final_res
|
||||
|
||||
|
||||
def _add_descriptor(
|
||||
command_str: str,
|
||||
setname: str,
|
||||
|
||||
17
libs/community/poetry.lock
generated
17
libs/community/poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
@@ -2117,7 +2117,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = ">=3.8.1,<4.0"
|
||||
@@ -2127,7 +2127,7 @@ develop = true
|
||||
[package.dependencies]
|
||||
aiohttp = "^3.8.3"
|
||||
async-timeout = {version = "^4.0.0", markers = "python_version < \"3.11\""}
|
||||
langchain-core = "^0.2.22"
|
||||
langchain-core = "^0.2.23"
|
||||
langchain-text-splitters = "^0.2.0"
|
||||
langsmith = "^0.1.17"
|
||||
numpy = [
|
||||
@@ -3819,7 +3819,6 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
@@ -5358,13 +5357,13 @@ tests = ["Werkzeug (==2.0.3)", "aiohttp", "boto3", "httplib2", "httpx", "pytest"
|
||||
|
||||
[[package]]
|
||||
name = "vdms"
|
||||
version = "0.0.20"
|
||||
version = "0.0.21"
|
||||
description = "VDMS Client Module"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4"
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,<4,>=2.6"
|
||||
files = [
|
||||
{file = "vdms-0.0.20-py3-none-any.whl", hash = "sha256:7b81127f2981f2dabdcc5880ad7eb4bc2c7833a25aaf79a7b1a560e86bf7b5ec"},
|
||||
{file = "vdms-0.0.20.tar.gz", hash = "sha256:746c21a96e420b9b034495537b42d70f2326b020a1c6907677f7851a926e8605"},
|
||||
{file = "vdms-0.0.21-py3-none-any.whl", hash = "sha256:18e785cd7ec66c3a6c5921a6a93fe2ca22d97f45f40dccb9ff0c954675139daf"},
|
||||
{file = "vdms-0.0.21.tar.gz", hash = "sha256:bbb62d3f1a5cdab6b6bd41950942880cc431729313742870eb255a23c5f0381f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -5759,4 +5758,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.8.1,<4.0"
|
||||
content-hash = "14d60e1f61fa9c0ba69cb4e227e4af3de395a8dd4a53b121fe488e7b9f75ea66"
|
||||
content-hash = "324e10fe59335abccbd422d9ee8ae771714edf72078a750b99c87ba853bd617c"
|
||||
|
||||
@@ -102,7 +102,7 @@ cassio = "^0.1.6"
|
||||
tiktoken = ">=0.3.2,<0.6.0"
|
||||
anthropic = "^0.3.11"
|
||||
fireworks-ai = "^0.9.0"
|
||||
vdms = "^0.0.20"
|
||||
vdms = ">=0.0.20"
|
||||
exllamav2 = "^0.0.18"
|
||||
|
||||
[tool.poetry.group.lint.dependencies]
|
||||
|
||||
@@ -13,6 +13,30 @@ fi
|
||||
|
||||
repository_path="$1"
|
||||
|
||||
# Check that we are not using features that cannot be captured via init.
|
||||
# pre-init is a custom decorator that we introduced to capture the same semantics
|
||||
# as @root_validator(pre=False, skip_on_failure=False) available in pydantic 1.
|
||||
count=$(git grep -E '(@root_validator)|(@validator)|(@pre_init)' -- "*.py" | wc -l)
|
||||
# PRs that increase the current count will not be accepted.
|
||||
# PRs that decrease update the code in the repository
|
||||
# and allow decreasing the count of are welcome!
|
||||
current_count=336
|
||||
|
||||
if [ "$count" -gt "$current_count" ]; then
|
||||
echo "The PR seems to be introducing new usage of @root_validator and/or @field_validator."
|
||||
echo "git grep -E '(@root_validator)|(@validator)' | wc -l returned $count"
|
||||
echo "whereas the expected count should be equal or less than $current_count"
|
||||
echo "Please update the code to instead use __init__"
|
||||
echo "For examples, please see: "
|
||||
echo "https://gist.github.com/eyurtsev/d1dcba10c2f35626e302f1b98a0f5a3c "
|
||||
echo "This linter is here to make sure that its easier to upgrade pydantic in the future."
|
||||
exit 1
|
||||
elif [ "$count" -lt "$current_count" ]; then
|
||||
echo "Please update the $current_count variable in ./scripts/check_pydantic.sh to $count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Search for lines matching the pattern within the specified repository
|
||||
result=$(git -C "$repository_path" grep -En '^import pydantic|^from pydantic')
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
|
||||
import vdms
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
embedding_function = FakeEmbeddings()
|
||||
|
||||
|
||||
# The connection string matches the default settings in the docker-compose file
|
||||
@@ -28,6 +29,7 @@ logging.basicConfig(level=logging.DEBUG)
|
||||
# cd [root]/docker
|
||||
# docker compose up -d vdms
|
||||
@pytest.fixture
|
||||
@pytest.mark.enable_socket
|
||||
def vdms_client() -> vdms.vdms:
|
||||
return VDMS_Client(
|
||||
host=os.getenv("VDMS_DBHOST", "localhost"),
|
||||
@@ -36,19 +38,19 @@ def vdms_client() -> vdms.vdms:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_init_from_client(vdms_client: vdms.vdms) -> None:
|
||||
embedding_function = FakeEmbeddings()
|
||||
_ = VDMS( # type: ignore[call-arg]
|
||||
embedding_function=embedding_function,
|
||||
embedding=embedding_function,
|
||||
client=vdms_client,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_from_texts_with_metadatas(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and search."""
|
||||
collection_name = "test_from_texts_with_metadatas"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
ids = [f"test_from_texts_with_metadatas_{i}" for i in range(len(texts))]
|
||||
metadatas = [{"page": str(i)} for i in range(1, len(texts) + 1)]
|
||||
@@ -67,10 +69,10 @@ def test_from_texts_with_metadatas(vdms_client: vdms.vdms) -> None:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_from_texts_with_metadatas_with_scores(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and scored search."""
|
||||
collection_name = "test_from_texts_with_metadatas_with_scores"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
ids = [f"test_from_texts_with_metadatas_with_scores_{i}" for i in range(len(texts))]
|
||||
metadatas = [{"page": str(i)} for i in range(1, len(texts) + 1)]
|
||||
@@ -82,19 +84,19 @@ def test_from_texts_with_metadatas_with_scores(vdms_client: vdms.vdms) -> None:
|
||||
collection_name=collection_name,
|
||||
client=vdms_client,
|
||||
)
|
||||
output = docsearch.similarity_search_with_score("foo", k=1)
|
||||
output = docsearch.similarity_search_with_score("foo", k=1, fetch_k=1)
|
||||
assert output == [
|
||||
(Document(page_content="foo", metadata={"page": "1", "id": ids[0]}), 0.0)
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_from_texts_with_metadatas_with_scores_using_vector(
|
||||
vdms_client: vdms.vdms,
|
||||
) -> None:
|
||||
"""Test end to end construction and scored search, using embedding vector."""
|
||||
collection_name = "test_from_texts_with_metadatas_with_scores_using_vector"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
ids = [f"test_from_texts_with_metadatas_{i}" for i in range(len(texts))]
|
||||
metadatas = [{"page": str(i)} for i in range(1, len(texts) + 1)]
|
||||
@@ -113,10 +115,10 @@ def test_from_texts_with_metadatas_with_scores_using_vector(
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_search_filter(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and search with metadata filtering."""
|
||||
collection_name = "test_search_filter"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["far", "bar", "baz"]
|
||||
ids = [f"test_search_filter_{i}" for i in range(len(texts))]
|
||||
metadatas = [{"first_letter": "{}".format(text[0])} for text in texts]
|
||||
@@ -144,10 +146,10 @@ def test_search_filter(vdms_client: vdms.vdms) -> None:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_search_filter_with_scores(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and scored search with metadata filtering."""
|
||||
collection_name = "test_search_filter_with_scores"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["far", "bar", "baz"]
|
||||
ids = [f"test_search_filter_with_scores_{i}" for i in range(len(texts))]
|
||||
metadatas = [{"first_letter": "{}".format(text[0])} for text in texts]
|
||||
@@ -185,10 +187,10 @@ def test_search_filter_with_scores(vdms_client: vdms.vdms) -> None:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_mmr(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and search."""
|
||||
collection_name = "test_mmr"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
ids = [f"test_mmr_{i}" for i in range(len(texts))]
|
||||
docsearch = VDMS.from_texts(
|
||||
@@ -203,10 +205,10 @@ def test_mmr(vdms_client: vdms.vdms) -> None:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_mmr_by_vector(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and search."""
|
||||
collection_name = "test_mmr_by_vector"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
ids = [f"test_mmr_by_vector_{i}" for i in range(len(texts))]
|
||||
docsearch = VDMS.from_texts(
|
||||
@@ -222,10 +224,10 @@ def test_mmr_by_vector(vdms_client: vdms.vdms) -> None:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_with_include_parameter(vdms_client: vdms.vdms) -> None:
|
||||
"""Test end to end construction and include parameter."""
|
||||
collection_name = "test_with_include_parameter"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
docsearch = VDMS.from_texts(
|
||||
texts=texts,
|
||||
@@ -233,19 +235,23 @@ def test_with_include_parameter(vdms_client: vdms.vdms) -> None:
|
||||
collection_name=collection_name,
|
||||
client=vdms_client,
|
||||
)
|
||||
|
||||
response, response_array = docsearch.get(collection_name, include=["embeddings"])
|
||||
assert response_array != []
|
||||
for emb in embedding_function.embed_documents(texts):
|
||||
assert embedding2bytes(emb) in response_array
|
||||
|
||||
response, response_array = docsearch.get(collection_name)
|
||||
assert response_array == []
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_update_document(vdms_client: vdms.vdms) -> None:
|
||||
"""Test the update_document function in the VDMS class."""
|
||||
collection_name = "test_update_document"
|
||||
|
||||
# Make a consistent embedding
|
||||
embedding_function = ConsistentFakeEmbeddings()
|
||||
const_embedding_function = ConsistentFakeEmbeddings()
|
||||
|
||||
# Initial document content and id
|
||||
initial_content = "foo"
|
||||
@@ -259,10 +265,10 @@ def test_update_document(vdms_client: vdms.vdms) -> None:
|
||||
client=vdms_client,
|
||||
collection_name=collection_name,
|
||||
documents=[original_doc],
|
||||
embedding=embedding_function,
|
||||
embedding=const_embedding_function,
|
||||
ids=[document_id],
|
||||
)
|
||||
response, old_embedding = docsearch.get(
|
||||
old_response, old_embedding = docsearch.get(
|
||||
collection_name,
|
||||
constraints={"id": ["==", document_id]},
|
||||
include=["metadata", "embeddings"],
|
||||
@@ -281,17 +287,15 @@ def test_update_document(vdms_client: vdms.vdms) -> None:
|
||||
)
|
||||
|
||||
# Perform a similarity search with the updated content
|
||||
output = docsearch.similarity_search(updated_content, k=1)
|
||||
output = docsearch.similarity_search(updated_content, k=3)[0]
|
||||
|
||||
# Assert that the updated document is returned by the search
|
||||
assert output == [
|
||||
Document(
|
||||
page_content=updated_content, metadata={"page": "1", "id": document_id}
|
||||
)
|
||||
]
|
||||
assert output == Document(
|
||||
page_content=updated_content, metadata={"page": "1", "id": document_id}
|
||||
)
|
||||
|
||||
# Assert that the new embedding is correct
|
||||
response, new_embedding = docsearch.get(
|
||||
new_response, new_embedding = docsearch.get(
|
||||
collection_name,
|
||||
constraints={"id": ["==", document_id]},
|
||||
include=["metadata", "embeddings"],
|
||||
@@ -299,16 +303,21 @@ def test_update_document(vdms_client: vdms.vdms) -> None:
|
||||
# new_embedding = response_array[0]
|
||||
|
||||
assert new_embedding[0] == embedding2bytes(
|
||||
embedding_function.embed_documents([updated_content])[0]
|
||||
const_embedding_function.embed_documents([updated_content])[0]
|
||||
)
|
||||
assert new_embedding != old_embedding
|
||||
|
||||
assert (
|
||||
new_response[0]["FindDescriptor"]["entities"][0]["content"]
|
||||
!= old_response[0]["FindDescriptor"]["entities"][0]["content"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_with_relevance_score(vdms_client: vdms.vdms) -> None:
|
||||
"""Test to make sure the relevance score is scaled to 0-1."""
|
||||
collection_name = "test_with_relevance_score"
|
||||
embedding_function = FakeEmbeddings()
|
||||
texts = ["foo", "bar", "baz"]
|
||||
ids = [f"test_relevance_scores_{i}" for i in range(len(texts))]
|
||||
metadatas = [{"page": str(i)} for i in range(1, len(texts) + 1)]
|
||||
@@ -320,7 +329,7 @@ def test_with_relevance_score(vdms_client: vdms.vdms) -> None:
|
||||
collection_name=collection_name,
|
||||
client=vdms_client,
|
||||
)
|
||||
output = docsearch.similarity_search_with_relevance_scores("foo", k=3)
|
||||
output = docsearch._similarity_search_with_relevance_scores("foo", k=3)
|
||||
assert output == [
|
||||
(Document(page_content="foo", metadata={"page": "1", "id": ids[0]}), 0.0),
|
||||
(Document(page_content="bar", metadata={"page": "2", "id": ids[1]}), 0.25),
|
||||
@@ -329,24 +338,24 @@ def test_with_relevance_score(vdms_client: vdms.vdms) -> None:
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_add_documents_no_metadata(vdms_client: vdms.vdms) -> None:
|
||||
collection_name = "test_add_documents_no_metadata"
|
||||
embedding_function = FakeEmbeddings()
|
||||
db = VDMS( # type: ignore[call-arg]
|
||||
collection_name=collection_name,
|
||||
embedding_function=embedding_function,
|
||||
embedding=embedding_function,
|
||||
client=vdms_client,
|
||||
)
|
||||
db.add_documents([Document(page_content="foo")])
|
||||
|
||||
|
||||
@pytest.mark.requires("vdms")
|
||||
@pytest.mark.enable_socket
|
||||
def test_add_documents_mixed_metadata(vdms_client: vdms.vdms) -> None:
|
||||
collection_name = "test_add_documents_mixed_metadata"
|
||||
embedding_function = FakeEmbeddings()
|
||||
db = VDMS( # type: ignore[call-arg]
|
||||
collection_name=collection_name,
|
||||
embedding_function=embedding_function,
|
||||
embedding=embedding_function,
|
||||
client=vdms_client,
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from langchain_core.messages import (
|
||||
HumanMessage,
|
||||
HumanMessageChunk,
|
||||
SystemMessage,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.pydantic_v1 import SecretStr
|
||||
from pytest import CaptureFixture, MonkeyPatch
|
||||
@@ -58,6 +59,18 @@ def test__convert_message_to_dict_system() -> None:
|
||||
assert result == expected_output
|
||||
|
||||
|
||||
def test__convert_message_to_dict_tool() -> None:
|
||||
message = ToolMessage(name="foo", content="bar", tool_call_id="abc123")
|
||||
result = _convert_message_to_dict(message)
|
||||
expected_output = {
|
||||
"name": "foo",
|
||||
"content": "bar",
|
||||
"tool_call_id": "abc123",
|
||||
"role": "tool",
|
||||
}
|
||||
assert result == expected_output
|
||||
|
||||
|
||||
def test__convert_message_to_dict_function() -> None:
|
||||
message = FunctionMessage(name="foo", content="bar")
|
||||
with pytest.raises(TypeError) as e:
|
||||
|
||||
@@ -11,6 +11,7 @@ EXPECTED_ALL = [
|
||||
"ChatDatabricks",
|
||||
"ChatDeepInfra",
|
||||
"ChatEverlyAI",
|
||||
"ChatEdenAI",
|
||||
"ChatFireworks",
|
||||
"ChatFriendli",
|
||||
"ChatGooglePalm",
|
||||
@@ -53,6 +54,7 @@ EXPECTED_ALL = [
|
||||
"VolcEngineMaasChat",
|
||||
"ChatOctoAI",
|
||||
"ChatSnowflakeCortex",
|
||||
"ChatYi",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ def test_llm_chat(monkeypatch: MonkeyPatch, test_model_id: str) -> None:
|
||||
{
|
||||
"text": response_text,
|
||||
"finish_reason": "completed",
|
||||
"is_search_required": None,
|
||||
"search_queries": None,
|
||||
"citations": None,
|
||||
"documents": None,
|
||||
"tool_calls": None,
|
||||
}
|
||||
),
|
||||
"model_id": "cohere.command-r-16k",
|
||||
|
||||
@@ -142,6 +142,7 @@ EXPECTED_ALL = [
|
||||
"S3DirectoryLoader",
|
||||
"S3FileLoader",
|
||||
"ScrapflyLoader",
|
||||
"ScrapingAntLoader",
|
||||
"SQLDatabaseLoader",
|
||||
"SRTLoader",
|
||||
"SeleniumURLLoader",
|
||||
|
||||
@@ -98,6 +98,7 @@ EXPECT_ALL = [
|
||||
"QianfanLLMEndpoint",
|
||||
"YandexGPT",
|
||||
"Yuan2",
|
||||
"YiLLM",
|
||||
"You",
|
||||
"VolcEngineMaasLLM",
|
||||
"WatsonxLLM",
|
||||
|
||||
@@ -31,7 +31,7 @@ def test_pass_headers_if_provided(monkeypatch: MonkeyPatch) -> None:
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
def mock_post(url, headers, json, stream, timeout): # type: ignore[no-untyped-def]
|
||||
def mock_post(url, headers, json, stream, timeout, auth): # type: ignore[no-untyped-def]
|
||||
assert url == "https://ollama-hostname:8000/api/generate"
|
||||
assert headers == {
|
||||
"Content-Type": "application/json",
|
||||
@@ -49,10 +49,35 @@ def test_pass_headers_if_provided(monkeypatch: MonkeyPatch) -> None:
|
||||
llm.invoke("Test prompt")
|
||||
|
||||
|
||||
def test_pass_auth_if_provided(monkeypatch: MonkeyPatch) -> None:
|
||||
llm = Ollama(
|
||||
base_url="https://ollama-hostname:8000",
|
||||
model="foo",
|
||||
auth=("Test-User", "Test-Password"),
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
def mock_post(url, headers, json, stream, timeout, auth): # type: ignore[no-untyped-def]
|
||||
assert url == "https://ollama-hostname:8000/api/generate"
|
||||
assert headers == {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
assert json is not None
|
||||
assert stream is True
|
||||
assert timeout == 300
|
||||
assert auth == ("Test-User", "Test-Password")
|
||||
|
||||
return mock_response_stream()
|
||||
|
||||
monkeypatch.setattr(requests, "post", mock_post)
|
||||
|
||||
llm.invoke("Test prompt")
|
||||
|
||||
|
||||
def test_handle_if_headers_not_provided(monkeypatch: MonkeyPatch) -> None:
|
||||
llm = Ollama(base_url="https://ollama-hostname:8000", model="foo", timeout=300)
|
||||
|
||||
def mock_post(url, headers, json, stream, timeout): # type: ignore[no-untyped-def]
|
||||
def mock_post(url, headers, json, stream, timeout, auth): # type: ignore[no-untyped-def]
|
||||
assert url == "https://ollama-hostname:8000/api/generate"
|
||||
assert headers == {
|
||||
"Content-Type": "application/json",
|
||||
@@ -72,7 +97,7 @@ def test_handle_kwargs_top_level_parameters(monkeypatch: MonkeyPatch) -> None:
|
||||
"""Test that top level params are sent to the endpoint as top level params"""
|
||||
llm = Ollama(base_url="https://ollama-hostname:8000", model="foo", timeout=300)
|
||||
|
||||
def mock_post(url, headers, json, stream, timeout): # type: ignore[no-untyped-def]
|
||||
def mock_post(url, headers, json, stream, timeout, auth): # type: ignore[no-untyped-def]
|
||||
assert url == "https://ollama-hostname:8000/api/generate"
|
||||
assert headers == {
|
||||
"Content-Type": "application/json",
|
||||
@@ -120,7 +145,7 @@ def test_handle_kwargs_with_unknown_param(monkeypatch: MonkeyPatch) -> None:
|
||||
"""
|
||||
llm = Ollama(base_url="https://ollama-hostname:8000", model="foo", timeout=300)
|
||||
|
||||
def mock_post(url, headers, json, stream, timeout): # type: ignore[no-untyped-def]
|
||||
def mock_post(url, headers, json, stream, timeout, auth): # type: ignore[no-untyped-def]
|
||||
assert url == "https://ollama-hostname:8000/api/generate"
|
||||
assert headers == {
|
||||
"Content-Type": "application/json",
|
||||
@@ -169,7 +194,7 @@ def test_handle_kwargs_with_options(monkeypatch: MonkeyPatch) -> None:
|
||||
"""
|
||||
llm = Ollama(base_url="https://ollama-hostname:8000", model="foo", timeout=300)
|
||||
|
||||
def mock_post(url, headers, json, stream, timeout): # type: ignore[no-untyped-def]
|
||||
def mock_post(url, headers, json, stream, timeout, auth): # type: ignore[no-untyped-def]
|
||||
assert url == "https://ollama-hostname:8000/api/generate"
|
||||
assert headers == {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
from typing import Dict, Tuple
|
||||
|
||||
import pytest as pytest
|
||||
from langchain_core.structured_query import (
|
||||
Comparator,
|
||||
Comparison,
|
||||
Operation,
|
||||
Operator,
|
||||
StructuredQuery,
|
||||
)
|
||||
|
||||
from langchain_community.query_constructors.hanavector import HanaTranslator
|
||||
|
||||
DEFAULT_TRANSLATOR = HanaTranslator()
|
||||
|
||||
|
||||
def test_visit_comparison() -> None:
|
||||
comp = Comparison(comparator=Comparator.LT, attribute="foo", value=1)
|
||||
expected = {"foo": {"$lt": 1}}
|
||||
actual = DEFAULT_TRANSLATOR.visit_comparison(comp)
|
||||
assert expected == actual
|
||||
|
||||
|
||||
def test_visit_operation() -> None:
|
||||
op = Operation(
|
||||
operator=Operator.AND,
|
||||
arguments=[
|
||||
Comparison(comparator=Comparator.LT, attribute="foo", value=2),
|
||||
Comparison(comparator=Comparator.EQ, attribute="bar", value="baz"),
|
||||
Comparison(comparator=Comparator.GT, attribute="abc", value=2.0),
|
||||
],
|
||||
)
|
||||
expected = {
|
||||
"$and": [{"foo": {"$lt": 2}}, {"bar": {"$eq": "baz"}}, {"abc": {"$gt": 2.0}}]
|
||||
}
|
||||
actual = DEFAULT_TRANSLATOR.visit_operation(op)
|
||||
assert expected == actual
|
||||
|
||||
|
||||
def test_visit_structured_query() -> None:
|
||||
query = "What is the capital of France?"
|
||||
structured_query = StructuredQuery(
|
||||
query=query,
|
||||
filter=None,
|
||||
)
|
||||
expected: Tuple[str, Dict] = (query, {})
|
||||
actual = DEFAULT_TRANSLATOR.visit_structured_query(structured_query)
|
||||
assert expected == actual
|
||||
|
||||
comp = Comparison(comparator=Comparator.LT, attribute="foo", value=1)
|
||||
structured_query = StructuredQuery(
|
||||
query=query,
|
||||
filter=comp,
|
||||
)
|
||||
expected = (query, {"filter": {"foo": {"$lt": 1}}})
|
||||
actual = DEFAULT_TRANSLATOR.visit_structured_query(structured_query)
|
||||
assert expected == actual
|
||||
|
||||
op = Operation(
|
||||
operator=Operator.AND,
|
||||
arguments=[
|
||||
Comparison(comparator=Comparator.LT, attribute="foo", value=2),
|
||||
Comparison(comparator=Comparator.EQ, attribute="bar", value="baz"),
|
||||
Comparison(comparator=Comparator.GT, attribute="abc", value=2.0),
|
||||
],
|
||||
)
|
||||
structured_query = StructuredQuery(
|
||||
query=query,
|
||||
filter=op,
|
||||
)
|
||||
expected = (
|
||||
query,
|
||||
{
|
||||
"filter": {
|
||||
"$and": [
|
||||
{"foo": {"$lt": 2}},
|
||||
{"bar": {"$eq": "baz"}},
|
||||
{"abc": {"$gt": 2.0}},
|
||||
]
|
||||
}
|
||||
},
|
||||
)
|
||||
actual = DEFAULT_TRANSLATOR.visit_structured_query(structured_query)
|
||||
assert expected == actual
|
||||
@@ -493,7 +493,7 @@ def test_similarity_search(index_details: dict, query_type: Optional[str]) -> No
|
||||
limit = 7
|
||||
|
||||
search_result = vectorsearch.similarity_search(
|
||||
query, k=limit, filters=filters, query_type=query_type
|
||||
query, k=limit, filter=filters, query_type=query_type
|
||||
)
|
||||
if index_details == DELTA_SYNC_INDEX_MANAGED_EMBEDDINGS:
|
||||
index.similarity_search.assert_called_once_with(
|
||||
@@ -518,6 +518,27 @@ def test_similarity_search(index_details: dict, query_type: Optional[str]) -> No
|
||||
assert all([DEFAULT_PRIMARY_KEY in d.metadata for d in search_result])
|
||||
|
||||
|
||||
@pytest.mark.requires("databricks", "databricks.vector_search")
|
||||
def test_similarity_search_both_filter_and_filters_passed() -> None:
|
||||
index = mock_index(DIRECT_ACCESS_INDEX)
|
||||
index.similarity_search.return_value = EXAMPLE_SEARCH_RESPONSE
|
||||
vectorsearch = default_databricks_vector_search(index)
|
||||
query = "foo"
|
||||
filter = {"some filter": True}
|
||||
filters = {"some other filter": False}
|
||||
|
||||
vectorsearch.similarity_search(query, filter=filter, filters=filters)
|
||||
index.similarity_search.assert_called_once_with(
|
||||
columns=[DEFAULT_PRIMARY_KEY, DEFAULT_TEXT_COLUMN],
|
||||
query_vector=DEFAULT_EMBEDDING_MODEL.embed_query(query),
|
||||
# `filter` should prevail over `filters`
|
||||
filters=filter,
|
||||
num_results=4,
|
||||
query_text=None,
|
||||
query_type=None,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.requires("databricks", "databricks.vector_search")
|
||||
@pytest.mark.parametrize(
|
||||
"index_details, columns, expected_columns",
|
||||
@@ -576,7 +597,7 @@ def test_mmr_parameters(index_details: dict) -> None:
|
||||
"k": limit,
|
||||
"fetch_k": fetch_k,
|
||||
"lambda_mult": lambda_mult,
|
||||
"filters": filters,
|
||||
"filter": filters,
|
||||
},
|
||||
)
|
||||
search_result = retriever.invoke(query)
|
||||
@@ -625,7 +646,7 @@ def test_similarity_search_by_vector(index_details: dict) -> None:
|
||||
limit = 7
|
||||
|
||||
search_result = vectorsearch.similarity_search_by_vector(
|
||||
query_embedding, k=limit, filters=filters
|
||||
query_embedding, k=limit, filter=filters
|
||||
)
|
||||
index.similarity_search.assert_called_once_with(
|
||||
columns=[DEFAULT_PRIMARY_KEY, DEFAULT_TEXT_COLUMN],
|
||||
@@ -681,3 +702,32 @@ def test_similarity_search_by_vector_not_supported_for_managed_embedding() -> No
|
||||
"`similarity_search_by_vector` is not supported for index with "
|
||||
"Databricks-managed embeddings." in str(ex.value)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.requires("databricks", "databricks.vector_search")
|
||||
@pytest.mark.parametrize(
|
||||
"method",
|
||||
[
|
||||
"similarity_search",
|
||||
"similarity_search_with_score",
|
||||
"similarity_search_by_vector",
|
||||
"similarity_search_by_vector_with_score",
|
||||
"max_marginal_relevance_search",
|
||||
"max_marginal_relevance_search_by_vector",
|
||||
],
|
||||
)
|
||||
def test_filter_arg_alias(method: str) -> None:
|
||||
index = mock_index(DIRECT_ACCESS_INDEX)
|
||||
vectorsearch = default_databricks_vector_search(index)
|
||||
query = "foo"
|
||||
query_embedding = DEFAULT_EMBEDDING_MODEL.embed_query("foo")
|
||||
filters = {"some filter": True}
|
||||
limit = 7
|
||||
|
||||
if "by_vector" in method:
|
||||
getattr(vectorsearch, method)(query_embedding, k=limit, filters=filters)
|
||||
else:
|
||||
getattr(vectorsearch, method)(query, k=limit, filters=filters)
|
||||
|
||||
index_call_args = index.similarity_search.call_args[1]
|
||||
assert index_call_args["filters"] == filters
|
||||
|
||||
@@ -60,6 +60,7 @@ from langchain_core.pydantic_v1 import (
|
||||
Field,
|
||||
root_validator,
|
||||
)
|
||||
from langchain_core.rate_limiters import BaseRateLimiter
|
||||
from langchain_core.runnables import RunnableMap, RunnablePassthrough
|
||||
from langchain_core.runnables.config import ensure_config, run_in_executor
|
||||
from langchain_core.tracers._streaming import _StreamingCallbackHandler
|
||||
@@ -210,6 +211,9 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
callback_manager: Optional[BaseCallbackManager] = Field(default=None, exclude=True)
|
||||
"""[DEPRECATED] Callback manager to add to the run trace."""
|
||||
|
||||
rate_limiter: Optional[BaseRateLimiter] = Field(default=None, exclude=True)
|
||||
"""An optional rate limiter to use for limiting the number of requests."""
|
||||
|
||||
@root_validator(pre=True)
|
||||
def raise_deprecation(cls, values: Dict) -> Dict:
|
||||
"""Raise deprecation warning if callback_manager is used.
|
||||
@@ -341,6 +345,10 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
batch_size=1,
|
||||
)
|
||||
generation: Optional[ChatGenerationChunk] = None
|
||||
|
||||
if self.rate_limiter:
|
||||
self.rate_limiter.acquire(blocking=True)
|
||||
|
||||
try:
|
||||
for chunk in self._stream(messages, stop=stop, **kwargs):
|
||||
if chunk.message.id is None:
|
||||
@@ -412,6 +420,9 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
batch_size=1,
|
||||
)
|
||||
|
||||
if self.rate_limiter:
|
||||
self.rate_limiter.acquire(blocking=True)
|
||||
|
||||
generation: Optional[ChatGenerationChunk] = None
|
||||
try:
|
||||
async for chunk in self._astream(
|
||||
@@ -742,6 +753,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
raise ValueError(
|
||||
"Asked to cache, but no cache found at `langchain.cache`."
|
||||
)
|
||||
|
||||
# Apply the rate limiter after checking the cache, since
|
||||
# we usually don't want to rate limit cache lookups, but
|
||||
# we do want to rate limit API requests.
|
||||
if self.rate_limiter:
|
||||
self.rate_limiter.acquire(blocking=True)
|
||||
|
||||
# If stream is not explicitly set, check if implicitly requested by
|
||||
# astream_events() or astream_log(). Bail out if _stream not implemented
|
||||
if type(self)._stream != BaseChatModel._stream and kwargs.pop(
|
||||
@@ -822,6 +840,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
raise ValueError(
|
||||
"Asked to cache, but no cache found at `langchain.cache`."
|
||||
)
|
||||
|
||||
# Apply the rate limiter after checking the cache, since
|
||||
# we usually don't want to rate limit cache lookups, but
|
||||
# we do want to rate limit API requests.
|
||||
if self.rate_limiter:
|
||||
self.rate_limiter.acquire(blocking=True)
|
||||
|
||||
# If stream is not explicitly set, check if implicitly requested by
|
||||
# astream_events() or astream_log(). Bail out if _astream not implemented
|
||||
if (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user