mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-16 01:59:52 +00:00
Compare commits
70 Commits
bagatur/do
...
jacob/chat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f90e665413 | ||
|
|
fad076fa06 | ||
|
|
59ffccf27d | ||
|
|
ac85fca6f0 | ||
|
|
f29ad020a0 | ||
|
|
b67561890b | ||
|
|
b970bfe8da | ||
|
|
36e432672a | ||
|
|
38425c99d2 | ||
|
|
3c387bc12d | ||
|
|
481493dbce | ||
|
|
f01fb47597 | ||
|
|
508bde7f40 | ||
|
|
5e73603e8a | ||
|
|
3e87b67a3c | ||
|
|
c314137f5b | ||
|
|
27665e3546 | ||
|
|
5975bf39ec | ||
|
|
4915c3cd86 | ||
|
|
e86fd946c8 | ||
|
|
0bc397957b | ||
|
|
52ccae3fb1 | ||
|
|
570b4f8e66 | ||
|
|
4e189cd89a | ||
|
|
a936472512 | ||
|
|
6543e585a5 | ||
|
|
6a75ef74ca | ||
|
|
70ff54eace | ||
|
|
5b5115c408 | ||
|
|
a989f82027 | ||
|
|
e30c6662df | ||
|
|
08d3fd7f2e | ||
|
|
42db96477f | ||
|
|
a79345f199 | ||
|
|
bcc71d1a57 | ||
|
|
68f7468754 | ||
|
|
61e876aad8 | ||
|
|
5df8ab574e | ||
|
|
f3d61a6e47 | ||
|
|
61b200947f | ||
|
|
75ad0bba2d | ||
|
|
1e3ce338ca | ||
|
|
6c89507988 | ||
|
|
31790d15ec | ||
|
|
db80832e4f | ||
|
|
ef42d9d559 | ||
|
|
148347e858 | ||
|
|
35e60728b7 | ||
|
|
6023953ea7 | ||
|
|
3b8eba32f9 | ||
|
|
e86e66bad7 | ||
|
|
e510cfaa23 | ||
|
|
355ef2a4a6 | ||
|
|
9dd7cbb447 | ||
|
|
0785432e7b | ||
|
|
adc008407e | ||
|
|
c4e9c9ca29 | ||
|
|
b8768bd6e7 | ||
|
|
f6a05e964b | ||
|
|
c173a69908 | ||
|
|
f9976b9630 | ||
|
|
5c2538b9f7 | ||
|
|
a91181fe6d | ||
|
|
04651f0248 | ||
|
|
1113700b09 | ||
|
|
54dd8e52a8 | ||
|
|
fe382fcf20 | ||
|
|
06f66f25e1 | ||
|
|
b1b351b37e | ||
|
|
4fad71882e |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -13,7 +13,7 @@ There are many ways to contribute to LangChain. Here are some common ways people
|
||||
|
||||
- [**Documentation**](https://python.langchain.com/docs/contributing/documentation): Help improve our docs, including this one!
|
||||
- [**Code**](https://python.langchain.com/docs/contributing/code): Help us write code, fix bugs, or improve our infrastructure.
|
||||
- [**Integrations**](https://python.langchain.com/docs/contributing/integration): Help us integrate with your favorite vendors and tools.
|
||||
- [**Integrations**](https://python.langchain.com/docs/contributing/integrations): Help us integrate with your favorite vendors and tools.
|
||||
|
||||
### 🚩GitHub Issues
|
||||
|
||||
|
||||
18
.github/DISCUSSION_TEMPLATE/ideas.yml
vendored
18
.github/DISCUSSION_TEMPLATE/ideas.yml
vendored
@@ -1,5 +1,4 @@
|
||||
labels: ["Idea"]
|
||||
description: Suggest ideas for LangChain features and improvements.
|
||||
labels: [idea]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
@@ -7,9 +6,11 @@ body:
|
||||
label: Checked
|
||||
description: Please confirm and check all the following options.
|
||||
options:
|
||||
- label: I searched existing ideas and did not find a similar one.
|
||||
- label: I searched existing ideas and did not find a similar one
|
||||
required: true
|
||||
- label: I added a very descriptive title to this idea.
|
||||
- label: I added a very descriptive title
|
||||
required: true
|
||||
- label: I've clearly described the feature request and motivation for it
|
||||
required: true
|
||||
- type: textarea
|
||||
id: feature-request
|
||||
@@ -19,7 +20,6 @@ body:
|
||||
label: Feature request
|
||||
description: |
|
||||
A clear and concise description of the feature proposal. Please provide links to any relevant GitHub repos, papers, or other resources if relevant.
|
||||
|
||||
- type: textarea
|
||||
id: motivation
|
||||
validations:
|
||||
@@ -28,3 +28,11 @@ body:
|
||||
label: Motivation
|
||||
description: |
|
||||
Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too.
|
||||
- type: textarea
|
||||
id: proposal
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: Proposal (If applicable)
|
||||
description: |
|
||||
If you would like to propose a solution, please describe it here.
|
||||
|
||||
122
.github/DISCUSSION_TEMPLATE/q-a.yml
vendored
Normal file
122
.github/DISCUSSION_TEMPLATE/q-a.yml
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
labels: [Question]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for your interest in 🦜️🔗 LangChain!
|
||||
|
||||
Please follow these instructions, fill every question, and do every step. 🙏
|
||||
|
||||
We're asking for this because answering questions and solving problems in GitHub takes a lot of time --
|
||||
this is time that we cannot spend on adding new features, fixing bugs, write documentation or reviewing pull requests.
|
||||
|
||||
By asking questions in a structured way (following this) it will be much easier to help you.
|
||||
|
||||
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
|
||||
|
||||
As there are too many questions, we will **DISCARD** and close the incomplete ones.
|
||||
|
||||
That will allow us (and others) to focus on helping people like you that follow the whole process. 🤓
|
||||
|
||||
Relevant links to check before opening a question to see if your question has already been answered, fixed or
|
||||
if there's another way to solve your problem:
|
||||
|
||||
[LangChain documentation with the integrated search](https://python.langchain.com/docs/get_started/introduction),
|
||||
[API Reference](https://api.python.langchain.com/en/stable/),
|
||||
[GitHub search](https://github.com/langchain-ai/langchain),
|
||||
[LangChain Github Discussions](https://github.com/langchain-ai/langchain/discussions),
|
||||
[LangChain Github Issues](https://github.com/langchain-ai/langchain/issues?q=is%3Aissue),
|
||||
[LangChain ChatBot](https://chat.langchain.com/)
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: Checked other resources
|
||||
description: Please confirm and check all the following options.
|
||||
options:
|
||||
- label: I added a very descriptive title to this question.
|
||||
required: true
|
||||
- label: I searched the LangChain documentation with the integrated search.
|
||||
required: true
|
||||
- label: I used the GitHub search to find a similar question and didn't find it.
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: help
|
||||
attributes:
|
||||
label: Commit to Help
|
||||
description: |
|
||||
After submitting this, I commit to one of:
|
||||
|
||||
* Read open questions until I find 2 where I can help someone and add a comment to help there.
|
||||
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
||||
* Once my question is answered, I will mark the answer as "accepted".
|
||||
options:
|
||||
- label: I commit to help with one of those options 👆
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: Example Code
|
||||
description: |
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
|
||||
|
||||
If a maintainer can copy it, run it, and see it right away, there's a much higher chance that you'll be able to get help.
|
||||
|
||||
**Important!**
|
||||
|
||||
* Use code tags (e.g., ```python ... ```) to correctly [format your code](https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting).
|
||||
* INCLUDE the language label (e.g. `python`) after the first three backticks to enable syntax highlighting. (e.g., ```python rather than ```).
|
||||
* Reduce your code to the minimum required to reproduce the issue if possible. This makes it much easier for others to help you.
|
||||
* Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.
|
||||
|
||||
placeholder: |
|
||||
from langchain_core.runnables import RunnableLambda
|
||||
|
||||
def bad_code(inputs) -> int:
|
||||
raise NotImplementedError('For demo purpose')
|
||||
|
||||
chain = RunnableLambda(bad_code)
|
||||
chain.invoke('Hello!')
|
||||
render: python
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
What is the problem, question, or error?
|
||||
|
||||
Write a short description explaining what you are doing, what you expect to happen, and what is currently happening.
|
||||
placeholder: |
|
||||
* I'm trying to use the `langchain` library to do X.
|
||||
* I expect to see Y.
|
||||
* Instead, it does Z.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: |
|
||||
Please share your system info with us.
|
||||
|
||||
"pip freeze | grep langchain"
|
||||
platform (windows / linux / mac)
|
||||
python version
|
||||
|
||||
OR if you're on a recent version of langchain-core you can paste the output of:
|
||||
|
||||
python -m langchain_core.sys_info
|
||||
placeholder: |
|
||||
"pip freeze | grep langchain"
|
||||
platform
|
||||
python version
|
||||
|
||||
Alternatively, if you're on a recent version of langchain-core you can paste the output of:
|
||||
|
||||
python -m langchain_core.sys_info
|
||||
|
||||
These will only surface LangChain packages, don't forget to include any other relevant
|
||||
packages you're using (if you're not sure what's relevant, you can paste the entire output of `pip freeze`).
|
||||
validations:
|
||||
required: true
|
||||
1
.github/workflows/_integration_test.yml
vendored
1
.github/workflows/_integration_test.yml
vendored
@@ -55,6 +55,7 @@ jobs:
|
||||
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
|
||||
GOOGLE_SEARCH_API_KEY: ${{ secrets.GOOGLE_SEARCH_API_KEY }}
|
||||
GOOGLE_CSE_ID: ${{ secrets.GOOGLE_CSE_ID }}
|
||||
EXA_API_KEY: ${{ secrets.EXA_API_KEY }}
|
||||
run: |
|
||||
make integration_tests
|
||||
|
||||
|
||||
13
.github/workflows/langchain_cli_release.yml
vendored
13
.github/workflows/langchain_cli_release.yml
vendored
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: libs/cli Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_release.yml
|
||||
with:
|
||||
working-directory: libs/cli
|
||||
secrets: inherit
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: libs/community Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_release.yml
|
||||
with:
|
||||
working-directory: libs/community
|
||||
secrets: inherit
|
||||
13
.github/workflows/langchain_core_release.yml
vendored
13
.github/workflows/langchain_core_release.yml
vendored
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: libs/core Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_release.yml
|
||||
with:
|
||||
working-directory: libs/core
|
||||
secrets: inherit
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: libs/experimental Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_release.yml
|
||||
with:
|
||||
working-directory: libs/experimental
|
||||
secrets: inherit
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: Experimental Test Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_test_release.yml
|
||||
with:
|
||||
working-directory: libs/experimental
|
||||
secrets: inherit
|
||||
13
.github/workflows/langchain_openai_release.yml
vendored
13
.github/workflows/langchain_openai_release.yml
vendored
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: libs/core Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_release.yml
|
||||
with:
|
||||
working-directory: libs/core
|
||||
secrets: inherit
|
||||
27
.github/workflows/langchain_release.yml
vendored
27
.github/workflows/langchain_release.yml
vendored
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: libs/langchain Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_release.yml
|
||||
with:
|
||||
working-directory: libs/langchain
|
||||
secrets: inherit
|
||||
|
||||
# N.B.: It's possible that PyPI doesn't make the new release visible / available
|
||||
# immediately after publishing. If that happens, the docker build might not
|
||||
# create a new docker image for the new release, since it won't see it.
|
||||
#
|
||||
# If this ends up being a problem, add a check to the end of the `_release.yml`
|
||||
# workflow that prevents the workflow from finishing until the new release
|
||||
# is visible and installable on PyPI.
|
||||
release-docker:
|
||||
needs:
|
||||
- release
|
||||
uses:
|
||||
./.github/workflows/langchain_release_docker.yml
|
||||
secrets: inherit
|
||||
13
.github/workflows/langchain_test_release.yml
vendored
13
.github/workflows/langchain_test_release.yml
vendored
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: Test Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses:
|
||||
./.github/workflows/_test_release.yml
|
||||
with:
|
||||
working-directory: libs/langchain
|
||||
secrets: inherit
|
||||
@@ -174,6 +174,7 @@ jobs:
|
||||
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
|
||||
GOOGLE_SEARCH_API_KEY: ${{ secrets.GOOGLE_SEARCH_API_KEY }}
|
||||
GOOGLE_CSE_ID: ${{ secrets.GOOGLE_CSE_ID }}
|
||||
EXA_API_KEY: ${{ secrets.EXA_API_KEY }}
|
||||
run: make integration_tests
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
formats:
|
||||
- pdf
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
|
||||
File diff suppressed because one or more lines are too long
272
docs/docs/integrations/chat/edenai.ipynb
Normal file
272
docs/docs/integrations/chat/edenai.ipynb
Normal file
@@ -0,0 +1,272 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Eden AI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Eden AI is revolutionizing the AI landscape by uniting the best AI providers, empowering users to unlock limitless possibilities and tap into the true potential of artificial intelligence. With an all-in-one comprehensive and hassle-free platform, it allows users to deploy AI features to production lightning fast, enabling effortless access to the full breadth of AI capabilities via a single API. (website: https://edenai.co/)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This example goes over how to use LangChain to interact with Eden AI models\n",
|
||||
"\n",
|
||||
"-----------------------------------------------------------------------------------"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"`EdenAI` goes beyond mere model invocation. It empowers you with advanced features, including:\n",
|
||||
"\n",
|
||||
"- **Multiple Providers**: Gain access to a diverse range of language models offered by various providers, giving you the freedom to choose the best-suited model for your use case.\n",
|
||||
"\n",
|
||||
"- **Fallback Mechanism**: Set a fallback mechanism to ensure seamless operations even if the primary provider is unavailable, you can easily switches to an alternative provider.\n",
|
||||
"\n",
|
||||
"- **Usage Tracking**: Track usage statistics on a per-project and per-API key basis. This feature allows you to monitor and manage resource consumption effectively.\n",
|
||||
"\n",
|
||||
"- **Monitoring and Observability**: `EdenAI` provides comprehensive monitoring and observability tools on the platform. Monitor the performance of your language models, analyze usage patterns, and gain valuable insights to optimize your applications.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Accessing the EDENAI's API requires an API key, \n",
|
||||
"\n",
|
||||
"which you can get by creating an account https://app.edenai.run/user/register and heading here https://app.edenai.run/admin/iam/api-keys\n",
|
||||
"\n",
|
||||
"Once we have a key we'll want to set it as an environment variable by running:\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"export EDENAI_API_KEY=\"...\"\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"You can find more details on the API reference : https://docs.edenai.co/reference"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If you'd prefer not to set an environment variable you can pass the key in directly via the edenai_api_key named parameter\n",
|
||||
"\n",
|
||||
" when initiating the EdenAI Chat Model class."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.chat_models.edenai import ChatEdenAI\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"chat = ChatEdenAI(\n",
|
||||
" edenai_api_key=\"...\", provider=\"openai\", temperature=0.2, max_tokens=250\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Hello! How can I assist you today?')"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"messages = [HumanMessage(content=\"Hello !\")]\n",
|
||||
"chat.invoke(messages)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Hello! How can I assist you today?')"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"await chat.ainvoke(messages)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Streaming and Batching\n",
|
||||
"\n",
|
||||
"`ChatEdenAI` supports streaming and batching. Below is an example."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello! How can I assist you today?"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for chunk in chat.stream(messages):\n",
|
||||
" print(chunk.content, end=\"\", flush=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[AIMessage(content='Hello! How can I assist you today?')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chat.batch([messages])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Fallback mecanism\n",
|
||||
"\n",
|
||||
"With Eden AI you can set a fallback mechanism to ensure seamless operations even if the primary provider is unavailable, you can easily switches to an alternative provider."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"chat = ChatEdenAI(\n",
|
||||
" edenai_api_key=\"...\",\n",
|
||||
" provider=\"openai\",\n",
|
||||
" temperature=0.2,\n",
|
||||
" max_tokens=250,\n",
|
||||
" fallback_providers=\"google\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"In this example, you can use Google as a backup provider if OpenAI encounters any issues.\n",
|
||||
"\n",
|
||||
"For more information and details about Eden AI, check out this link: : https://docs.edenai.co/docs/additional-parameters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chaining Calls\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate.from_template(\n",
|
||||
" \"What is a good name for a company that makes {product}?\"\n",
|
||||
")\n",
|
||||
"chain = prompt | chat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='VitalBites')"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chain.invoke({\"product\": \"healthy snacks\"})"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "langchain-pr",
|
||||
"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.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Hugging Face Chat Wrapper\n",
|
||||
"# Hugging Face\n",
|
||||
"\n",
|
||||
"This notebook shows how to get started using Hugging Face LLM's as chat models.\n",
|
||||
"This notebook shows how to get started using `Hugging Face` LLM's as chat models.\n",
|
||||
"\n",
|
||||
"In particular, we will:\n",
|
||||
"1. Utilize the [HuggingFaceTextGenInference](https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/llms/huggingface_text_gen_inference.py), [HuggingFaceEndpoint](https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/llms/huggingface_endpoint.py), or [HuggingFaceHub](https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/llms/huggingface_hub.py) integrations to instantiate an `LLM`.\n",
|
||||
@@ -49,7 +49,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### `HuggingFaceTextGenInference`"
|
||||
"### `HuggingFaceTextGenInference`"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -93,7 +93,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### `HuggingFaceEndpoint`"
|
||||
"### `HuggingFaceEndpoint`"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -121,7 +121,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### `HuggingFaceHub`"
|
||||
"### `HuggingFaceHub`"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -291,7 +291,7 @@
|
||||
"source": [
|
||||
"## 3. Take it for a spin as an agent!\n",
|
||||
"\n",
|
||||
"Here we'll test out `Zephyr-7B-beta` as a zero-shot ReAct Agent. The example below is taken from [here](https://python.langchain.com/docs/modules/agents/agent_types/react#using-chat-models).\n",
|
||||
"Here we'll test out `Zephyr-7B-beta` as a zero-shot `ReAct` Agent. The example below is taken from [here](https://python.langchain.com/docs/modules/agents/agent_types/react#using-chat-models).\n",
|
||||
"\n",
|
||||
"> Note: To run this section, you'll need to have a [SerpAPI Token](https://serpapi.com/) saved as an environment variable: `SERPAPI_API_KEY`"
|
||||
]
|
||||
@@ -448,7 +448,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.5"
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
218
docs/docs/integrations/chat/litellm_router.ipynb
Normal file
218
docs/docs/integrations/chat/litellm_router.ipynb
Normal file
@@ -0,0 +1,218 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "59148044",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"sidebar_label: LiteLLM Router\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "247da7a6",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "bf733a38-db84-4363-89e2-de6735c37230",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# ChatLiteLLMRouter\n",
|
||||
"\n",
|
||||
"[LiteLLM](https://github.com/BerriAI/litellm) is a library that simplifies calling Anthropic, Azure, Huggingface, Replicate, etc. \n",
|
||||
"\n",
|
||||
"This notebook covers how to get started with using Langchain + the LiteLLM Router I/O library. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "d4a7c55d-b235-4ca4-a579-c90cc9570da9",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.schema import HumanMessage\n",
|
||||
"from langchain_community.chat_models import ChatLiteLLMRouter\n",
|
||||
"from litellm import Router"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "70cf04e8-423a-4ff6-8b09-f11fb711c817",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_list = [\n",
|
||||
" {\n",
|
||||
" \"model_name\": \"gpt-4\",\n",
|
||||
" \"litellm_params\": {\n",
|
||||
" \"model\": \"azure/gpt-4-1106-preview\",\n",
|
||||
" \"api_key\": \"<your-api-key>\",\n",
|
||||
" \"api_version\": \"2023-05-15\",\n",
|
||||
" \"api_base\": \"https://<your-endpoint>.openai.azure.com/\",\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"model_name\": \"gpt-4\",\n",
|
||||
" \"litellm_params\": {\n",
|
||||
" \"model\": \"azure/gpt-4-1106-preview\",\n",
|
||||
" \"api_key\": \"<your-api-key>\",\n",
|
||||
" \"api_version\": \"2023-05-15\",\n",
|
||||
" \"api_base\": \"https://<your-endpoint>.openai.azure.com/\",\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"litellm_router = Router(model_list=model_list)\n",
|
||||
"chat = ChatLiteLLMRouter(router=litellm_router)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "8199ef8f-eb8b-4253-9ea0-6c24a013ca4c",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content=\"J'aime programmer.\")"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"messages = [\n",
|
||||
" HumanMessage(\n",
|
||||
" content=\"Translate this sentence from English to French. I love programming.\"\n",
|
||||
" )\n",
|
||||
"]\n",
|
||||
"chat(messages)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "c361ab1e-8c0c-4206-9e3c-9d1424a12b9c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## `ChatLiteLLMRouter` also supports async and streaming functionality:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "93a21c5c-6ef9-4688-be60-b2e1f94842fb",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.callbacks.manager import CallbackManager\n",
|
||||
"from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "c5fac0e9-05a4-4fc1-a3b3-e5bbb24b971b",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"LLMResult(generations=[[ChatGeneration(text=\"J'adore programmer.\", generation_info={'finish_reason': 'stop'}, message=AIMessage(content=\"J'adore programmer.\"))]], llm_output={'token_usage': {'completion_tokens': 6, 'prompt_tokens': 19, 'total_tokens': 25}, 'model_name': None}, run=[RunInfo(run_id=UUID('75003ec9-1e2b-43b7-a216-10dcc0f75e00'))])"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"await chat.agenerate([messages])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "025be980-e50d-4a68-93dc-c9c7b500ce34",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"J'adore programmer."
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content=\"J'adore programmer.\")"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chat = ChatLiteLLMRouter(\n",
|
||||
" router=litellm_router,\n",
|
||||
" streaming=True,\n",
|
||||
" verbose=True,\n",
|
||||
" callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]),\n",
|
||||
")\n",
|
||||
"chat(messages)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c253883f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.13"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 2,
|
||||
"id": "497736aa",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 3,
|
||||
"id": "009e0036",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -34,19 +34,19 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 8,
|
||||
"id": "910fb6ee",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"loader = ToMarkdownLoader.from_api_key(\n",
|
||||
" url=\"https://python.langchain.com/en/latest/\", api_key=api_key\n",
|
||||
"loader = ToMarkdownLoader(\n",
|
||||
" url=\"https://python.langchain.com/docs/get_started/introduction\", api_key=api_key\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 9,
|
||||
"id": "ac8db139",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -56,7 +56,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 10,
|
||||
"id": "706304e9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -64,130 +64,106 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"## Contents\n",
|
||||
"**LangChain** is a framework for developing applications powered by language models. It enables applications that:\n",
|
||||
"\n",
|
||||
"- [Getting Started](#getting-started)\n",
|
||||
"- [Modules](#modules)\n",
|
||||
"- [Use Cases](#use-cases)\n",
|
||||
"- [Reference Docs](#reference-docs)\n",
|
||||
"- [LangChain Ecosystem](#langchain-ecosystem)\n",
|
||||
"- [Additional Resources](#additional-resources)\n",
|
||||
"- **Are context-aware**: connect a language model to sources of context (prompt instructions, few shot examples, content to ground its response in, etc.)\n",
|
||||
"- **Reason**: rely on a language model to reason (about how to answer based on provided context, what actions to take, etc.)\n",
|
||||
"\n",
|
||||
"## Welcome to LangChain [\\#](\\#welcome-to-langchain \"Permalink to this headline\")\n",
|
||||
"This framework consists of several parts.\n",
|
||||
"\n",
|
||||
"**LangChain** is a framework for developing applications powered by language models. We believe that the most powerful and differentiated applications will not only call out to a language model, but will also be:\n",
|
||||
"- **LangChain Libraries**: The Python and JavaScript libraries. Contains interfaces and integrations for a myriad of components, a basic run time for combining these components into chains and agents, and off-the-shelf implementations of chains and agents.\n",
|
||||
"- **[LangChain Templates](https://python.langchain.com/docs/templates)**: A collection of easily deployable reference architectures for a wide variety of tasks.\n",
|
||||
"- **[LangServe](https://python.langchain.com/docs/langserve)**: A library for deploying LangChain chains as a REST API.\n",
|
||||
"- **[LangSmith](https://python.langchain.com/docs/langsmith)**: A developer platform that lets you debug, test, evaluate, and monitor chains built on any LLM framework and seamlessly integrates with LangChain.\n",
|
||||
"\n",
|
||||
"1. _Data-aware_: connect a language model to other sources of data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"2. _Agentic_: allow a language model to interact with its environment\n",
|
||||
"Together, these products simplify the entire application lifecycle:\n",
|
||||
"\n",
|
||||
"- **Develop**: Write your applications in LangChain/LangChain.js. Hit the ground running using Templates for reference.\n",
|
||||
"- **Productionize**: Use LangSmith to inspect, test and monitor your chains, so that you can constantly improve and deploy with confidence.\n",
|
||||
"- **Deploy**: Turn any chain into an API with LangServe.\n",
|
||||
"\n",
|
||||
"The LangChain framework is designed around these principles.\n",
|
||||
"## LangChain Libraries [](\\#langchain-libraries \"Direct link to LangChain Libraries\")\n",
|
||||
"\n",
|
||||
"This is the Python specific portion of the documentation. For a purely conceptual guide to LangChain, see [here](https://docs.langchain.com/docs/). For the JavaScript documentation, see [here](https://js.langchain.com/docs/).\n",
|
||||
"The main value props of the LangChain packages are:\n",
|
||||
"\n",
|
||||
"## Getting Started [\\#](\\#getting-started \"Permalink to this headline\")\n",
|
||||
"1. **Components**: composable tools and integrations for working with language models. Components are modular and easy-to-use, whether you are using the rest of the LangChain framework or not\n",
|
||||
"2. **Off-the-shelf chains**: built-in assemblages of components for accomplishing higher-level tasks\n",
|
||||
"\n",
|
||||
"How to get started using LangChain to create an Language Model application.\n",
|
||||
"Off-the-shelf chains make it easy to get started. Components make it easy to customize existing chains and build new ones.\n",
|
||||
"\n",
|
||||
"- [Quickstart Guide](https://python.langchain.com/en/latest/getting_started/getting_started.html)\n",
|
||||
"The LangChain libraries themselves are made up of several different packages.\n",
|
||||
"\n",
|
||||
"- **`langchain-core`**: Base abstractions and LangChain Expression Language.\n",
|
||||
"- **`langchain-community`**: Third party integrations.\n",
|
||||
"- **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture.\n",
|
||||
"\n",
|
||||
"Concepts and terminology.\n",
|
||||
"## Get started [](\\#get-started \"Direct link to Get started\")\n",
|
||||
"\n",
|
||||
"- [Concepts and terminology](https://python.langchain.com/en/latest/getting_started/concepts.html)\n",
|
||||
"[Here’s](https://python.langchain.com/docs/get_started/installation) how to install LangChain, set up your environment, and start building.\n",
|
||||
"\n",
|
||||
"We recommend following our [Quickstart](https://python.langchain.com/docs/get_started/quickstart) guide to familiarize yourself with the framework by building your first LangChain application.\n",
|
||||
"\n",
|
||||
"Tutorials created by community experts and presented on YouTube.\n",
|
||||
"Read up on our [Security](https://python.langchain.com/docs/security) best practices to make sure you're developing safely with LangChain.\n",
|
||||
"\n",
|
||||
"- [Tutorials](https://python.langchain.com/en/latest/getting_started/tutorials.html)\n",
|
||||
"note\n",
|
||||
"\n",
|
||||
"These docs focus on the Python LangChain library. [Head here](https://js.langchain.com) for docs on the JavaScript LangChain library.\n",
|
||||
"\n",
|
||||
"## Modules [\\#](\\#modules \"Permalink to this headline\")\n",
|
||||
"## LangChain Expression Language (LCEL) [](\\#langchain-expression-language-lcel \"Direct link to LangChain Expression Language (LCEL)\")\n",
|
||||
"\n",
|
||||
"These modules are the core abstractions which we view as the building blocks of any LLM-powered application.\n",
|
||||
"LCEL is a declarative way to compose chains. LCEL was designed from day 1 to support putting prototypes in production, with no code changes, from the simplest “prompt + LLM” chain to the most complex chains.\n",
|
||||
"\n",
|
||||
"For each module LangChain provides standard, extendable interfaces. LanghChain also provides external integrations and even end-to-end implementations for off-the-shelf use.\n",
|
||||
"- **[Overview](https://python.langchain.com/docs/expression_language/)**: LCEL and its benefits\n",
|
||||
"- **[Interface](https://python.langchain.com/docs/expression_language/interface)**: The standard interface for LCEL objects\n",
|
||||
"- **[How-to](https://python.langchain.com/docs/expression_language/how_to)**: Key features of LCEL\n",
|
||||
"- **[Cookbook](https://python.langchain.com/docs/expression_language/cookbook)**: Example code for accomplishing common tasks\n",
|
||||
"\n",
|
||||
"The docs for each module contain quickstart examples, how-to guides, reference docs, and conceptual guides.\n",
|
||||
"## Modules [](\\#modules \"Direct link to Modules\")\n",
|
||||
"\n",
|
||||
"The modules are (from least to most complex):\n",
|
||||
"LangChain provides standard, extendable interfaces and integrations for the following modules:\n",
|
||||
"\n",
|
||||
"- [Models](https://python.langchain.com/docs/modules/model_io/models/): Supported model types and integrations.\n",
|
||||
"#### [Model I/O](https://python.langchain.com/docs/modules/model_io/) [](\\#model-io \"Direct link to model-io\")\n",
|
||||
"\n",
|
||||
"- [Prompts](https://python.langchain.com/en/latest/modules/prompts.html): Prompt management, optimization, and serialization.\n",
|
||||
"Interface with language models\n",
|
||||
"\n",
|
||||
"- [Memory](https://python.langchain.com/en/latest/modules/memory.html): Memory refers to state that is persisted between calls of a chain/agent.\n",
|
||||
"#### [Retrieval](https://python.langchain.com/docs/modules/data_connection/) [](\\#retrieval \"Direct link to retrieval\")\n",
|
||||
"\n",
|
||||
"- [Indexes](https://python.langchain.com/en/latest/modules/data_connection.html): Language models become much more powerful when combined with application-specific data - this module contains interfaces and integrations for loading, querying and updating external data.\n",
|
||||
"Interface with application-specific data\n",
|
||||
"\n",
|
||||
"- [Chains](https://python.langchain.com/en/latest/modules/chains.html): Chains are structured sequences of calls (to an LLM or to a different utility).\n",
|
||||
"#### [Agents](https://python.langchain.com/docs/modules/agents/) [](\\#agents \"Direct link to agents\")\n",
|
||||
"\n",
|
||||
"- [Agents](https://python.langchain.com/en/latest/modules/agents.html): An agent is a Chain in which an LLM, given a high-level directive and a set of tools, repeatedly decides an action, executes the action and observes the outcome until the high-level directive is complete.\n",
|
||||
"Let models choose which tools to use given high-level directives\n",
|
||||
"\n",
|
||||
"- [Callbacks](https://python.langchain.com/en/latest/modules/callbacks/getting_started.html): Callbacks let you log and stream the intermediate steps of any chain, making it easy to observe, debug, and evaluate the internals of an application.\n",
|
||||
"## Examples, ecosystem, and resources [](\\#examples-ecosystem-and-resources \"Direct link to Examples, ecosystem, and resources\")\n",
|
||||
"\n",
|
||||
"### [Use cases](https://python.langchain.com/docs/use_cases/question_answering/) [](\\#use-cases \"Direct link to use-cases\")\n",
|
||||
"\n",
|
||||
"## Use Cases [\\#](\\#use-cases \"Permalink to this headline\")\n",
|
||||
"Walkthroughs and techniques for common end-to-end use cases, like:\n",
|
||||
"\n",
|
||||
"Best practices and built-in implementations for common LangChain use cases:\n",
|
||||
"- [Document question answering](https://python.langchain.com/docs/use_cases/question_answering/)\n",
|
||||
"- [Chatbots](https://python.langchain.com/docs/use_cases/chatbots/)\n",
|
||||
"- [Analyzing structured data](https://python.langchain.com/docs/use_cases/sql/)\n",
|
||||
"- and much more...\n",
|
||||
"\n",
|
||||
"- [Autonomous Agents](https://python.langchain.com/en/latest/use_cases/autonomous_agents.html): Autonomous agents are long-running agents that take many steps in an attempt to accomplish an objective. Examples include AutoGPT and BabyAGI.\n",
|
||||
"### [Integrations](https://python.langchain.com/docs/integrations/providers/) [](\\#integrations \"Direct link to integrations\")\n",
|
||||
"\n",
|
||||
"- [Agent Simulations](https://python.langchain.com/en/latest/use_cases/agent_simulations.html): Putting agents in a sandbox and observing how they interact with each other and react to events can be an effective way to evaluate their long-range reasoning and planning abilities.\n",
|
||||
"LangChain is part of a rich ecosystem of tools that integrate with our framework and build on top of it. Check out our growing list of [integrations](https://python.langchain.com/docs/integrations/providers/).\n",
|
||||
"\n",
|
||||
"- [Personal Assistants](https://python.langchain.com/en/latest/use_cases/personal_assistants.html): One of the primary LangChain use cases. Personal assistants need to take actions, remember interactions, and have knowledge about your data.\n",
|
||||
"### [Guides](https://python.langchain.com/docs/guides/debugging) [](\\#guides \"Direct link to guides\")\n",
|
||||
"\n",
|
||||
"- [Question Answering](https://python.langchain.com/en/latest/use_cases/question_answering.html): Another common LangChain use case. Answering questions over specific documents, only utilizing the information in those documents to construct an answer.\n",
|
||||
"Best practices for developing with LangChain.\n",
|
||||
"\n",
|
||||
"- [Chatbots](https://python.langchain.com/en/latest/use_cases/chatbots.html): Language models love to chat, making this a very natural use of them.\n",
|
||||
"### [API reference](https://api.python.langchain.com) [](\\#api-reference \"Direct link to api-reference\")\n",
|
||||
"\n",
|
||||
"- [Querying Tabular Data](https://python.langchain.com/en/latest/use_cases/tabular.html): Recommended reading if you want to use language models to query structured data (CSVs, SQL, dataframes, etc).\n",
|
||||
"Head to the reference section for full documentation of all classes and methods in the LangChain and LangChain Experimental Python packages.\n",
|
||||
"\n",
|
||||
"- [Code Understanding](https://python.langchain.com/en/latest/use_cases/code.html): Recommended reading if you want to use language models to analyze code.\n",
|
||||
"### [Developer's guide](https://python.langchain.com/docs/contributing) [](\\#developers-guide \"Direct link to developers-guide\")\n",
|
||||
"\n",
|
||||
"- [Interacting with APIs](https://python.langchain.com/en/latest/use_cases/apis.html): Enabling language models to interact with APIs is extremely powerful. It gives them access to up-to-date information and allows them to take actions.\n",
|
||||
"Check out the developer's guide for guidelines on contributing and help getting your dev environment set up.\n",
|
||||
"\n",
|
||||
"- [Extraction](https://python.langchain.com/en/latest/use_cases/extraction.html): Extract structured information from text.\n",
|
||||
"\n",
|
||||
"- [Summarization](https://python.langchain.com/en/latest/use_cases/summarization.html): Compressing longer documents. A type of Data-Augmented Generation.\n",
|
||||
"\n",
|
||||
"- [Evaluation](https://python.langchain.com/en/latest/use_cases/evaluation.html): Generative models are hard to evaluate with traditional metrics. One promising approach is to use language models themselves to do the evaluation.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Reference Docs [\\#](\\#reference-docs \"Permalink to this headline\")\n",
|
||||
"\n",
|
||||
"Full documentation on all methods, classes, installation methods, and integration setups for LangChain.\n",
|
||||
"\n",
|
||||
"- [Reference Documentation](https://python.langchain.com/en/latest/reference.html)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## LangChain Ecosystem [\\#](\\#langchain-ecosystem \"Permalink to this headline\")\n",
|
||||
"\n",
|
||||
"Guides for how other companies/products can be used with LangChain.\n",
|
||||
"\n",
|
||||
"- [LangChain Ecosystem](https://python.langchain.com/en/latest/ecosystem.html)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Additional Resources [\\#](\\#additional-resources \"Permalink to this headline\")\n",
|
||||
"\n",
|
||||
"Additional resources we think may be useful as you develop your application!\n",
|
||||
"\n",
|
||||
"- [LangChainHub](https://github.com/hwchase17/langchain-hub): The LangChainHub is a place to share and explore other prompts, chains, and agents.\n",
|
||||
"\n",
|
||||
"- [Gallery](https://python.langchain.com/en/latest/additional_resources/gallery.html): A collection of our favorite projects that use LangChain. Useful for finding inspiration or seeing how things were done in other applications.\n",
|
||||
"\n",
|
||||
"- [Deployments](https://python.langchain.com/en/latest/additional_resources/deployments.html): A collection of instructions, code snippets, and template repositories for deploying LangChain apps.\n",
|
||||
"\n",
|
||||
"- [Tracing](https://python.langchain.com/en/latest/additional_resources/tracing.html): A guide on using tracing in LangChain to visualize the execution of chains and agents.\n",
|
||||
"\n",
|
||||
"- [Model Laboratory](https://python.langchain.com/en/latest/additional_resources/model_laboratory.html): Experimenting with different prompts, models, and chains is a big part of developing the best possible application. The ModelLaboratory makes it easy to do so.\n",
|
||||
"\n",
|
||||
"- [Discord](https://discord.gg/6adMQxSpJS): Join us on our Discord to discuss all things LangChain!\n",
|
||||
"\n",
|
||||
"- [YouTube](https://python.langchain.com/en/latest/additional_resources/youtube.html): A collection of the LangChain tutorials and videos.\n",
|
||||
"\n",
|
||||
"- [Production Support](https://forms.gle/57d8AmXBYp8PP8tZA): As you move your LangChains into production, we’d love to offer more comprehensive support. Please fill out this form and we’ll set up a dedicated support Slack channel.\n"
|
||||
"Head to the [Community navigator](https://python.langchain.com/docs/community) to find places to ask questions, share feedback, meet other developers, and dream about the future of LLM’s.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -198,7 +174,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5dde17e7",
|
||||
"id": "7c89b313-adb6-4aa2-9cd8-952a5724a2ce",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
@@ -220,7 +196,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.6"
|
||||
"version": "3.11.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -106,6 +106,45 @@
|
||||
"\n",
|
||||
"conversation.predict(input=\"Hi there!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Guardrails for Amazon Bedrock example \n",
|
||||
"\n",
|
||||
"In this section, we are going to set up a Bedrock language model with specific guardrails that include tracing capabilities. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Any\n",
|
||||
"\n",
|
||||
"from langchain_core.callbacks import AsyncCallbackHandler\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class BedrockAsyncCallbackHandler(AsyncCallbackHandler):\n",
|
||||
" # Async callback handler that can be used to handle callbacks from langchain.\n",
|
||||
"\n",
|
||||
" async def on_llm_error(self, error: BaseException, **kwargs: Any) -> Any:\n",
|
||||
" reason = kwargs.get(\"reason\")\n",
|
||||
" if reason == \"GUARDRAIL_INTERVENED\":\n",
|
||||
" print(f\"Guardrails: {kwargs}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# guardrails for Amazon Bedrock with trace\n",
|
||||
"llm = Bedrock(\n",
|
||||
" credentials_profile_name=\"bedrock-admin\",\n",
|
||||
" model_id=\"<Model_ID>\",\n",
|
||||
" model_kwargs={},\n",
|
||||
" guardrails={\"id\": \"<Guardrail_ID>\", \"version\": \"<Version>\", \"trace\": True},\n",
|
||||
" callbacks=[BedrockAsyncCallbackHandler()],\n",
|
||||
")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
@@ -223,7 +223,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
@@ -406,7 +406,7 @@
|
||||
"- `n_gpu_layers` - determines how many layers of the model are offloaded to your GPU.\n",
|
||||
"- `n_batch` - how many tokens are processed in parallel. \n",
|
||||
"\n",
|
||||
"Setting these parameters correctly will dramatically improve the evaluation speed (see [wrapper code](https://github.com/mmagnesium/langchain/blob/master/langchain/llms/llamacpp.py) for more details)."
|
||||
"Setting these parameters correctly will dramatically improve the evaluation speed (see [wrapper code](https://github.com/langchain-ai/langchain/blob/master/libs/community/langchain_community/llms/llamacpp.py) for more details)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -487,12 +487,12 @@
|
||||
"\n",
|
||||
"Two of the most important GPU parameters are:\n",
|
||||
"\n",
|
||||
"- `n_gpu_layers` - determines how many layers of the model are offloaded to your Metal GPU, in the most case, set it to `1` is enough for Metal\n",
|
||||
"- `n_gpu_layers` - determines how many layers of the model are offloaded to your Metal GPU.\n",
|
||||
"- `n_batch` - how many tokens are processed in parallel, default is 8, set to bigger number.\n",
|
||||
"- `f16_kv` - for some reason, Metal only support `True`, otherwise you will get error such as `Asserting on type 0\n",
|
||||
"GGML_ASSERT: .../ggml-metal.m:706: false && \"not implemented\"`\n",
|
||||
"\n",
|
||||
"Setting these parameters correctly will dramatically improve the evaluation speed (see [wrapper code](https://github.com/mmagnesium/langchain/blob/master/langchain/llms/llamacpp.py) for more details)."
|
||||
"Setting these parameters correctly will dramatically improve the evaluation speed (see [wrapper code](https://github.com/langchain-ai/langchain/blob/master/libs/community/langchain_community/llms/llamacpp.py) for more details)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -501,7 +501,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"n_gpu_layers = 1 # Metal set to 1 is enough.\n",
|
||||
"n_gpu_layers = 1 # Change this value based on your model and your GPU VRAM pool.\n",
|
||||
"n_batch = 512 # Should be between 1 and n_ctx, consider the amount of RAM of your Apple Silicon Chip.\n",
|
||||
"# Make sure the model path is correct for your system!\n",
|
||||
"llm = LlamaCpp(\n",
|
||||
@@ -680,7 +680,7 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.10.12 ('langchain_venv': venv)",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -694,7 +694,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
"version": "3.11.6"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
||||
191
docs/docs/integrations/llms/oci_generative_ai.ipynb
Normal file
191
docs/docs/integrations/llms/oci_generative_ai.ipynb
Normal file
@@ -0,0 +1,191 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Oracle Cloud Infrastructure Generative AI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Oracle Cloud Infrastructure (OCI) Generative AI is a fully managed service that provides a set of state-of-the-art, customizable large language models (LLMs) that cover a wide range of use cases, and which is available through a single API.\n",
|
||||
"Using the OCI Generative AI service you can access ready-to-use pretrained models, or create and host your own fine-tuned custom models based on your own data on dedicated AI clusters. Detailed documentation of the service and API is available __[here](https://docs.oracle.com/en-us/iaas/Content/generative-ai/home.htm)__ and __[here](https://docs.oracle.com/en-us/iaas/api/#/en/generative-ai/20231130/)__.\n",
|
||||
"\n",
|
||||
"This notebook explains how to use OCI's Genrative AI models with LangChain."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Prerequisite\n",
|
||||
"We will need to install the oci sdk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -U oci"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### OCI Generative AI API endpoint \n",
|
||||
"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Authentication\n",
|
||||
"The authentication methods supported for this langchain integration are:\n",
|
||||
"\n",
|
||||
"1. API Key\n",
|
||||
"2. Session token\n",
|
||||
"3. Instance principal\n",
|
||||
"4. Resource principal \n",
|
||||
"\n",
|
||||
"These follows the standard SDK authentication methods detailed __[here](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdk_authentication_methods.htm)__.\n",
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.llms import OCIGenAI\n",
|
||||
"\n",
|
||||
"# use default authN method API-key\n",
|
||||
"llm = OCIGenAI(\n",
|
||||
" model_id=\"MY_MODEL\",\n",
|
||||
" service_endpoint=\"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com\",\n",
|
||||
" compartment_id=\"MY_OCID\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"response = llm.invoke(\"Tell me one fact about earth\", temperature=0.7)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains import LLMChain\n",
|
||||
"from langchain_core.prompts import PromptTemplate\n",
|
||||
"\n",
|
||||
"# Use Session Token to authN\n",
|
||||
"llm = OCIGenAI(\n",
|
||||
" model_id=\"MY_MODEL\",\n",
|
||||
" service_endpoint=\"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com\",\n",
|
||||
" compartment_id=\"MY_OCID\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"prompt = PromptTemplate(input_variables=[\"query\"], template=\"{query}\")\n",
|
||||
"\n",
|
||||
"llm_chain = LLMChain(llm=llm, prompt=prompt)\n",
|
||||
"\n",
|
||||
"response = llm_chain.invoke(\"what is the capital of france?\")\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.schema.output_parser import StrOutputParser\n",
|
||||
"from langchain.schema.runnable import RunnablePassthrough\n",
|
||||
"from langchain_community.embeddings import OCIGenAIEmbeddings\n",
|
||||
"from langchain_community.vectorstores import FAISS\n",
|
||||
"\n",
|
||||
"embeddings = OCIGenAIEmbeddings(\n",
|
||||
" model_id=\"MY_EMBEDDING_MODEL\",\n",
|
||||
" service_endpoint=\"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com\",\n",
|
||||
" compartment_id=\"MY_OCID\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"vectorstore = FAISS.from_texts(\n",
|
||||
" [\n",
|
||||
" \"Larry Ellison co-founded Oracle Corporation in 1977 with Bob Miner and Ed Oates.\",\n",
|
||||
" \"Oracle Corporation is an American multinational computer technology company headquartered in Austin, Texas, United States.\",\n",
|
||||
" ],\n",
|
||||
" embedding=embeddings,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"retriever = vectorstore.as_retriever()\n",
|
||||
"\n",
|
||||
"template = \"\"\"Answer the question based only on the following context:\n",
|
||||
"{context}\n",
|
||||
" \n",
|
||||
"Question: {question}\n",
|
||||
"\"\"\"\n",
|
||||
"prompt = PromptTemplate.from_template(template)\n",
|
||||
"\n",
|
||||
"llm = OCIGenAI(\n",
|
||||
" model_id=\"MY_MODEL\",\n",
|
||||
" service_endpoint=\"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com\",\n",
|
||||
" compartment_id=\"MY_OCID\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chain = (\n",
|
||||
" {\"context\": retriever, \"question\": RunnablePassthrough()}\n",
|
||||
" | prompt\n",
|
||||
" | llm\n",
|
||||
" | StrOutputParser()\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(chain.invoke(\"when was oracle founded?\"))\n",
|
||||
"print(chain.invoke(\"where is oracle headquartered?\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "oci_langchain",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.18"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
"\n",
|
||||
"This example showcases how to connect to [PromptLayer](https://www.promptlayer.com) to start recording your OpenAI requests.\n",
|
||||
"\n",
|
||||
"Another example is [here](https://python.langchain.com/en/latest/ecosystem/promptlayer.html)."
|
||||
"Another example is [here](https://python.langchain.com/docs/integrations/providers/promptlayer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -225,7 +225,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.6"
|
||||
"version": "3.11.6"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
||||
@@ -58,31 +58,24 @@ See a [usage example](/docs/integrations/llms/huggingface_textgen_inference).
|
||||
from langchain_community.llms import HuggingFaceTextGenInference
|
||||
```
|
||||
|
||||
## Chat models
|
||||
|
||||
### Models from Hugging Face
|
||||
|
||||
## Document Loaders
|
||||
We can use the `Hugging Face` LLM classes or directly use the `ChatHuggingFace` class.
|
||||
|
||||
### Hugging Face dataset
|
||||
|
||||
>[Hugging Face Hub](https://huggingface.co/docs/hub/index) is home to over 75,000
|
||||
> [datasets](https://huggingface.co/docs/hub/index#datasets) in more than 100 languages
|
||||
> that can be used for a broad range of tasks across NLP, Computer Vision, and Audio.
|
||||
> They used for a diverse range of tasks such as translation, automatic speech
|
||||
> recognition, and image classification.
|
||||
|
||||
We need to install `datasets` python package.
|
||||
We need to install several python packages.
|
||||
|
||||
```bash
|
||||
pip install datasets
|
||||
pip install huggingface_hub
|
||||
pip install transformers
|
||||
```
|
||||
|
||||
See a [usage example](/docs/integrations/document_loaders/hugging_face_dataset).
|
||||
See a [usage example](/docs/integrations/chat/huggingface).
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders.hugging_face_dataset import HuggingFaceDatasetLoader
|
||||
from langchain_community.chat_models.huggingface import ChatHuggingFace
|
||||
```
|
||||
|
||||
|
||||
## Embedding Models
|
||||
|
||||
### Hugging Face Hub
|
||||
@@ -126,6 +119,48 @@ See a [usage example](/docs/integrations/text_embedding/bge_huggingface).
|
||||
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
|
||||
```
|
||||
|
||||
### Hugging Face Text Embeddings Inference (TEI)
|
||||
|
||||
>[Hugging Face Text Embeddings Inference (TEI)](https://huggingface.co/docs/text-generation-inference/index) is a toolkit for deploying and serving open-source
|
||||
> text embeddings and sequence classification models. `TEI` enables high-performance extraction for the most popular models,
|
||||
>including `FlagEmbedding`, `Ember`, `GTE` and `E5`.
|
||||
|
||||
We need to install `huggingface-hub` python package.
|
||||
|
||||
```bash
|
||||
pip install huggingface-hub
|
||||
```
|
||||
|
||||
See a [usage example](/docs/integrations/text_embedding/text_embeddings_inference).
|
||||
|
||||
```python
|
||||
from langchain_community.embeddings import HuggingFaceHubEmbeddings
|
||||
```
|
||||
|
||||
|
||||
## Document Loaders
|
||||
|
||||
### Hugging Face dataset
|
||||
|
||||
>[Hugging Face Hub](https://huggingface.co/docs/hub/index) is home to over 75,000
|
||||
> [datasets](https://huggingface.co/docs/hub/index#datasets) in more than 100 languages
|
||||
> that can be used for a broad range of tasks across NLP, Computer Vision, and Audio.
|
||||
> They used for a diverse range of tasks such as translation, automatic speech
|
||||
> recognition, and image classification.
|
||||
|
||||
We need to install `datasets` python package.
|
||||
|
||||
```bash
|
||||
pip install datasets
|
||||
```
|
||||
|
||||
See a [usage example](/docs/integrations/document_loaders/hugging_face_dataset).
|
||||
|
||||
```python
|
||||
from langchain_community.document_loaders.hugging_face_dataset import HuggingFaceDatasetLoader
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
|
||||
13
docs/docs/integrations/providers/baichuan.mdx
Normal file
13
docs/docs/integrations/providers/baichuan.mdx
Normal file
@@ -0,0 +1,13 @@
|
||||
# Baichuan
|
||||
|
||||
>[Baichuan Inc.](https://www.baichuan-ai.com/) is a Chinese startup in the era of AGI, dedicated to addressing fundamental human needs: Efficiency, Health, and Happiness.
|
||||
|
||||
## Visit Us
|
||||
Visit us at https://www.baichuan-ai.com/.
|
||||
Register and get an API key if you are trying out our APIs.
|
||||
|
||||
## Baichuan Chat Model
|
||||
An example is available at [example](/docs/integrations/chat/baichuan).
|
||||
|
||||
## Baichuan Text Embedding Model
|
||||
An example is available at [example] (/docs/integrations/text_embedding/baichuan)
|
||||
@@ -1,45 +1,52 @@
|
||||
# DeepInfra
|
||||
|
||||
This page covers how to use the DeepInfra ecosystem within LangChain.
|
||||
>[DeepInfra](https://deepinfra.com/docs) allows us to run the
|
||||
> [latest machine learning models](https://deepinfra.com/models) with ease.
|
||||
> DeepInfra takes care of all the heavy lifting related to running, scaling and monitoring
|
||||
> the models. Users can focus on your application and integrate the models with simple REST API calls.
|
||||
|
||||
>DeepInfra provides [examples](https://deepinfra.com/docs/advanced/langchain) of integration with LangChain.
|
||||
|
||||
This page covers how to use the `DeepInfra` ecosystem within `LangChain`.
|
||||
It is broken into two parts: installation and setup, and then references to specific DeepInfra wrappers.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
- Get your DeepInfra api key from this link [here](https://deepinfra.com/).
|
||||
- Get an DeepInfra api key and set it as an environment variable (`DEEPINFRA_API_TOKEN`)
|
||||
|
||||
## Available Models
|
||||
|
||||
DeepInfra provides a range of Open Source LLMs ready for deployment.
|
||||
You can list supported models for
|
||||
|
||||
You can see supported models for
|
||||
[text-generation](https://deepinfra.com/models?type=text-generation) and
|
||||
[embeddings](https://deepinfra.com/models?type=embeddings).
|
||||
google/flan\* models can be viewed [here](https://deepinfra.com/models?type=text2text-generation).
|
||||
|
||||
You can view a [list of request and response parameters](https://deepinfra.com/meta-llama/Llama-2-70b-chat-hf/api).
|
||||
|
||||
Chat models [follow openai api](https://deepinfra.com/meta-llama/Llama-2-70b-chat-hf/api?example=openai-http)
|
||||
|
||||
## Wrappers
|
||||
|
||||
### LLM
|
||||
## LLM
|
||||
|
||||
There exists an DeepInfra LLM wrapper, which you can access with
|
||||
See a [usage example](/docs/integrations/llms/deepinfra).
|
||||
|
||||
```python
|
||||
from langchain_community.llms import DeepInfra
|
||||
```
|
||||
|
||||
### Embeddings
|
||||
## Embeddings
|
||||
|
||||
There is also an DeepInfra Embeddings wrapper, you can access with
|
||||
See a [usage example](/docs/integrations/text_embedding/deepinfra).
|
||||
|
||||
```python
|
||||
from langchain_community.embeddings import DeepInfraEmbeddings
|
||||
```
|
||||
|
||||
### Chat Models
|
||||
## Chat Models
|
||||
|
||||
There is a chat-oriented wrapper as well, accessible with
|
||||
See a [usage example](/docs/integrations/chat/deepinfra).
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatDeepInfra
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
"id": "134a0785",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Chat Over Documents with Vectara\n",
|
||||
"\n",
|
||||
"This notebook is based on the [chat_vector_db](https://github.com/hwchase17/langchain/blob/master/docs/modules/chains/index_examples/chat_vector_db.html) notebook, but using Vectara as the vector database."
|
||||
"# Chat Over Documents with Vectara"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -186,9 +184,7 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "e8ce4fe9",
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
@@ -547,7 +543,6 @@
|
||||
"execution_count": 26,
|
||||
"id": "e2badd21",
|
||||
"metadata": {
|
||||
"scrolled": false,
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
@@ -755,7 +750,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.9"
|
||||
"version": "3.11.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
75
docs/docs/integrations/text_embedding/baichuan.ipynb
Normal file
75
docs/docs/integrations/text_embedding/baichuan.ipynb
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Baichuan Text Embeddings\n",
|
||||
"\n",
|
||||
"As of today (Jan 25th, 2024) BaichuanTextEmbeddings ranks #1 in C-MTEB (Chinese Multi-Task Embedding Benchmark) leaderboard.\n",
|
||||
"\n",
|
||||
"Leaderboard (Under Overall -> Chinese section): https://huggingface.co/spaces/mteb/leaderboard\n",
|
||||
"\n",
|
||||
"Official Website: https://platform.baichuan-ai.com/docs/text-Embedding\n",
|
||||
"An API-key is required to use this embedding model. You can get one by registering at https://platform.baichuan-ai.com/docs/text-Embedding.\n",
|
||||
"BaichuanTextEmbeddings support 512 token window and preduces vectors with 1024 dimensions. \n",
|
||||
"\n",
|
||||
"Please NOTE that BaichuanTextEmbeddings only supports Chinese text embedding. Multi-language support is coming soon.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "plaintext"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.embeddings import BaichuanTextEmbeddings\n",
|
||||
"\n",
|
||||
"# Place your Baichuan API-key here.\n",
|
||||
"embeddings = BaichuanTextEmbeddings(baichuan_api_key=\"sk-*\")\n",
|
||||
"\n",
|
||||
"text_1 = \"今天天气不错\"\n",
|
||||
"text_2 = \"今天阳光很好\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "plaintext"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_result = embeddings.embed_query(text_1)\n",
|
||||
"query_result"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "plaintext"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"doc_result = embeddings.embed_documents([text_1, text_2])\n",
|
||||
"doc_result"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -10,9 +10,42 @@
|
||||
"Let's load the OpenAI Embedding class."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "40ff98ff-58e9-4716-8788-227a5c3f473d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"First we install langchain-openai and set the required env vars"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": null,
|
||||
"id": "c66c4613-6c67-40ca-b3b1-c026750d1742",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "62e3710e-55a0-44fb-ba51-2f1d520dfc38",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "0be1af71",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -22,17 +55,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"execution_count": 5,
|
||||
"id": "2c66e5da",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"embeddings = OpenAIEmbeddings()"
|
||||
"embeddings = OpenAIEmbeddings(model=\"text-embedding-3-large\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"execution_count": 6,
|
||||
"id": "01370375",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -40,33 +73,50 @@
|
||||
"text = \"This is a test document.\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f012c222-3fa9-470a-935c-758b2048d9af",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Usage\n",
|
||||
"### Embed query"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"execution_count": 7,
|
||||
"id": "bfb6142c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Warning: model not found. Using cl100k_base encoding.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"query_result = embeddings.embed_query(text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"execution_count": 8,
|
||||
"id": "91bc875d-829b-4c3d-8e6f-fc2dda30a3bd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[-0.003186025367556387,\n",
|
||||
" 0.011071979803637493,\n",
|
||||
" -0.004020420763285827,\n",
|
||||
" -0.011658221276953042,\n",
|
||||
" -0.0010534035786864363]"
|
||||
"[-0.014380056377383358,\n",
|
||||
" -0.027191711627651764,\n",
|
||||
" -0.020042716111860304,\n",
|
||||
" 0.057301379620345545,\n",
|
||||
" -0.022267658631828974]"
|
||||
]
|
||||
},
|
||||
"execution_count": 32,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -75,33 +125,49 @@
|
||||
"query_result[:5]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6b733391-1e23-438b-a6bc-0d77eed9426e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Embed documents"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"execution_count": 9,
|
||||
"id": "0356c3b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Warning: model not found. Using cl100k_base encoding.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"doc_result = embeddings.embed_documents([text])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"execution_count": 10,
|
||||
"id": "a4b0d49e-0c73-44b6-aed5-5b426564e085",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[-0.003186025367556387,\n",
|
||||
" 0.011071979803637493,\n",
|
||||
" -0.004020420763285827,\n",
|
||||
" -0.011658221276953042,\n",
|
||||
" -0.0010534035786864363]"
|
||||
"[-0.014380056377383358,\n",
|
||||
" -0.027191711627651764,\n",
|
||||
" -0.020042716111860304,\n",
|
||||
" 0.057301379620345545,\n",
|
||||
" -0.022267658631828974]"
|
||||
]
|
||||
},
|
||||
"execution_count": 34,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -112,131 +178,87 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bb61bbeb",
|
||||
"id": "e7dc464a-6fa2-4cff-ab2e-49a0566d819b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's load the OpenAI Embedding class with first generation models (e.g. text-search-ada-doc-001/text-search-ada-query-001). Note: These are not recommended models - see [here](https://platform.openai.com/docs/guides/embeddings/what-are-embeddings)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "c0b072cc",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_openai import OpenAIEmbeddings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "a56b70f5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"embeddings = OpenAIEmbeddings(model=\"text-embedding-ada-002\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "14aefb64",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"text = \"This is a test document.\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "3c39ed33",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_result = embeddings.embed_query(text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"id": "2ee7ce9f-d506-4810-8897-e44334412714",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[0.004452846988523035,\n",
|
||||
" 0.034550655976098514,\n",
|
||||
" -0.015029939040690051,\n",
|
||||
" 0.03827273883655212,\n",
|
||||
" 0.005785414075152477]"
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"query_result[:5]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"id": "e3221db6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"doc_result = embeddings.embed_documents([text])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"id": "a0865409-3a6d-468f-939f-abde17c7cac3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[0.004452846988523035,\n",
|
||||
" 0.034550655976098514,\n",
|
||||
" -0.015029939040690051,\n",
|
||||
" 0.03827273883655212,\n",
|
||||
" 0.005785414075152477]"
|
||||
]
|
||||
},
|
||||
"execution_count": 28,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"doc_result[0][:5]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "aaad49f8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"## Specify dimensions\n",
|
||||
"\n",
|
||||
"# if you are behind an explicit proxy, you can use the OPENAI_PROXY environment variable to pass through\n",
|
||||
"os.environ[\"OPENAI_PROXY\"] = \"http://proxy.yourcompany.com:8080\""
|
||||
"With the `text-embedding-3` class of models, you can specify the size of the embeddings you want returned. For example by default `text-embedding-3-large` returned embeddings of dimension 3072:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "f7be1e7b-54c6-4893-b8ad-b872e6705735",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"3072"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(doc_result[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "33287142-0835-4958-962f-385ae4447431",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"But by passing in `dimensions=1024` we can reduce the size of our embeddings to 1024:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "854ee772-2de9-4a83-84e0-908033d98e4e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"embeddings_1024 = OpenAIEmbeddings(model=\"text-embedding-3-large\", dimensions=1024)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "3b464396-8d94-478b-8329-849b56e1ae23",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Warning: model not found. Using cl100k_base encoding.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1024"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(embeddings_1024.embed_documents([text])[0])"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"display_name": "poetry-venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
"name": "poetry-venv"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
@@ -248,7 +270,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
"version": "3.9.1"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
"source": [
|
||||
"# Text Embeddings Inference\n",
|
||||
"\n",
|
||||
"Text Embeddings Inference (TEI) is a toolkit for deploying and serving open source text embeddings and sequence classification models. TEI enables high-performance extraction for the most popular models, including FlagEmbedding, Ember, GTE and E5.\n",
|
||||
">[Hugging Face Text Embeddings Inference (TEI)](https://huggingface.co/docs/text-generation-inference/index) is a toolkit for deploying and serving open-source\n",
|
||||
"> text embeddings and sequence classification models. `TEI` enables high-performance extraction for the most popular models,\n",
|
||||
">including `FlagEmbedding`, `Ember`, `GTE` and `E5`.\n",
|
||||
"\n",
|
||||
"To use it within langchain, first install `huggingface-hub`."
|
||||
]
|
||||
@@ -21,7 +23,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet huggingface-hub -q"
|
||||
"%pip install --upgrade huggingface-hub"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -146,9 +148,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "conda_python3",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "conda_python3"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
@@ -160,7 +162,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.13"
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"id": "4x4kQ0VcodAC"
|
||||
},
|
||||
"source": [
|
||||
"# Metaphor Search"
|
||||
"# Exa Search"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15,13 +15,13 @@
|
||||
"id": "V1x8wEUhodAH"
|
||||
},
|
||||
"source": [
|
||||
"Metaphor is a search engine fully designed for use by LLMs. Search for documents on the internet using **natural language queries**, then retrieve **cleaned HTML content** from desired documents.\n",
|
||||
"Exa (formerly Metaphor Search) is a search engine fully designed for use by LLMs. Search for documents on the internet using **natural language queries**, then retrieve **cleaned HTML content** from desired documents.\n",
|
||||
"\n",
|
||||
"Unlike keyword-based search (Google), Metaphor's neural search capabilities allow it to semantically understand queries and return relevant documents. For example, we could search `\"fascinating article about cats\"` and compare the search results from [Google](https://www.google.com/search?q=fascinating+article+about+cats) and [Metaphor](https://metaphor.systems/search?q=fascinating%20article%20about%20cats&autopromptString=Here%20is%20a%20fascinating%20article%20about%20cats%3A). Google gives us SEO-optimized listicles based on the keyword \"fascinating\". Metaphor just works.\n",
|
||||
"Unlike keyword-based search (Google), Exa's neural search capabilities allow it to semantically understand queries and return relevant documents. For example, we could search `\"fascinating article about cats\"` and compare the search results from [Google](https://www.google.com/search?q=fascinating+article+about+cats) and [Exa](https://search.exa.ai/search?q=fascinating%20article%20about%20cats&autopromptString=Here%20is%20a%20fascinating%20article%20about%20cats%3A). Google gives us SEO-optimized listicles based on the keyword \"fascinating\". Exa just works.\n",
|
||||
"\n",
|
||||
"This notebook goes over how to use Metaphor Search with LangChain.\n",
|
||||
"This notebook goes over how to use Exa Search with LangChain.\n",
|
||||
"\n",
|
||||
"First, get a Metaphor API key and add it as an environment variable. Get 1000 free searches/month by [signing up here](https://platform.metaphor.systems/)."
|
||||
"First, get an Exa API key and add it as an environment variable. Get 1000 free searches/month by [signing up here](https://dashboard.exa.ai/)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -34,7 +34,88 @@
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"METAPHOR_API_KEY\"] = \"...\""
|
||||
"os.environ[\"EXA_API_KEY\"] = \"...\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And install the integration package"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain-exa\n",
|
||||
"\n",
|
||||
"# and some deps for this notebook\n",
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Using ExaSearchRetriever\n",
|
||||
"\n",
|
||||
"ExaSearchRetriever is a retriever that uses Exa Search to retrieve relevant documents."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[Result(title='Find Us:', url='https://travelila.com/best-time-to-visit-japan/', id='UFLQGtanQffaDErhngnzgA', score=0.1865834891796112, published_date='2021-01-05', author=None, text='If you are planning to spend your next vacation in Japan, then hold your excitement a bit. It would help if you planned which places you will visit in Japan and the country’s best things. It’s entirel', highlights=None, highlight_scores=None), Result(title='When Is The Best Time of Year To Visit Japan?', url='https://boutiquejapan.com/when-is-the-best-time-of-year-to-visit-japan/', id='70b0IMuaQpshjpBpnwsfUg', score=0.17796635627746582, published_date='2022-09-26', author='Andres Zuleta', text='The good news for travelers is that there is no single best time of year to travel to Japan — yet this makes it hard to decide when to visit, as each season has its own special highlights.When plannin', highlights=None, highlight_scores=None), Result(title='Here is the Best Time to Visit Japan - Cooking Sun', url='https://www.cooking-sun.com/best-time-to-visit-japan/', id='2mh-xvoqGPT-ZRvX9GezNQ', score=0.17497511208057404, published_date='2018-12-17', author='Cooking Sun', text='Japan is a diverse and beautiful country that’s brimming with culture. For some travelers, visiting Japan is a dream come true, since it grazes bucket lists across the globe. One of the best parts abo', highlights=None, highlight_scores=None), Result(title='When to Visit Japan? Bests Times and 2023 Travel Tips', url='https://www.jrailpass.com/blog/when-visit-japan-times', id='KqCnY8fF-nc76n1wNpIo1Q', score=0.17359933257102966, published_date='2020-02-18', author='JRailPass', text='When is the best time to visit Japan? This is a question without a simple answer. Japan is a year-round destination, with interesting activities, attractions, and festivities throughout the year.Your ', highlights=None, highlight_scores=None), Result(title='Complete Guide To Visiting Japan In February 2023: Weather, What To See & Do | LIVE JAPAN travel guide', url='https://livejapan.com/en/article-a0002948/', id='i3nmekOdM8_VBxPfcJmxng', score=0.17215865850448608, published_date='2019-11-13', author='Lucio Maurizi', text='\\n \\n \\n HOME\\n Complete Guide To Visiting Japan In February 2023: Weather, What To See & Do\\n \\n \\n \\n \\n \\n \\n Date published: 13 November 2019 \\n Last updated: 26 January 2021 \\n \\n \\n So you’re planning your tra', highlights=None, highlight_scores=None)]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Based on the given context, there is no specific best time mentioned to visit Japan. Each season has its own special highlights, and Japan is a year-round destination with interesting activities, attractions, and festivities throughout the year. Therefore, the best time to visit Japan depends on personal preferences and the specific activities or events one wants to experience.')"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.prompts import PromptTemplate\n",
|
||||
"from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
|
||||
"from langchain_exa import ExaSearchRetriever, TextContentsOptions\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"# retrieve 5 documents, with content truncated at 1000 characters\n",
|
||||
"retriever = ExaSearchRetriever(\n",
|
||||
" k=5, text_contents_options=TextContentsOptions(max_length=200)\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"prompt = PromptTemplate.from_template(\n",
|
||||
" \"\"\"Answer the following query based on the following context:\n",
|
||||
"query: {query}\n",
|
||||
"<context>\n",
|
||||
"{context}\n",
|
||||
"</context\"\"\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI()\n",
|
||||
"\n",
|
||||
"chain = (\n",
|
||||
" RunnableParallel({\"context\": retriever, \"query\": RunnablePassthrough()})\n",
|
||||
" | prompt\n",
|
||||
" | llm\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chain.invoke(\"When is the best time to visit japan?\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -43,14 +124,14 @@
|
||||
"id": "ip5_D9MkodAK"
|
||||
},
|
||||
"source": [
|
||||
"## Using the Metaphor SDK as LangChain Agent Tools\n",
|
||||
"## Using the Exa SDK as LangChain Agent Tools\n",
|
||||
"\n",
|
||||
"The [Metaphor SDK](https://docs.metaphor.systems/) creates a client that can use the Metaphor API to perform three functions:\n",
|
||||
"The [Exa SDK](https://docs.exa.ai/) creates a client that can use the Exa API to perform three functions:\n",
|
||||
"- `search`: Given a natural language search query, retrieve a list of search results.\n",
|
||||
"- `find_similar`: Given a URL, retrieve a list of search results corresponding to webpages which are similar to the document at the provided URL.\n",
|
||||
"- `get_content`: Given a list of document ids fetched from `search` or `find_similar`, get cleaned HTML content for each document.\n",
|
||||
"\n",
|
||||
"We can use the `@tool` decorator and docstrings to create LangChain Tool wrappers that tell an LLM agent how to use Metaphor."
|
||||
"We can use the `@tool` decorator and docstrings to create LangChain Tool wrappers that tell an LLM agent how to use Exa."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -61,7 +142,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet metaphor-python"
|
||||
"%pip install --upgrade --quiet langchain-exa"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -72,16 +153,16 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from exa_py import Exa\n",
|
||||
"from langchain.agents import tool\n",
|
||||
"from metaphor_python import Metaphor\n",
|
||||
"\n",
|
||||
"metaphor = Metaphor(api_key=os.environ[\"METAPHOR_API_KEY\"])\n",
|
||||
"exa = Exa(api_key=os.environ[\"EXA_API_KEY\"])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def search(query: str):\n",
|
||||
" \"\"\"Search for a webpage based on the query.\"\"\"\n",
|
||||
" return metaphor.search(f\"{query}\", use_autoprompt=True, num_results=5)\n",
|
||||
" return exa.search(f\"{query}\", use_autoprompt=True, num_results=5)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
@@ -89,7 +170,7 @@
|
||||
" \"\"\"Search for webpages similar to a given URL.\n",
|
||||
" The url passed in should be a URL returned from `search`.\n",
|
||||
" \"\"\"\n",
|
||||
" return metaphor.find_similar(url, num_results=5)\n",
|
||||
" return exa.find_similar(url, num_results=5)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
@@ -97,7 +178,7 @@
|
||||
" \"\"\"Get the contents of a webpage.\n",
|
||||
" The ids passed in should be a list of ids returned from `search`.\n",
|
||||
" \"\"\"\n",
|
||||
" return metaphor.get_contents(ids)\n",
|
||||
" return exa.get_contents(ids)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [search, get_contents, find_similar]"
|
||||
@@ -109,9 +190,9 @@
|
||||
"id": "sVe2ca9OodAO"
|
||||
},
|
||||
"source": [
|
||||
"### Providing Metaphor Tools to an Agent\n",
|
||||
"### Providing Exa Tools to an Agent\n",
|
||||
"\n",
|
||||
"We can provide the Metaphor tools we just created to a LangChain `OpenAIFunctionsAgent`. When asked to `Summarize for me a fascinating article about cats`, the agent uses the `search` tool to perform a Metaphor search with an appropriate search query, uses the `get_contents` tool to perform Metaphor content retrieval, and then returns a summary of the retrieved content."
|
||||
"We can provide the Exa tools we just created to a LangChain `OpenAIFunctionsAgent`. When asked to `Summarize for me a fascinating article about cats`, the agent uses the `search` tool to perform a Exa search with an appropriate search query, uses the `get_contents` tool to perform Exa content retrieval, and then returns a summary of the retrieved content."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -237,9 +318,11 @@
|
||||
"id": "e3FHjxT-RoIH"
|
||||
},
|
||||
"source": [
|
||||
"## Advanced Metaphor Features\n",
|
||||
"## Advanced Exa Features\n",
|
||||
"\n",
|
||||
"Metaphor supports powerful filters by domain and date. We can provide a more powerful `search` tool to the agent that lets it decide to apply filters if they are useful for the objective. See all of Metaphor's search features [here](https://github.com/metaphorsystems/metaphor-python/)."
|
||||
"Exa supports powerful filters by domain and date. We can provide a more powerful `search` tool to the agent that lets it decide to apply filters if they are useful for the objective. See all of Exa's search features [here](https://github.com/metaphorsystems/metaphor-python/).\n",
|
||||
"\n",
|
||||
"[//]: # \"TODO(erick): switch metaphor github link to exa github link when sdk published\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -250,10 +333,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from exa_py import Exa\n",
|
||||
"from langchain.agents import tool\n",
|
||||
"from metaphor_python import Metaphor\n",
|
||||
"\n",
|
||||
"metaphor = Metaphor(api_key=os.environ[\"METAPHOR_API_KEY\"])\n",
|
||||
"exa = Exa(api_key=os.environ[\"Exa_API_KEY\"])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
@@ -262,7 +345,7 @@
|
||||
" Set the optional include_domains (list[str]) parameter to restrict the search to a list of domains.\n",
|
||||
" Set the optional start_published_date (str) parameter to restrict the search to documents published after the date (YYYY-MM-DD).\n",
|
||||
" \"\"\"\n",
|
||||
" return metaphor.search(\n",
|
||||
" return exa.search(\n",
|
||||
" f\"{query}\",\n",
|
||||
" use_autoprompt=True,\n",
|
||||
" num_results=5,\n",
|
||||
@@ -276,7 +359,7 @@
|
||||
" \"\"\"Search for webpages similar to a given URL.\n",
|
||||
" The url passed in should be a URL returned from `search`.\n",
|
||||
" \"\"\"\n",
|
||||
" return metaphor.find_similar(url, num_results=5)\n",
|
||||
" return exa.find_similar(url, num_results=5)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
@@ -284,7 +367,7 @@
|
||||
" \"\"\"Get the contents of a webpage.\n",
|
||||
" The ids passed in should be a list of ids returned from `search`.\n",
|
||||
" \"\"\"\n",
|
||||
" return metaphor.get_contents(ids)\n",
|
||||
" return exa.get_contents(ids)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [search, get_contents, find_similar]"
|
||||
@@ -449,7 +532,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.0"
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
160
docs/docs/integrations/tools/ionic.ipynb
Normal file
160
docs/docs/integrations/tools/ionic.ipynb
Normal file
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Ionic\n",
|
||||
">[Ionic](https://www.ioniccommerce.com/) stands at the forefront of commerce innovation, offering a suite of APIs that serve as the backbone for AI assistants and their developers. With Ionic, you unlock a new realm of possibility where convenience and intelligence converge, enabling users to navigate purchases with unprecedented ease. Experience the synergy of human desire and AI capability, all through Ionic's seamless integration.\n",
|
||||
"\n",
|
||||
"By including an `IonicTool` in the list of tools provided to an Agent, you are effortlessly adding e-commerce capabilities to your Agent. For more documetation on setting up your Agent with Ionic, see the [Ionic documentation](https://docs.ioniccommerce.com/guides/langchain).\n",
|
||||
"\n",
|
||||
"This Jupyter Notebook demonstrates how to use the `Ionic` tool with an Agent.\n",
|
||||
"\n",
|
||||
"First, let's install the `ionic-langchain` package.\n",
|
||||
"**The `ionic-langchain` package is maintained by the Ionic team, not the LangChain maintainers.**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pip install ionic-langchain > /dev/null"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now, let's create an `IonicTool` instance and initialize an Agent with the tool."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2024-01-24T17:33:11.755683Z",
|
||||
"start_time": "2024-01-24T17:33:11.174044Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from ionic_langchain.tool import Ionic, IonicTool\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain.agents import AgentExecutor, Tool, create_react_agent\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"\n",
|
||||
"open_ai_key = os.environ[\"OPENAI_API_KEY\"]\n",
|
||||
"\n",
|
||||
"llm = OpenAI(openai_api_key=open_ai_key, temperature=0.5)\n",
|
||||
"\n",
|
||||
"tools: list[Tool] = [IonicTool().tool()]\n",
|
||||
"\n",
|
||||
"prompt = hub.pull(\"hwchase17/react\") # the example prompt for create_react_agent\n",
|
||||
"\n",
|
||||
"agent = create_react_agent(\n",
|
||||
" llm,\n",
|
||||
" tools,\n",
|
||||
" prompt=prompt,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now, we can use the Agent to shop for products and get product information from Ionic."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2024-01-24T17:34:31.257036Z",
|
||||
"start_time": "2024-01-24T17:33:45.849440Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001B[1m> Entering new AgentExecutor chain...\u001B[0m\n",
|
||||
"\u001B[32;1m\u001B[1;3m Since the user is looking for a specific product, we should use Ionic Commerce Shopping Tool to find and compare products.\n",
|
||||
"Action: Ionic Commerce Shopping Tool\n",
|
||||
"Action Input: 4K Monitor, 5, 100000, 1000000\u001B[0m\u001B[36;1m\u001B[1;3m[{'products': [{'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://goto.walmart.com/c/123456/568844/9383?veh=aff&sourceid=imp_000011112222333344&u=https%3A%2F%2Fwww.walmart.com%2Fip%2F118806626'}], 'merchant_name': 'Walmart', 'merchant_product_id': '118806626', 'name': 'ASUS ProArt Display PA32UCX-PK 32” 4K HDR Mini LED Monitor, 99% DCI-P3 99.5% Adobe RGB, DeltaE<1, 10-bit, IPS, Thunderbolt 3 USB-C HDMI DP, Calman Ready, Dolby Vision, 1200nits, w/ X-rite Calibrator', 'price': '$2299.00', 'status': 'available', 'thumbnail': 'https://i5.walmartimages.com/asr/5ddc6e4a-5197-4f08-b505-83551b541de3.fd51cbae2a4d88fb366f5880b41eef03.png?odnHeight=100&odnWidth=100&odnBg=ffffff', 'brand_name': 'ASUS', 'upc': '192876749388'}, {'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://www.amazon.com/dp/B0BHXNL922?tag=ioniccommer00-20&linkCode=osi&th=1&psc=1'}], 'merchant_name': 'Amazon', 'merchant_product_id': 'B0BHXNL922', 'name': 'LG Ultrafine™ OLED Monitor (27EQ850) – 27 inch 4K UHD (3840 x 2160) OLED Pro Display with Adobe RGB 99%, DCI-P3 99%, 1M:1 Contrast Ratio, Hardware Calibration, Multi-Interface, USB Type-C™ (PD 90W)', 'price': '$1796.99', 'status': 'available', 'thumbnail': 'https://m.media-amazon.com/images/I/41VEl4V2U4L._SL160_.jpg', 'brand_name': 'LG', 'upc': None}, {'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://www.amazon.com/dp/B0BZR81SQG?tag=ioniccommer00-20&linkCode=osi&th=1&psc=1'}], 'merchant_name': 'Amazon', 'merchant_product_id': 'B0BZR81SQG', 'name': 'ASUS ROG Swift 38” 4K HDMI 2.1 HDR DSC Gaming Monitor (PG38UQ) - UHD (3840 x 2160), 144Hz, 1ms, Fast IPS, G-SYNC Compatible, Speakers, FreeSync Premium Pro, DisplayPort, DisplayHDR600, 98% DCI-P3', 'price': '$1001.42', 'status': 'available', 'thumbnail': 'https://m.media-amazon.com/images/I/41ULH0sb1zL._SL160_.jpg', 'brand_name': 'ASUS', 'upc': None}, {'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://www.amazon.com/dp/B0BBSV1LK5?tag=ioniccommer00-20&linkCode=osi&th=1&psc=1'}], 'merchant_name': 'Amazon', 'merchant_product_id': 'B0BBSV1LK5', 'name': 'ASUS ROG Swift 41.5\" 4K OLED 138Hz 0.1ms Gaming Monitor PG42UQ', 'price': '$1367.09', 'status': 'available', 'thumbnail': 'https://m.media-amazon.com/images/I/51ZM41brvHL._SL160_.jpg', 'brand_name': 'ASUS', 'upc': None}, {'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://www.amazon.com/dp/B07K8877Y5?tag=ioniccommer00-20&linkCode=osi&th=1&psc=1'}], 'merchant_name': 'Amazon', 'merchant_product_id': 'B07K8877Y5', 'name': 'LG 32UL950-W 32\" Class Ultrafine 4K UHD LED Monitor with Thunderbolt 3 Connectivity Silver (31.5\" Display)', 'price': '$1149.33', 'status': 'available', 'thumbnail': 'https://m.media-amazon.com/images/I/41Q2OE2NnDL._SL160_.jpg', 'brand_name': 'LG', 'upc': None}], 'query': {'query': '4K Monitor', 'max_price': 1000000, 'min_price': 100000, 'num_results': 5}}]\u001B[0m\u001B[32;1m\u001B[1;3m Since the results are in cents, we should convert them back to dollars before displaying the results to the user.\n",
|
||||
"Action: Convert prices to dollars\n",
|
||||
"Action Input: [{'products': [{'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://goto.walmart.com/c/123456/568844/9383?veh=aff&sourceid=imp_000011112222333344&u=https%3A%2F%2Fwww.walmart.com%2Fip%2F118806626'}], 'merchant_name': 'Walmart', 'merchant_product_id': '118806626', 'name': 'ASUS ProArt Display PA32UCX-PK 32” 4K HDR Mini LED Monitor, 99% DCI-P3 99.5% Adobe RGB, DeltaE<1, 10-bit, IPS, Thunderbolt 3 USB-C HDMI DP, Calman Ready, Dolby Vision, 1200nits, w/ X-rite Calibrator', 'price': '$2299.00', 'status': 'available', 'thumbnail': 'https://i5.walmartimages.com/asr/5ddc6e4a-5197\u001B[0mConvert prices to dollars is not a valid tool, try one of [Ionic Commerce Shopping Tool].\u001B[32;1m\u001B[1;3m The results are in a list format, we should display them to the user in a more readable format.\n",
|
||||
"Action: Display results in readable format\n",
|
||||
"Action Input: [{'products': [{'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://goto.walmart.com/c/123456/568844/9383?veh=aff&sourceid=imp_000011112222333344&u=https%3A%2F%2Fwww.walmart.com%2Fip%2F118806626'}], 'merchant_name': 'Walmart', 'merchant_product_id': '118806626', 'name': 'ASUS ProArt Display PA32UCX-PK 32” 4K HDR Mini LED Monitor, 99% DCI-P3 99.5% Adobe RGB, DeltaE<1, 10-bit, IPS, Thunderbolt 3 USB-C HDMI DP, Calman Ready, Dolby Vision, 1200nits, w/ X-rite Calibrator', 'price': '$2299.00', 'status': 'available', 'thumbnail': 'https://i5.walmartimages.com/asr/5ddc6e4a-5197\u001B[0mDisplay results in readable format is not a valid tool, try one of [Ionic Commerce Shopping Tool].\u001B[32;1m\u001B[1;3m We should check if the user is satisfied with the results or if they have additional requirements.\n",
|
||||
"Action: Check user satisfaction\n",
|
||||
"Action Input: None\u001B[0mCheck user satisfaction is not a valid tool, try one of [Ionic Commerce Shopping Tool].\u001B[32;1m\u001B[1;3m I now know the final answer\n",
|
||||
"Final Answer: The final answer is [{'products': [{'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://goto.walmart.com/c/123456/568844/9383?veh=aff&sourceid=imp_000011112222333344&u=https%3A%2F%2Fwww.walmart.com%2Fip%2F118806626'}], 'merchant_name': 'Walmart', 'merchant_product_id': '118806626', 'name': 'ASUS ProArt Display PA32UCX-PK 32” 4K HDR Mini LED Monitor, 99% DCI-P3 99.5% Adobe RGB, DeltaE<1, 10-bit, IPS, Thunderbolt 3 USB-C HDMI DP, Calman Ready, Dolby Vision, 1200nits, w/ X-rite Calibrator', 'price': '$2299.00', 'status': 'available', 'thumbnail': 'https://i5.walmartimages.com/asr/5ddc6e4a-5197-4f08-b505-83551b541de3.fd51cbae2\u001B[0m\n",
|
||||
"\n",
|
||||
"\u001B[1m> Finished chain.\u001B[0m\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": "{'input': \"I'm looking for a new 4K Monitor with 1000R under $1000\",\n 'output': \"The final answer is [{'products': [{'links': [{'text': 'Details', 'type': 'pdp', 'url': 'https://goto.walmart.com/c/123456/568844/9383?veh=aff&sourceid=imp_000011112222333344&u=https%3A%2F%2Fwww.walmart.com%2Fip%2F118806626'}], 'merchant_name': 'Walmart', 'merchant_product_id': '118806626', 'name': 'ASUS ProArt Display PA32UCX-PK 32” 4K HDR Mini LED Monitor, 99% DCI-P3 99.5% Adobe RGB, DeltaE<1, 10-bit, IPS, Thunderbolt 3 USB-C HDMI DP, Calman Ready, Dolby Vision, 1200nits, w/ X-rite Calibrator', 'price': '$2299.00', 'status': 'available', 'thumbnail': 'https://i5.walmartimages.com/asr/5ddc6e4a-5197-4f08-b505-83551b541de3.fd51cbae2\"}"
|
||||
},
|
||||
"execution_count": 32,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"input = \"I'm looking for a new 4K Monitor under $1000\"\n",
|
||||
"\n",
|
||||
"agent_executor.invoke({\"input\": input})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "f85209c3c4c190dca7367d6a1e623da50a9a4392fd53313a7cf9d4bda9c4b85b"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
703
docs/docs/integrations/vectorstores/hanavector.ipynb
Normal file
703
docs/docs/integrations/vectorstores/hanavector.ipynb
Normal file
@@ -0,0 +1,703 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# SAP HANA Cloud Vector Engine\n",
|
||||
"\n",
|
||||
">SAP HANA Cloud Vector Engine is a vector store fully integrated into the SAP HANA Cloud database."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Installation of the HANA database driver."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Pip install necessary package\n",
|
||||
"%pip install --upgrade --quiet hdbcli"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To use `OpenAIEmbeddings` so we use the OpenAI API Key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:16.802456Z",
|
||||
"start_time": "2023-09-09T08:02:07.065604Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"# Use OPENAI_API_KEY env variable\n",
|
||||
"# os.environ[\"OPENAI_API_KEY\"] = \"Your OpenAI API key\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load the sample document \"state_of_the_union.txt\" and create chunks from it."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:25.452472Z",
|
||||
"start_time": "2023-09-09T08:02:25.441563Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.docstore.document import Document\n",
|
||||
"from langchain.text_splitter import CharacterTextSplitter\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores.hanavector import HanaDB\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"text_documents = TextLoader(\"../../modules/state_of_the_union.txt\").load()\n",
|
||||
"text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=0)\n",
|
||||
"text_chunks = text_splitter.split_documents(text_documents)\n",
|
||||
"print(f\"Number of document chunks: {len(text_chunks)}\")\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Create a database connection to a HANA Cloud instance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:28.174088Z",
|
||||
"start_time": "2023-09-09T08:02:28.162698Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"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": [
|
||||
"Create a LangChain VectorStore interface for the HANA database and specify the table (collection) to use for accessing the vector embeddings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:04:16.696625Z",
|
||||
"start_time": "2023-09-09T08:02:31.817790Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"db = HanaDB(\n",
|
||||
" embedding=embeddings, connection=connection, table_name=\"STATE_OF_THE_UNION\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Add the loaded document chunks to the table. For this example, we delete any previous content from the table which might exist from previous runs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete already existing documents from the table\n",
|
||||
"db.delete(filter={})\n",
|
||||
"\n",
|
||||
"# add the loaded document chunks\n",
|
||||
"db.add_documents(text_chunks)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Perform a query to get the two best matching document chunks from the ones that we added in the previous step.\n",
|
||||
"By default \"Cosine Similarity\" is used for the search."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"docs = db.similarity_search(query, k=2)\n",
|
||||
"\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Query the same content with \"Euclidian Distance\". The results shoud be the same as with \"Cosine Similarity\"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores.utils import DistanceStrategy\n",
|
||||
"\n",
|
||||
"db = HanaDB(\n",
|
||||
" embedding=embeddings,\n",
|
||||
" connection=connection,\n",
|
||||
" distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE,\n",
|
||||
" table_name=\"STATE_OF_THE_UNION\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"docs = db.similarity_search(query, k=2)\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"source": [
|
||||
"Maximal Marginal Relevance Search (MMR)\n",
|
||||
"\n",
|
||||
"Maximal marginal relevance optimizes for similarity to query AND diversity among selected documents. First 20 (fetch_k) items will be retrieved from the DB. The MMR algorithm will then find the best 2 (k) matches."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:05:23.276819Z",
|
||||
"start_time": "2023-09-09T08:05:21.972256Z"
|
||||
},
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs = db.max_marginal_relevance_search(query, k=2, fetch_k=20)\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Basic Vectorstore Operations"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"db = HanaDB(\n",
|
||||
" connection=connection, embedding=embeddings, table_name=\"LANGCHAIN_DEMO_BASIC\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Delete already existing documents from the table\n",
|
||||
"db.delete(filter={})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can add simple text documents to the existing table."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs = [Document(page_content=\"Some text\"), Document(page_content=\"Other docs\")]\n",
|
||||
"db.add_documents(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Add documents with metadata."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"foo\",\n",
|
||||
" metadata={\"start\": 100, \"end\": 150, \"doc_name\": \"foo.txt\", \"quality\": \"bad\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"bar\",\n",
|
||||
" metadata={\"start\": 200, \"end\": 250, \"doc_name\": \"bar.txt\", \"quality\": \"good\"},\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"db.add_documents(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Query documents with specific metadata."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs = db.similarity_search(\"foobar\", k=2, filter={\"quality\": \"bad\"})\n",
|
||||
"# With filtering on \"quality\"==\"bad\", only one document should be returned\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content)\n",
|
||||
" print(doc.metadata)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Delete documents with specific metadata."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"db.delete(filter={\"quality\": \"bad\"})\n",
|
||||
"\n",
|
||||
"# Now the similarity search with the same filter will return no results\n",
|
||||
"docs = db.similarity_search(\"foobar\", k=2, filter={\"quality\": \"bad\"})\n",
|
||||
"print(len(docs))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Using a VectorStore as a retriever in chains for retrieval augmented generation (RAG)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 36,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.memory import ConversationBufferMemory\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"# Access the vector DB with a new table\n",
|
||||
"db = HanaDB(\n",
|
||||
" connection=connection,\n",
|
||||
" embedding=embeddings,\n",
|
||||
" table_name=\"LANGCHAIN_DEMO_RETRIEVAL_CHAIN\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Delete already existing entries from the table\n",
|
||||
"db.delete(filter={})\n",
|
||||
"\n",
|
||||
"# add the loaded document chunks from the \"State Of The Union\" file\n",
|
||||
"db.add_documents(text_chunks)\n",
|
||||
"\n",
|
||||
"# Create a retriever instance of the vector store\n",
|
||||
"retriever = db.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Define the prompt."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 37,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"\n",
|
||||
"prompt_template = \"\"\"\n",
|
||||
"You are an expert in state of the union topics. You are provided multiple context items that are related to the prompt you have to answer.\n",
|
||||
"Use the following pieces of context to answer the question at the end.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"{context}\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Question: {question}\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"PROMPT = PromptTemplate(\n",
|
||||
" template=prompt_template, input_variables=[\"context\", \"question\"]\n",
|
||||
")\n",
|
||||
"chain_type_kwargs = {\"prompt\": PROMPT}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Create the ConversationalRetrievalChain, which handles the chat history and the retrieval of similar document chunks to be added to the prompt."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains import ConversationalRetrievalChain\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\")\n",
|
||||
"memory = ConversationBufferMemory(\n",
|
||||
" memory_key=\"chat_history\", output_key=\"answer\", return_messages=True\n",
|
||||
")\n",
|
||||
"qa_chain = ConversationalRetrievalChain.from_llm(\n",
|
||||
" llm,\n",
|
||||
" db.as_retriever(search_kwargs={\"k\": 5}),\n",
|
||||
" return_source_documents=True,\n",
|
||||
" memory=memory,\n",
|
||||
" verbose=False,\n",
|
||||
" combine_docs_chain_kwargs={\"prompt\": PROMPT},\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ask the first question (and verify how many text chunks have been used)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"question = \"What about Mexico and Guatemala?\"\n",
|
||||
"\n",
|
||||
"result = qa_chain.invoke({\"question\": question})\n",
|
||||
"print(\"Answer from LLM:\")\n",
|
||||
"print(\"================\")\n",
|
||||
"print(result[\"answer\"])\n",
|
||||
"\n",
|
||||
"source_docs = result[\"source_documents\"]\n",
|
||||
"print(\"================\")\n",
|
||||
"print(f\"Number of used source document chunks: {len(source_docs)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Examine the used chunks of the chain in detail. Check if the best ranked chunk contains info about \"Mexico and Guatemala\" as mentioned in the question."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for doc in source_docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content)\n",
|
||||
" print(doc.metadata)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ask another question on the same conversational chain. The answer should relate to the previous answer given."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"question = \"What about other countries?\"\n",
|
||||
"\n",
|
||||
"result = qa_chain.invoke({\"question\": question})\n",
|
||||
"print(\"Answer from LLM:\")\n",
|
||||
"print(\"================\")\n",
|
||||
"print(result[\"answer\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Standard tables vs. \"custom\" tables with vector data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"As default behaviour, the table for the embeddings is created with 3 columns\n",
|
||||
"* A column \"VEC_TEXT\", which contains the text of the Document\n",
|
||||
"* A column \"VEC_METADATA\", which contains the metadata of the Document\n",
|
||||
"* A column \"VEC_VECTOR\", which contains the embeddings-vector of the document's text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Access the vector DB with a new table\n",
|
||||
"db = HanaDB(\n",
|
||||
" connection=connection, embedding=embeddings, table_name=\"LANGCHAIN_DEMO_NEW_TABLE\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Delete already existing entries from the table\n",
|
||||
"db.delete(filter={})\n",
|
||||
"\n",
|
||||
"# Add a simple document with some metadata\n",
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"A simple document\",\n",
|
||||
" metadata={\"start\": 100, \"end\": 150, \"doc_name\": \"simple.txt\"},\n",
|
||||
" )\n",
|
||||
"]\n",
|
||||
"db.add_documents(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Show the columns in table \"LANGCHAIN_DEMO_NEW_TABLE\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"cur = connection.cursor()\n",
|
||||
"cur.execute(\n",
|
||||
" \"SELECT COLUMN_NAME, DATA_TYPE_NAME FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME = CURRENT_SCHEMA AND TABLE_NAME = 'LANGCHAIN_DEMO_NEW_TABLE'\"\n",
|
||||
")\n",
|
||||
"rows = cur.fetchall()\n",
|
||||
"for row in rows:\n",
|
||||
" print(row)\n",
|
||||
"cur.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Show the value of the inserted document in the three columns "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"cur = connection.cursor()\n",
|
||||
"cur.execute(\n",
|
||||
" \"SELECT VEC_TEXT, VEC_META, TO_NVARCHAR(VEC_VECTOR) FROM LANGCHAIN_DEMO_NEW_TABLE LIMIT 1\"\n",
|
||||
")\n",
|
||||
"rows = cur.fetchall()\n",
|
||||
"print(rows[0][0]) # The text\n",
|
||||
"print(rows[0][1]) # The metadata\n",
|
||||
"print(rows[0][2]) # The vector\n",
|
||||
"cur.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Custom tables must have at least three columns that match the semantics of a standard table\n",
|
||||
"* A column with type \"NCLOB\" or \"NVARCHAR\" for the text/context of the embeddings\n",
|
||||
"* A column with type \"NCLOB\" or \"NVARCHAR\" for the metadata \n",
|
||||
"* A column with type REAL_VECTOR for the embedding vector\n",
|
||||
"\n",
|
||||
"The table can contain additional columns. When new Documents are inserted to the table, these addtional columns must allow NULL values."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a new table \"MY_OWN_TABLE\" with three \"standard\" columns and one additional column\n",
|
||||
"my_own_table_name = \"MY_OWN_TABLE\"\n",
|
||||
"cur = connection.cursor()\n",
|
||||
"cur.execute(\n",
|
||||
" (\n",
|
||||
" f\"CREATE TABLE {my_own_table_name} (\"\n",
|
||||
" \"SOME_OTHER_COLUMN NVARCHAR(42), \"\n",
|
||||
" \"MY_TEXT NVARCHAR(2048), \"\n",
|
||||
" \"MY_METADATA NVARCHAR(1024), \"\n",
|
||||
" \"MY_VECTOR REAL_VECTOR )\"\n",
|
||||
" )\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create a HanaDB instance with the own table\n",
|
||||
"db = HanaDB(\n",
|
||||
" connection=connection,\n",
|
||||
" embedding=embeddings,\n",
|
||||
" table_name=my_own_table_name,\n",
|
||||
" content_column=\"MY_TEXT\",\n",
|
||||
" metadata_column=\"MY_METADATA\",\n",
|
||||
" vector_column=\"MY_VECTOR\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Add a simple document with some metadata\n",
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Some other text\",\n",
|
||||
" metadata={\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\"},\n",
|
||||
" )\n",
|
||||
"]\n",
|
||||
"db.add_documents(docs)\n",
|
||||
"\n",
|
||||
"# Check if data has been inserted into our own table\n",
|
||||
"cur.execute(f\"SELECT * FROM {my_own_table_name} LIMIT 1\")\n",
|
||||
"rows = cur.fetchall()\n",
|
||||
"print(rows[0][0]) # Value of column \"SOME_OTHER_DATA\". Should be NULL/None\n",
|
||||
"print(rows[0][1]) # The text\n",
|
||||
"print(rows[0][2]) # The metadata\n",
|
||||
"print(rows[0][3]) # The vector\n",
|
||||
"\n",
|
||||
"cur.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Add another document and perform a similarity search on the custom table"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Some more text\",\n",
|
||||
" metadata={\"start\": 800, \"end\": 950, \"doc_name\": \"more.txt\"},\n",
|
||||
" )\n",
|
||||
"]\n",
|
||||
"db.add_documents(docs)\n",
|
||||
"\n",
|
||||
"query = \"What's up?\"\n",
|
||||
"docs = db.similarity_search(query, k=2)\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(doc.page_content)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -167,9 +167,9 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"URL = 'https://www.conseil-constitutionnel.fr/node/3850/pdf'\n",
|
||||
"PDF = 'Déclaration_des_droits_de_l_homme_et_du_citoyen.pdf'\n",
|
||||
"open(PDF, 'wb').write(requests.get(URL).content)"
|
||||
"URL = \"https://www.conseil-constitutionnel.fr/node/3850/pdf\"\n",
|
||||
"PDF = \"Déclaration_des_droits_de_l_homme_et_du_citoyen.pdf\"\n",
|
||||
"open(PDF, \"wb\").write(requests.get(URL).content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -208,7 +208,7 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"print('Read a PDF...')\n",
|
||||
"print(\"Read a PDF...\")\n",
|
||||
"loader = PyPDFLoader(PDF)\n",
|
||||
"pages = loader.load_and_split()\n",
|
||||
"len(pages)"
|
||||
@@ -252,12 +252,14 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"print('Create a Vector Database from PDF text...')\n",
|
||||
"embeddings = OpenAIEmbeddings(model='text-embedding-ada-002')\n",
|
||||
"print(\"Create a Vector Database from PDF text...\")\n",
|
||||
"embeddings = OpenAIEmbeddings(model=\"text-embedding-ada-002\")\n",
|
||||
"texts = [p.page_content for p in pages]\n",
|
||||
"metadata = pd.DataFrame(index=list(range(len(texts))))\n",
|
||||
"metadata['tag'] = 'law'\n",
|
||||
"metadata['title'] = 'Déclaration des Droits de l\\'Homme et du Citoyen de 1789'.encode('utf-8')\n",
|
||||
"metadata[\"tag\"] = \"law\"\n",
|
||||
"metadata[\"title\"] = \"Déclaration des Droits de l'Homme et du Citoyen de 1789\".encode(\n",
|
||||
" \"utf-8\"\n",
|
||||
")\n",
|
||||
"vectordb = KDBAI(table, embeddings)\n",
|
||||
"vectordb.add_texts(texts=texts, metadatas=metadata)"
|
||||
]
|
||||
@@ -288,11 +290,13 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"print('Create LangChain Pipeline...')\n",
|
||||
"qabot = RetrievalQA.from_chain_type(chain_type='stuff',\n",
|
||||
" llm=ChatOpenAI(model='gpt-3.5-turbo-16k', temperature=TEMP), \n",
|
||||
" retriever=vectordb.as_retriever(search_kwargs=dict(k=K)),\n",
|
||||
" return_source_documents=True)"
|
||||
"print(\"Create LangChain Pipeline...\")\n",
|
||||
"qabot = RetrievalQA.from_chain_type(\n",
|
||||
" chain_type=\"stuff\",\n",
|
||||
" llm=ChatOpenAI(model=\"gpt-3.5-turbo-16k\", temperature=TEMP),\n",
|
||||
" retriever=vectordb.as_retriever(search_kwargs=dict(k=K)),\n",
|
||||
" return_source_documents=True,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -325,9 +329,9 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"Q = 'Summarize the document in English:'\n",
|
||||
"print(f'\\n\\n{Q}\\n')\n",
|
||||
"print(qabot.invoke(dict(query=Q))['result'])"
|
||||
"Q = \"Summarize the document in English:\"\n",
|
||||
"print(f\"\\n\\n{Q}\\n\")\n",
|
||||
"print(qabot.invoke(dict(query=Q))[\"result\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -362,9 +366,9 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"Q = 'Is it a fair law and why ?'\n",
|
||||
"print(f'\\n\\n{Q}\\n')\n",
|
||||
"print(qabot.invoke(dict(query=Q))['result'])"
|
||||
"Q = \"Is it a fair law and why ?\"\n",
|
||||
"print(f\"\\n\\n{Q}\\n\")\n",
|
||||
"print(qabot.invoke(dict(query=Q))[\"result\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -414,9 +418,9 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"Q = 'What are the rights and duties of the man, the citizen and the society ?'\n",
|
||||
"print(f'\\n\\n{Q}\\n')\n",
|
||||
"print(qabot.invoke(dict(query=Q))['result'])"
|
||||
"Q = \"What are the rights and duties of the man, the citizen and the society ?\"\n",
|
||||
"print(f\"\\n\\n{Q}\\n\")\n",
|
||||
"print(qabot.invoke(dict(query=Q))[\"result\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -441,9 +445,9 @@
|
||||
],
|
||||
"source": [
|
||||
"%%time\n",
|
||||
"Q = 'Is this law practical ?'\n",
|
||||
"print(f'\\n\\n{Q}\\n')\n",
|
||||
"print(qabot.invoke(dict(query=Q))['result'])"
|
||||
"Q = \"Is this law practical ?\"\n",
|
||||
"print(f\"\\n\\n{Q}\\n\")\n",
|
||||
"print(qabot.invoke(dict(query=Q))[\"result\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 1,
|
||||
"id": "a0fbfbba-3c82-4298-a312-9cec016d9d2e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -138,8 +138,7 @@
|
||||
"from langchain.agents import AgentExecutor\n",
|
||||
"from langchain.agents.format_scratchpad import format_to_openai_function_messages\n",
|
||||
"from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser\n",
|
||||
"from langchain.tools import DuckDuckGoSearchResults\n",
|
||||
"from langchain_community.tools.convert_to_openai import format_tool_to_openai_function\n",
|
||||
"from langchain_community.tools import DuckDuckGoSearchResults\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"# Fetches the latest version of this prompt\n",
|
||||
@@ -156,7 +155,7 @@
|
||||
" ), # General internet search using DuckDuckGo\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"llm_with_tools = llm.bind(functions=[format_tool_to_openai_function(t) for t in tools])\n",
|
||||
"llm_with_tools = llm.bind_functions(tools)\n",
|
||||
"\n",
|
||||
"runnable_agent = (\n",
|
||||
" {\n",
|
||||
@@ -334,7 +333,6 @@
|
||||
"from langchain.agents import AgentExecutor, AgentType, initialize_agent, load_tools\n",
|
||||
"from langchain.agents.format_scratchpad import format_to_openai_function_messages\n",
|
||||
"from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser\n",
|
||||
"from langchain_community.tools.convert_to_openai import format_tool_to_openai_function\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -1345,9 +1343,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"display_name": "poetry-venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
"name": "poetry-venv"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
@@ -1359,7 +1357,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
"version": "3.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"\n",
|
||||
"Newer OpenAI models have been fine-tuned to detect when **one or more** function(s) should be called and respond with the inputs that should be passed to the function(s). In an API call, you can describe functions and have the model intelligently choose to output a JSON object containing arguments to call these functions. The goal of the OpenAI tools APIs is to more reliably return valid and useful function calls than what can be done using a generic text completion or chat API.\n",
|
||||
"\n",
|
||||
"OpenAI termed the capability to invoke a **single** function as **functions**, and the capability to invoke **one or more** funcitons as **tools**.\n",
|
||||
"OpenAI termed the capability to invoke a **single** function as **functions**, and the capability to invoke **one or more** functions as **tools**.\n",
|
||||
"\n",
|
||||
":::tip\n",
|
||||
"\n",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"\n",
|
||||
"* Use with regular LLMs, not with chat models.\n",
|
||||
"* Use only with unstructured tools; i.e., tools that accept a single string input.\n",
|
||||
"* See [AgentTypes](/docs/moduels/agents/agent_types/) documentation for more agent types.\n",
|
||||
"* See [AgentTypes](/docs/modules/agents/agent_types/) documentation for more agent types.\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# pip install chromadb"
|
||||
"%pip install -qU chromadb langchain langchain-community langchain-openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -61,7 +61,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 2,
|
||||
"id": "e3002ed7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -96,14 +96,12 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 3,
|
||||
"id": "204ef7ca",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.agents.agent_toolkits.conversational_retrieval.tool import (\n",
|
||||
" create_retriever_tool,\n",
|
||||
")\n",
|
||||
"from langchain.tools.retriever import create_retriever_tool\n",
|
||||
"\n",
|
||||
"retriever_tool = create_retriever_tool(\n",
|
||||
" retriever,\n",
|
||||
@@ -124,15 +122,14 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 4,
|
||||
"id": "2df91723",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from langchain.utils.openai_functions import convert_pydantic_to_openai_function\n",
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Response(BaseModel):\n",
|
||||
@@ -169,7 +166,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 5,
|
||||
"id": "dfb73fe3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -181,7 +178,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 6,
|
||||
"id": "5b46cdb2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -224,14 +221,13 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 7,
|
||||
"id": "73c785f9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.agents import AgentExecutor\n",
|
||||
"from langchain.agents.format_scratchpad import format_to_openai_function_messages\n",
|
||||
"from langchain_community.tools.convert_to_openai import format_tool_to_openai_function\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
|
||||
"from langchain_openai import ChatOpenAI"
|
||||
]
|
||||
@@ -269,14 +265,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm_with_tools = llm.bind(\n",
|
||||
" functions=[\n",
|
||||
" # The retriever tool\n",
|
||||
" format_tool_to_openai_function(retriever_tool),\n",
|
||||
" # Response schema\n",
|
||||
" convert_pydantic_to_openai_function(Response),\n",
|
||||
" ]\n",
|
||||
")"
|
||||
"llm_with_tools = llm.bind_functions([retriever_tool, Response])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -302,7 +291,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 12,
|
||||
"id": "2cfd783e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -322,7 +311,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 20,
|
||||
"id": "2667c9a4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -333,7 +322,55 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
|
||||
"\u001b[32;1m\u001b[1;3m\u001b[0m\u001b[36;1m\u001b[1;3m[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={'page_chunk': 31, 'source': '../../state_of_the_union.txt'}), Document(page_content='One was stationed at bases and breathing in toxic smoke from “burn pits” that incinerated wastes of war—medical and hazard material, jet fuel, and more. \\n\\nWhen they came home, many of the world’s fittest and best trained warriors were never the same. \\n\\nHeadaches. Numbness. Dizziness. \\n\\nA cancer that would put them in a flag-draped coffin. \\n\\nI know. \\n\\nOne of those soldiers was my son Major Beau Biden. \\n\\nWe don’t know for sure if a burn pit was the cause of his brain cancer, or the diseases of so many of our troops. \\n\\nBut I’m committed to finding out everything we can. \\n\\nCommitted to military families like Danielle Robinson from Ohio. \\n\\nThe widow of Sergeant First Class Heath Robinson. \\n\\nHe was born a soldier. Army National Guard. Combat medic in Kosovo and Iraq. \\n\\nStationed near Baghdad, just yards from burn pits the size of football fields. \\n\\nHeath’s widow Danielle is here with us tonight. They loved going to Ohio State football games. He loved building Legos with their daughter.', metadata={'page_chunk': 37, 'source': '../../state_of_the_union.txt'}), Document(page_content='A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. \\n\\nAnd if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. \\n\\nWe can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. \\n\\nWe’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. \\n\\nWe’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. \\n\\nWe’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.', metadata={'page_chunk': 32, 'source': '../../state_of_the_union.txt'}), Document(page_content='But cancer from prolonged exposure to burn pits ravaged Heath’s lungs and body. \\n\\nDanielle says Heath was a fighter to the very end. \\n\\nHe didn’t know how to stop fighting, and neither did she. \\n\\nThrough her pain she found purpose to demand we do better. \\n\\nTonight, Danielle—we are. \\n\\nThe VA is pioneering new ways of linking toxic exposures to diseases, already helping more veterans get benefits. \\n\\nAnd tonight, I’m announcing we’re expanding eligibility to veterans suffering from nine respiratory cancers. \\n\\nI’m also calling on Congress: pass a law to make sure veterans devastated by toxic exposures in Iraq and Afghanistan finally get the benefits and comprehensive health care they deserve. \\n\\nAnd fourth, let’s end cancer as we know it. \\n\\nThis is personal to me and Jill, to Kamala, and to so many of you. \\n\\nCancer is the #2 cause of death in America–second only to heart disease.', metadata={'page_chunk': 38, 'source': '../../state_of_the_union.txt'})]\u001b[0m\u001b[32;1m\u001b[1;3m{'name': 'Response', 'arguments': '{\\n \"answer\": \"President mentioned Ketanji Brown Jackson as a nominee for the United States Supreme Court and praised her as one of the nation\\'s top legal minds.\",\\n \"sources\": [31]\\n}'}\u001b[0m\n",
|
||||
"\u001b[32;1m\u001b[1;3m\u001b[0m\u001b[36;1m\u001b[1;3mTonight. 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",
|
||||
"And for our LGBTQ+ Americans, let’s finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong. \n",
|
||||
"\n",
|
||||
"As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential. \n",
|
||||
"\n",
|
||||
"While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice. \n",
|
||||
"\n",
|
||||
"And soon, we’ll strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things. \n",
|
||||
"\n",
|
||||
"So tonight I’m offering a Unity Agenda for the Nation. Four big things we can do together. \n",
|
||||
"\n",
|
||||
"First, beat the opioid epidemic.\n",
|
||||
"\n",
|
||||
"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",
|
||||
"\n",
|
||||
"Last year COVID-19 kept us apart. This year we are finally together again. \n",
|
||||
"\n",
|
||||
"Tonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n",
|
||||
"\n",
|
||||
"With a duty to one another to the American people to the Constitution. \n",
|
||||
"\n",
|
||||
"And with an unwavering resolve that freedom will always triumph over tyranny. \n",
|
||||
"\n",
|
||||
"Six 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",
|
||||
"\n",
|
||||
"He thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n",
|
||||
"\n",
|
||||
"He met the Ukrainian people. \n",
|
||||
"\n",
|
||||
"From President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.\n",
|
||||
"\n",
|
||||
"A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. \n",
|
||||
"\n",
|
||||
"And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. \n",
|
||||
"\n",
|
||||
"We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. \n",
|
||||
"\n",
|
||||
"We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. \n",
|
||||
"\n",
|
||||
"We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. \n",
|
||||
"\n",
|
||||
"We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.\u001b[0m\u001b[32;1m\u001b[1;3m{'arguments': '{\\n\"answer\": \"President Biden nominated Ketanji Brown Jackson for the United States Supreme Court and described her as one of our nation\\'s top legal minds who will continue Justice Breyer\\'s legacy of excellence.\",\\n\"sources\": [6]\\n}', 'name': 'Response'}\u001b[0m\n",
|
||||
"\n",
|
||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
||||
]
|
||||
@@ -341,18 +378,18 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'answer': \"President mentioned Ketanji Brown Jackson as a nominee for the United States Supreme Court and praised her as one of the nation's top legal minds.\",\n",
|
||||
" 'sources': [31]}"
|
||||
"{'answer': \"President Biden nominated Ketanji Brown Jackson for the United States Supreme Court and described her as one of our nation's top legal minds who will continue Justice Breyer's legacy of excellence.\",\n",
|
||||
" 'sources': [6]}"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"agent_executor.invoke(\n",
|
||||
" {\"input\": \"what did the president say about kentaji brown jackson\"},\n",
|
||||
" {\"input\": \"what did the president say about ketanji brown jackson\"},\n",
|
||||
" return_only_outputs=True,\n",
|
||||
")"
|
||||
]
|
||||
@@ -368,9 +405,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"display_name": "poetry-venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
"name": "poetry-venv"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
@@ -382,7 +419,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.1"
|
||||
"version": "3.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -152,9 +152,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.tools.convert_to_openai import format_tool_to_openai_tool\n",
|
||||
"\n",
|
||||
"llm_with_tools = llm.bind(tools=[format_tool_to_openai_tool(tool) for tool in tools])"
|
||||
"llm_with_tools = llm.bind_tools(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -229,9 +227,9 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'actions': [OpenAIToolAgentAction(tool='get_word_length', tool_input={'word': 'eudca'}, log=\"\\nInvoking: `get_word_length` with `{'word': 'eudca'}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_U9SR78eT398r9UbzID2N9LXh', 'function': {'arguments': '{\\n \"word\": \"eudca\"\\n}', 'name': 'get_word_length'}, 'type': 'function'}]})], tool_call_id='call_U9SR78eT398r9UbzID2N9LXh')],\n",
|
||||
" 'messages': [AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_U9SR78eT398r9UbzID2N9LXh', 'function': {'arguments': '{\\n \"word\": \"eudca\"\\n}', 'name': 'get_word_length'}, 'type': 'function'}]})]},\n",
|
||||
" {'steps': [AgentStep(action=OpenAIToolAgentAction(tool='get_word_length', tool_input={'word': 'eudca'}, log=\"\\nInvoking: `get_word_length` with `{'word': 'eudca'}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_U9SR78eT398r9UbzID2N9LXh', 'function': {'arguments': '{\\n \"word\": \"eudca\"\\n}', 'name': 'get_word_length'}, 'type': 'function'}]})], tool_call_id='call_U9SR78eT398r9UbzID2N9LXh'), observation=5)],\n",
|
||||
"[{'actions': [OpenAIToolAgentAction(tool='get_word_length', tool_input={'word': 'eudca'}, log=\"\\nInvoking: `get_word_length` with `{'word': 'eudca'}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_A07D5TuyqcNIL0DIEVRPpZkg', 'function': {'arguments': '{\\n \"word\": \"eudca\"\\n}', 'name': 'get_word_length'}, 'type': 'function'}]})], tool_call_id='call_A07D5TuyqcNIL0DIEVRPpZkg')],\n",
|
||||
" 'messages': [AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_A07D5TuyqcNIL0DIEVRPpZkg', 'function': {'arguments': '{\\n \"word\": \"eudca\"\\n}', 'name': 'get_word_length'}, 'type': 'function'}]})]},\n",
|
||||
" {'steps': [AgentStep(action=OpenAIToolAgentAction(tool='get_word_length', tool_input={'word': 'eudca'}, log=\"\\nInvoking: `get_word_length` with `{'word': 'eudca'}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_A07D5TuyqcNIL0DIEVRPpZkg', 'function': {'arguments': '{\\n \"word\": \"eudca\"\\n}', 'name': 'get_word_length'}, 'type': 'function'}]})], tool_call_id='call_A07D5TuyqcNIL0DIEVRPpZkg'), observation=5)],\n",
|
||||
" 'messages': [FunctionMessage(content='5', name='get_word_length')]},\n",
|
||||
" {'output': 'There are 5 letters in the word \"eudca\".',\n",
|
||||
" 'messages': [AIMessage(content='There are 5 letters in the word \"eudca\".')]}]"
|
||||
@@ -449,7 +447,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
"version": "3.9.1"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
||||
@@ -12,71 +12,101 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "bb220019-4012-4da4-bfee-01fb8189aa49",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-community langchain-openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "d65d8a60",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.schema import HumanMessage\n",
|
||||
"from langchain_community.tools import MoveFileTool\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_core.utils.function_calling import convert_to_openai_function\n",
|
||||
"from langchain_openai import ChatOpenAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 20,
|
||||
"id": "abd8dc72",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo-0613\")"
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "dce2cdb7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools import MoveFileTool, format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 21,
|
||||
"id": "3b3dc766",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [MoveFileTool()]\n",
|
||||
"functions = [format_tool_to_openai_function(t) for t in tools]"
|
||||
"functions = [convert_to_openai_function(t) for t in tools]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 12,
|
||||
"id": "d38c4a22-2e9e-4d15-a9e1-bf8103c6303b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'name': 'move_file',\n",
|
||||
" 'description': 'Move or rename a file from one location to another',\n",
|
||||
" 'parameters': {'type': 'object',\n",
|
||||
" 'properties': {'source_path': {'description': 'Path of the file to move',\n",
|
||||
" 'type': 'string'},\n",
|
||||
" 'destination_path': {'description': 'New path for the moved file',\n",
|
||||
" 'type': 'string'}},\n",
|
||||
" 'required': ['source_path', 'destination_path']}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"functions[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "230a7939",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"message = model.predict_messages(\n",
|
||||
"message = model.invoke(\n",
|
||||
" [HumanMessage(content=\"move file foo to bar\")], functions=functions\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 16,
|
||||
"id": "c118c940",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'function_call': {'name': 'move_file', 'arguments': '{\\n \"source_path\": \"foo\",\\n \"destination_path\": \"bar\"\\n}'}}, example=False)"
|
||||
"AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"source_path\": \"foo\",\\n \"destination_path\": \"bar\"\\n}', 'name': 'move_file'}})"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -108,12 +138,64 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "751da79f",
|
||||
"cell_type": "markdown",
|
||||
"id": "77dd0d9f-2f24-4535-a658-a061f91e009a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": [
|
||||
"With OpenAI chat models we can also automatically bind and convert function-like objects with `bind_functions`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "24bb1518-8100-4ac3-acea-04acfac963d1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"source_path\": \"foo\",\\n \"destination_path\": \"bar\"\\n}', 'name': 'move_file'}})"
|
||||
]
|
||||
},
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model_with_functions = model.bind_functions(tools)\n",
|
||||
"model_with_functions.invoke([HumanMessage(content=\"move file foo to bar\")])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "000ec6ff-ca67-4206-ba56-cc2a91b85ce6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Or we can use the update OpenAI API that uses `tools` and `tool_choice` instead of `functions` and `function_call` by using `ChatOpenAI.bind_tools`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "1a333e4e-df55-4e15-9d2e-4fd142d969f3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_btkY3xV71cEVAOHnNa5qwo44', 'function': {'arguments': '{\\n \"source_path\": \"foo\",\\n \"destination_path\": \"bar\"\\n}', 'name': 'move_file'}, 'type': 'function'}]})"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model_with_tools = model.bind_tools(tools)\n",
|
||||
"model_with_tools.invoke([HumanMessage(content=\"move file foo to bar\")])"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Logging to file\n",
|
||||
"This example shows how to print logs to file. It shows how to use the `FileCallbackHandler`, which does the same thing as [`StdOutCallbackHandler`](https://python.langchain.com/en/latest/modules/callbacks/getting_started.html#using-an-existing-handler), but instead writes the output to file. It also uses the `loguru` library to log other outputs that are not captured by the handler."
|
||||
"This example shows how to print logs to file. It shows how to use the `FileCallbackHandler`, which does the same thing as [`StdOutCallbackHandler`](https://python.langchain.com/docs/modules/callbacks/#get-started), but instead writes the output to file. It also uses the `loguru` library to log other outputs that are not captured by the handler."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -166,7 +166,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.16"
|
||||
"version": "3.11.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
" * document addition by id (`add_documents` method with `ids` argument)\n",
|
||||
" * delete by id (`delete` method with `ids` argument)\n",
|
||||
"\n",
|
||||
"Compatible Vectorstores: `AnalyticDB`, `AstraDB`, `AwaDB`, `Bagel`, `Cassandra`, `Chroma`, `DashVector`, `DatabricksVectorSearch`, `DeepLake`, `Dingo`, `ElasticVectorSearch`, `ElasticsearchStore`, `FAISS`, `MyScale`, `PGVector`, `Pinecone`, `Qdrant`, `Redis`, `ScaNN`, `SupabaseVectorStore`, `SurrealDBStore`, `TimescaleVector`, `Vald`, `Vearch`, `VespaStore`, `Weaviate`, `ZepVectorStore`.\n",
|
||||
"Compatible Vectorstores: `AnalyticDB`, `AstraDB`, `AwaDB`, `Bagel`, `Cassandra`, `Chroma`, `DashVector`, `DatabricksVectorSearch`, `DeepLake`, `Dingo`, `ElasticVectorSearch`, `ElasticsearchStore`, `FAISS`, `HanaDB`, `MyScale`, `PGVector`, `Pinecone`, `Qdrant`, `Redis`, `ScaNN`, `SupabaseVectorStore`, `SurrealDBStore`, `TimescaleVector`, `Vald`, `Vearch`, `VespaStore`, `Weaviate`, `ZepVectorStore`.\n",
|
||||
" \n",
|
||||
"## Caution\n",
|
||||
"\n",
|
||||
|
||||
492
docs/docs/modules/model_io/chat/function_calling.ipynb
Normal file
492
docs/docs/modules/model_io/chat/function_calling.ipynb
Normal file
@@ -0,0 +1,492 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "dae8d4ed-9150-45da-b494-7717ab0a2960",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Function calling\n",
|
||||
"\n",
|
||||
"Certain chat models, like [OpenAI's](https://platform.openai.com/docs/guides/function-calling), have a function-calling API that lets you describe functions and their arguments, and have the model return a JSON object with a function to invoke and the inputs to that function. Function-calling is extremely useful for building [tool-using chains and agents](/docs/use_cases/tool_use/), and for getting structured outputs from models more generally.\n",
|
||||
"\n",
|
||||
"LangChain comes with a number of utilities to make function-calling easy. Namely, it comes with\n",
|
||||
"\n",
|
||||
"* simple syntax for binding functions to models\n",
|
||||
"* converters for formatting various types of objects to the expected function schemas\n",
|
||||
"* output parsers for extracting the function invocations from API responses\n",
|
||||
"\n",
|
||||
"We'll focus here on the first two bullets. To see how output parsing works as well check out the [OpenAI Tools output parsers](/docs/modules/model_io/output_parsers/types/openai_tools)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a177c64b-7c99-495c-b362-5ed3b40aa26a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Defining functions\n",
|
||||
"\n",
|
||||
"We'll focus on the [OpenAI function format](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools) here since as of this writing that is the main model provider that supports function calling. LangChain has a built-in converter that can turn Python functions, Pydantic classes, and LangChain Tools into the OpenAI function format:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "f6d1dc0c-6170-4977-809f-365099f628ea",
|
||||
"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;49m23.2.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.3.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-core langchain-openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6bd290bd-7621-466b-a73e-fc8480f879ec",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Python function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "41ebab5c-0e9f-4b49-86ee-9290ced2fe96",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"multiply\",\n",
|
||||
" \"description\": \"Multiply two integers together.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"a\": {\n",
|
||||
" \"type\": \"integer\",\n",
|
||||
" \"description\": \"First integer\"\n",
|
||||
" },\n",
|
||||
" \"b\": {\n",
|
||||
" \"type\": \"integer\",\n",
|
||||
" \"description\": \"Second integer\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\n",
|
||||
" \"a\",\n",
|
||||
" \"b\"\n",
|
||||
" ]\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"from langchain_core.utils.function_calling import convert_to_openai_tool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def multiply(a: int, b: int) -> int:\n",
|
||||
" \"\"\"Multiply two integers together.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" a: First integer\n",
|
||||
" b: Second integer\n",
|
||||
" \"\"\"\n",
|
||||
" return a * b\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(json.dumps(convert_to_openai_tool(multiply), indent=2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ecf22577-38ab-48f1-ba0b-371aaba1bacc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Pydantic class"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "ecc8ffd4-aed3-4f47-892d-1896cc1ca4dc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"multiply\",\n",
|
||||
" \"description\": \"Multiply two integers together.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"a\": {\n",
|
||||
" \"description\": \"First integer\",\n",
|
||||
" \"type\": \"integer\"\n",
|
||||
" },\n",
|
||||
" \"b\": {\n",
|
||||
" \"description\": \"Second integer\",\n",
|
||||
" \"type\": \"integer\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\n",
|
||||
" \"a\",\n",
|
||||
" \"b\"\n",
|
||||
" ]\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class multiply(BaseModel):\n",
|
||||
" \"\"\"Multiply two integers together.\"\"\"\n",
|
||||
"\n",
|
||||
" a: int = Field(..., description=\"First integer\")\n",
|
||||
" b: int = Field(..., description=\"Second integer\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(json.dumps(convert_to_openai_tool(multiply), indent=2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b83d5a88-50ed-4ae4-85cf-8b895617496f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### LangChain Tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "696c7dd6-660c-4797-909f-bf878b3acf93",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"multiply\",\n",
|
||||
" \"description\": \"Multiply two integers together.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"a\": {\n",
|
||||
" \"description\": \"First integer\",\n",
|
||||
" \"type\": \"integer\"\n",
|
||||
" },\n",
|
||||
" \"b\": {\n",
|
||||
" \"description\": \"Second integer\",\n",
|
||||
" \"type\": \"integer\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"required\": [\n",
|
||||
" \"a\",\n",
|
||||
" \"b\"\n",
|
||||
" ]\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from typing import Any, Type\n",
|
||||
"\n",
|
||||
"from langchain_core.tools import BaseTool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class MultiplySchema(BaseModel):\n",
|
||||
" \"\"\"Multiply tool schema.\"\"\"\n",
|
||||
"\n",
|
||||
" a: int = Field(..., description=\"First integer\")\n",
|
||||
" b: int = Field(..., description=\"Second integer\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Multiply(BaseTool):\n",
|
||||
" args_schema: Type[BaseModel] = MultiplySchema\n",
|
||||
" name: str = \"multiply\"\n",
|
||||
" description: str = \"Multiply two integers together.\"\n",
|
||||
"\n",
|
||||
" def _run(self, a: int, b: int, **kwargs: Any) -> Any:\n",
|
||||
" return a * b\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Note: we're passing in a Multiply object not the class itself.\n",
|
||||
"print(json.dumps(convert_to_openai_tool(Multiply()), indent=2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "04bda177-202f-4811-bb74-f3fa7094a15b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Binding functions\n",
|
||||
"\n",
|
||||
"Now that we've defined a function, we'll want to pass it in to our model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "a5aa93a7-6859-43e8-be85-619d975b908c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_JvOu9oUwMrQHiDekZTbpNCHY', 'function': {'arguments': '{\\n \"a\": 5,\\n \"b\": 3\\n}', 'name': 'multiply'}, 'type': 'function'}]})"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo\")\n",
|
||||
"llm.invoke(\"what's 5 times three\", tools=[convert_to_openai_tool(multiply)])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "dd0e7365-32d0-46a3-b8f2-caf27d5d9262",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And if we wanted this function to be passed in every time we call the tool, we could bind it to the tool:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "87165d64-31a7-4332-965e-18fa939fda50",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_cwRoTnD1ux1SnWXLrTj2KlWH', 'function': {'arguments': '{\\n \"a\": 5,\\n \"b\": 3\\n}', 'name': 'multiply'}, 'type': 'function'}]})"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_tool = llm.bind(tools=[convert_to_openai_tool(multiply)])\n",
|
||||
"llm_with_tool.invoke(\"what's 5 times three\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "21b4d000-3828-4e32-9226-55119f47ee67",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can also enforce that a tool is called using the [tool_choice](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools) parameter."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "2daa354c-cc85-4a60-a9b2-b681ec22ca33",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_sWjLyioSZAtYMQRLMTzncz1v', 'function': {'arguments': '{\\n \"a\": 5,\\n \"b\": 4\\n}', 'name': 'multiply'}, 'type': 'function'}]})"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_tool = llm.bind(\n",
|
||||
" tools=[convert_to_openai_tool(multiply)],\n",
|
||||
" tool_choice={\"type\": \"function\", \"function\": {\"name\": \"multiply\"}},\n",
|
||||
")\n",
|
||||
"llm_with_tool.invoke(\n",
|
||||
" \"don't answer my question. no do answer my question. no don't. what's five times four\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ce013d11-49ea-4de9-8bbc-bc9ae203002c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The [ChatOpenAI](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI) class even comes with a `bind_tools` helper function that handles converting function-like objects to the OpenAI format and binding them for you:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "842c9914-ac28-428f-9fcc-556177e8e715",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LCdBa4cbhMJPRdtkhDzpRh7x', 'function': {'arguments': '{\\n \"a\": 5,\\n \"b\": 3\\n}', 'name': 'multiply'}, 'type': 'function'}]})"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_tool = llm.bind_tools([multiply], tool_choice=\"multiply\")\n",
|
||||
"llm_with_tool.invoke(\"what's 5 times three\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7d6e22d8-9f33-4845-9364-0d276df35ff5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Legacy args `functions` and `function_call`\n",
|
||||
"\n",
|
||||
"Until Fall of 2023 the OpenAI API expected arguments `functions` and `funtion_call` instead of `tools` and `tool_choice`, and they had a slightly different format than `tools` and `tool_choice`. LangChain maintains utilities for using the old API if you need to use that as well:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "a317f71e-177e-404b-b09c-8fb365a4d8a2",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'name': 'multiply',\n",
|
||||
" 'description': 'Multiply two integers together.',\n",
|
||||
" 'parameters': {'type': 'object',\n",
|
||||
" 'properties': {'a': {'description': 'First integer', 'type': 'integer'},\n",
|
||||
" 'b': {'description': 'Second integer', 'type': 'integer'}},\n",
|
||||
" 'required': ['a', 'b']}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.utils.function_calling import convert_to_openai_function\n",
|
||||
"\n",
|
||||
"convert_to_openai_function(multiply)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "dd124259-75e2-4704-9f57-824d3e463bfa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"a\": 3,\\n \"b\": 1000000\\n}', 'name': 'multiply'}})"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_functions = llm.bind(\n",
|
||||
" functions=[convert_to_openai_function(multiply)], function_call={\"name\": \"multiply\"}\n",
|
||||
")\n",
|
||||
"llm_with_functions.invoke(\"what's 3 times a million\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "d9a90af9-1c81-4ace-b155-1589f7308a1c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"a\": 3,\\n \"b\": 1000000\\n}', 'name': 'multiply'}})"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_functions = llm.bind_functions([multiply], function_call=\"multiply\")\n",
|
||||
"llm_with_functions.invoke(\"what's 3 times a million\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7779808d-d75c-4d76-890d-ba8c6c571514",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"* **Output parsing**: See [OpenAI Tools output parsers](/docs/modules/model_io/output_parsers/types/openai_tools) and [OpenAI Functions output parsers](/docs/modules/model_io/output_parsers/types/openai_functions) to learn about extracting the function calling API responses into various formats.\n",
|
||||
"* **Tool use**: See how to construct chains and agents that actually call the invoked tools in [these guides](/docs/use_cases/tool_use/)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -24,5 +24,6 @@ We have several how-to guides for more advanced usage of LLMs.
|
||||
This includes:
|
||||
|
||||
- [How to cache ChatModel responses](./chat_model_caching)
|
||||
- [How to use ChatModels that support function calling](./function_calling)
|
||||
- [How to stream responses from a ChatModel](./streaming)
|
||||
- [How to track token usage in a ChatModel call](./token_usage_tracking)
|
||||
|
||||
@@ -32,7 +32,8 @@ LangChain has lots of different types of output parsers. This is a list of outpu
|
||||
|
||||
| Name | Supports Streaming | Has Format Instructions | Calls LLM | Input Type | Output Type | Description |
|
||||
|-----------------|--------------------|-------------------------------|-----------|----------------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [OpenAIFunctions](./types/openai_functions) | ✅ | (Passes `functions` to model) | | `Message` (with `function_call`) | JSON object | Uses OpenAI function calling to structure the return output. If you are using a model that supports function calling, this is generally the most reliable method. |
|
||||
| [OpenAITools](./types/openai_tools) | | (Passes `tools` to model) | | `Message` (with `tool_choice`) | JSON object | Uses latest OpenAI function calling args `tools` and `tool_choice` to structure the return output. If you are using a model that supports function calling, this is generally the most reliable method. |
|
||||
| [OpenAIFunctions](./types/openai_functions) | ✅ | (Passes `functions` to model) | | `Message` (with `function_call`) | JSON object | Uses legacy OpenAI function calling args `functions` and `function_call` to structure the return output. |
|
||||
| [JSON](./types/json) | ✅ | ✅ | | `str \| Message` | JSON object | Returns a JSON object as specified. You can specify a Pydantic model and it will return JSON for that model. Probably the most reliable output parser for getting structured data that does NOT use function calling. |
|
||||
| [XML](./types/xml) | ✅ | ✅ | | `str \| Message` | `dict` | Returns a dictionary of tags. Use when XML output is needed. Use with models that are good at writing XML (like Anthropic's). |
|
||||
| [CSV](./types/csv) | ✅ | ✅ | | `str \| Message` | `List[str]` | Returns a list of comma separated values. |
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bcbe5c87",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# OpenAI Tools\n",
|
||||
"\n",
|
||||
"These output parsers extract tool calls from OpenAI's function calling API responses. This means they are only usable with models that support function calling, and specifically the latest `tools` and `tool_choice` parameters. We recommend familiarizing yourself with [function calling](/docs/modules/model_io/chat/function_calling) before reading this guide.\n",
|
||||
"\n",
|
||||
"There are a few different variants of output parsers:\n",
|
||||
"\n",
|
||||
"- [JsonOutputToolsParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.openai_tools.JsonOutputToolsParser.html#langchain.output_parsers.openai_tools.JsonOutputToolsParser): Returns the arguments of the function call as JSON\n",
|
||||
"- [JsonOutputKeyToolsParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.openai_tools.JsonOutputKeyToolsParser.html#langchain.output_parsers.openai_tools.JsonOutputKeyToolsParser): Returns the value of specific key in the function call as JSON\n",
|
||||
"- [PydanticToolsParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.openai_tools.PydanticToolsParser.html#langchain.output_parsers.openai_tools.PydanticToolsParser): Returns the arguments of the function call as a Pydantic Model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aac4262b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field, validator\n",
|
||||
"from langchain_openai import ChatOpenAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "52cb351d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Joke(BaseModel):\n",
|
||||
" \"\"\"Joke to tell user.\"\"\"\n",
|
||||
"\n",
|
||||
" setup: str = Field(description=\"question to set up a joke\")\n",
|
||||
" punchline: str = Field(description=\"answer to resolve the joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "2c3259c4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0).bind_tools([Joke])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "75c33a76-ead8-43aa-ba18-c1822c38cfa9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'function',\n",
|
||||
" 'function': {'name': 'Joke',\n",
|
||||
" 'description': 'Joke to tell user.',\n",
|
||||
" 'parameters': {'type': 'object',\n",
|
||||
" 'properties': {'setup': {'description': 'question to set up a joke',\n",
|
||||
" 'type': 'string'},\n",
|
||||
" 'punchline': {'description': 'answer to resolve the joke',\n",
|
||||
" 'type': 'string'}},\n",
|
||||
" 'required': ['setup', 'punchline']}}}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.kwargs[\"tools\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "d3e9007c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [(\"system\", \"You are helpful assistant\"), (\"user\", \"{input}\")]\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "87680951",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## JsonOutputToolsParser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "cb065bdd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.output_parsers.openai_tools import JsonOutputToolsParser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "6ff758c8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parser = JsonOutputToolsParser()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "27a3acd1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"chain = prompt | model | parser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "59b59179",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'Joke',\n",
|
||||
" 'args': {'setup': \"Why don't scientists trust atoms?\",\n",
|
||||
" 'punchline': 'Because they make up everything!'}}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chain.invoke({\"input\": \"tell me a joke\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0f093b2b-ffd1-47b7-9221-b4265ae52701",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To include the tool call id we can specify `return_id=True`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "d43fd620-dcdc-4ad0-a3a9-e7d2d71d6e68",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'Joke',\n",
|
||||
" 'args': {'setup': \"Why don't scientists trust atoms?\",\n",
|
||||
" 'punchline': 'Because they make up everything!'},\n",
|
||||
" 'id': 'call_Isuoh0RTeQzzOKGg5QlQ7UqI'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"parser = JsonOutputToolsParser(return_id=True)\n",
|
||||
"chain = prompt | model | parser\n",
|
||||
"chain.invoke({\"input\": \"tell me a joke\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7ca55ac9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## JsonOutputKeyToolsParser\n",
|
||||
"\n",
|
||||
"This merely extracts a single key from the returned response. This is useful for when you are passing in a single tool and just want it's arguments."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "f8bc404e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers.openai_tools import JsonOutputKeyToolsParser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "c91c5949",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parser = JsonOutputKeyToolsParser(key_name=\"Joke\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "b4583baf",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"chain = prompt | model | parser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "e8b766ff",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'setup': \"Why don't scientists trust atoms?\",\n",
|
||||
" 'punchline': 'Because they make up everything!'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 23,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chain.invoke({\"input\": \"tell me a joke\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fc5695c5-451f-482f-bde6-462d85f1a93e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Certain models can return multiple tool invocations each call, so by default the output is a list. If we just want to return the first tool invocation, we can specify `return_single=True`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "b1f3097a-5040-435e-9e26-bbdf9506aead",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'setup': \"Why don't scientists trust atoms?\",\n",
|
||||
" 'punchline': 'Because they make up everything!'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"parser = JsonOutputKeyToolsParser(key_name=\"Joke\", return_single=True)\n",
|
||||
"chain = prompt | model | parser\n",
|
||||
"chain.invoke({\"input\": \"tell me a joke\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "941a3d4e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## PydanticToolsParser\n",
|
||||
"\n",
|
||||
"This builds on top of `JsonOutputToolsParser` but passes the results to a Pydantic Model. This allows for further validation should you choose."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "f51823fe",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.output_parsers.openai_tools import PydanticToolsParser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"id": "3c6a5e4d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Joke(BaseModel):\n",
|
||||
" \"\"\"Joke to tell user.\"\"\"\n",
|
||||
"\n",
|
||||
" setup: str = Field(description=\"question to set up a joke\")\n",
|
||||
" punchline: str = Field(description=\"answer to resolve the joke\")\n",
|
||||
"\n",
|
||||
" # You can add custom validation logic easily with Pydantic.\n",
|
||||
" @validator(\"setup\")\n",
|
||||
" def question_ends_with_question_mark(cls, field):\n",
|
||||
" if field[-1] != \"?\":\n",
|
||||
" raise ValueError(\"Badly formed question!\")\n",
|
||||
" return field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"parser = PydanticToolsParser(tools=[Joke])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"id": "d2bbd54f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0).bind_tools([Joke])\n",
|
||||
"chain = prompt | model | parser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"id": "db1a06e8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Joke(setup=\"Why don't scientists trust atoms?\", punchline='Because they make up everything!')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 31,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chain.invoke({\"input\": \"tell me a joke\"})"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -430,7 +430,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": null,
|
||||
"id": "64650362",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -452,8 +452,8 @@
|
||||
"from langchain.prompts import (\n",
|
||||
" PromptTemplate,\n",
|
||||
")\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field, validator\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"from pydantic import BaseModel, Field, validator\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Person(BaseModel):\n",
|
||||
@@ -531,8 +531,8 @@
|
||||
"from langchain.prompts import (\n",
|
||||
" PromptTemplate,\n",
|
||||
")\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field, validator\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"from pydantic import BaseModel, Field, validator\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define your desired data structure.\n",
|
||||
|
||||
866
docs/docs/use_cases/question_answering/citations.ipynb
Normal file
866
docs/docs/use_cases/question_answering/citations.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -67,7 +67,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"execution_count": 2,
|
||||
"id": "0221fdfd-2a18-4449-a123-e6b0b15bb3d9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -77,7 +77,7 @@
|
||||
"[{'type': 'count_emails', 'args': {'last_n_days': 5}, 'output': 10}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -86,7 +86,6 @@
|
||||
"from operator import itemgetter\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"from langchain_community.tools.convert_to_openai import format_tool_to_openai_tool\n",
|
||||
"from langchain_core.runnables import Runnable, RunnableLambda, RunnablePassthrough\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
@@ -105,9 +104,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [count_emails, send_email]\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0).bind(\n",
|
||||
" tools=[format_tool_to_openai_tool(t) for t in tools]\n",
|
||||
")\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0).bind_tools(tools)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def call_tool(tool_invocation: dict) -> Runnable:\n",
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 5,
|
||||
"id": "c35359ae-a740-48c5-b5e7-1a377fb25aa2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -137,9 +137,6 @@
|
||||
"from typing import Union\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"from langchain_community.tools.convert_to_openai import (\n",
|
||||
" format_tool_to_openai_tool,\n",
|
||||
")\n",
|
||||
"from langchain_core.runnables import (\n",
|
||||
" Runnable,\n",
|
||||
" RunnableLambda,\n",
|
||||
@@ -150,7 +147,7 @@
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\")\n",
|
||||
"tools = [multiply, exponentiate, add]\n",
|
||||
"model_with_tools = model.bind(tools=[format_tool_to_openai_tool(t) for t in tools])\n",
|
||||
"model_with_tools = model.bind_tools(tools)\n",
|
||||
"tool_map = {tool.name: tool for tool in tools}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 1,
|
||||
"id": "e13ec98c-8521-4d63-b521-caf92da87b70",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -103,7 +103,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 2,
|
||||
"id": "c35359ae-a740-48c5-b5e7-1a377fb25aa2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -112,9 +112,6 @@
|
||||
"from typing import Union\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"from langchain_community.tools.convert_to_openai import (\n",
|
||||
" format_tool_to_openai_tool,\n",
|
||||
")\n",
|
||||
"from langchain_core.runnables import (\n",
|
||||
" Runnable,\n",
|
||||
" RunnableLambda,\n",
|
||||
@@ -125,7 +122,7 @@
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo-1106\")\n",
|
||||
"tools = [multiply, exponentiate, add]\n",
|
||||
"model_with_tools = model.bind(tools=[format_tool_to_openai_tool(t) for t in tools])\n",
|
||||
"model_with_tools = model.bind_tools(tools)\n",
|
||||
"tool_map = {tool.name: tool for tool in tools}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"### Function calling\n",
|
||||
"One of the most reliable ways to use tools with LLMs is with function calling APIs (also sometimes called tool calling or parallel function calling). This only works with models that explicitly support function calling, like OpenAI models.\n",
|
||||
"One of the most reliable ways to use tools with LLMs is with function calling APIs (also sometimes called tool calling or parallel function calling). This only works with models that explicitly support function calling, like OpenAI models. To learn more head to the [function calling guide](/docs/modules/model_io/chat/function_calling).\n",
|
||||
"\n",
|
||||
"First we'll define our model and tools. We'll start with just a single tool, `multiply`."
|
||||
]
|
||||
@@ -168,13 +168,23 @@
|
||||
"id": "c22e6f0f-c5ad-4c0f-9514-e626704ea51c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Next we'll convert our LangChain Tool to an OpenAI format JSONSchema function, and bind this as the `tools` argument to be passed to all ChatOpenAI calls. Since we only have a single Tool and in this initial chain we want to make sure it's always used, we'll also specify `tool_choice`. See the [OpenAI chat API reference](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice) for more on these parameters."
|
||||
"Next we'll convert our LangChain Tool to an OpenAI format JSONSchema function, and bind this as the `tools` argument to be passed to all ChatOpenAI calls. Since we only have a single Tool and in this initial chain we want to make sure it's always used, we'll also specify `tool_choice`. See the [OpenAI chat API reference](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice) for more on these parameters:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "2babd759-bccd-4d50-95ad-365a07347926",
|
||||
"id": "3bfe2cdc-7d72-457c-a9a1-5fa1e0bcde55",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_with_tools = model.bind_tools([multiply], tool_choice=\"multiply\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "19f6285f-d8b1-432c-8c07-f7aee3fc0fa4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -183,39 +193,40 @@
|
||||
"[{'type': 'function',\n",
|
||||
" 'function': {'name': 'multiply',\n",
|
||||
" 'description': 'multiply(first_int: int, second_int: int) -> int - Multiply two integers together.',\n",
|
||||
" 'parameters': {'title': 'multiplySchemaSchema',\n",
|
||||
" 'type': 'object',\n",
|
||||
" 'properties': {'first_int': {'title': 'First Int', 'type': 'integer'},\n",
|
||||
" 'second_int': {'title': 'Second Int', 'type': 'integer'}},\n",
|
||||
" 'parameters': {'type': 'object',\n",
|
||||
" 'properties': {'first_int': {'type': 'integer'},\n",
|
||||
" 'second_int': {'type': 'integer'}},\n",
|
||||
" 'required': ['first_int', 'second_int']}}}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_community.tools.convert_to_openai import (\n",
|
||||
" format_tool_to_openai_tool,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"formatted_tools = [format_tool_to_openai_tool(multiply)]\n",
|
||||
"formatted_tools"
|
||||
"model_with_tools.kwargs[\"tools\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "3bfe2cdc-7d72-457c-a9a1-5fa1e0bcde55",
|
||||
"execution_count": 8,
|
||||
"id": "340c1b04-38cb-4467-83ca-8aa2b59176d8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'type': 'function', 'function': {'name': 'multiply'}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model_with_tools = model.bind(\n",
|
||||
" tools=formatted_tools,\n",
|
||||
" # We specify tool_choice to enforce that the 'multiply' function is called by the model.\n",
|
||||
" tool_choice={\"type\": \"function\", \"function\": {\"name\": \"multiply\"}},\n",
|
||||
")"
|
||||
"model_with_tools.kwargs[\"tool_choice\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"id": "1d20604e-c4d1-4d21-841b-23e4f61aec36",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -92,13 +92,12 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define model and bind tool\n",
|
||||
"from langchain_community.tools.convert_to_openai import format_tool_to_openai_tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"model_with_tools = model.bind(\n",
|
||||
" tools=[format_tool_to_openai_tool(complex_tool)],\n",
|
||||
" tool_choice={\"type\": \"function\", \"function\": {\"name\": \"complex_tool\"}},\n",
|
||||
"model_with_tools = model.bind_tools(\n",
|
||||
" [complex_tool],\n",
|
||||
" tool_choice=\"complex_tool\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
@@ -259,9 +258,8 @@
|
||||
" | JsonOutputKeyToolsParser(key_name=\"complex_tool\", return_single=True)\n",
|
||||
" | complex_tool\n",
|
||||
")\n",
|
||||
"better_model = ChatOpenAI(model=\"gpt-4-1106-preview\", temperature=0).bind(\n",
|
||||
" tools=[format_tool_to_openai_tool(complex_tool)],\n",
|
||||
" tool_choice={\"type\": \"function\", \"function\": {\"name\": \"complex_tool\"}},\n",
|
||||
"better_model = ChatOpenAI(model=\"gpt-4-1106-preview\", temperature=0).bind_tools(\n",
|
||||
" [complex_tool], tool_choice=\"complex_tool\"\n",
|
||||
")\n",
|
||||
"better_chain = (\n",
|
||||
" better_model\n",
|
||||
|
||||
@@ -3715,6 +3715,10 @@
|
||||
{
|
||||
"source": "/docs/integrations/providers/google_document_ai",
|
||||
"destination": "/docs/integrations/platforms/google#google-document-ai"
|
||||
},
|
||||
{
|
||||
"source": "/docs/integrations/tools/metaphor_search",
|
||||
"destination": "/docs/integrations/tools/exa_search"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
langchain-core==0.1.14
|
||||
langchain-core==0.1.16
|
||||
|
||||
@@ -94,15 +94,19 @@ def analyze_text(
|
||||
files serialized to HTML string.
|
||||
"""
|
||||
resp: Dict[str, Any] = {}
|
||||
textstat = import_textstat()
|
||||
spacy = import_spacy()
|
||||
text_complexity_metrics = {
|
||||
key: getattr(textstat, key)(text) for key in get_text_complexity_metrics()
|
||||
}
|
||||
resp.update({"text_complexity_metrics": text_complexity_metrics})
|
||||
resp.update(text_complexity_metrics)
|
||||
try:
|
||||
textstat = import_textstat()
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
text_complexity_metrics = {
|
||||
key: getattr(textstat, key)(text) for key in get_text_complexity_metrics()
|
||||
}
|
||||
resp.update({"text_complexity_metrics": text_complexity_metrics})
|
||||
resp.update(text_complexity_metrics)
|
||||
|
||||
if nlp is not None:
|
||||
spacy = import_spacy()
|
||||
doc = nlp(text)
|
||||
|
||||
dep_out = spacy.displacy.render( # type: ignore
|
||||
@@ -279,9 +283,7 @@ class MlflowCallbackHandler(BaseMetadataCallbackHandler, BaseCallbackHandler):
|
||||
) -> None:
|
||||
"""Initialize callback handler."""
|
||||
import_pandas()
|
||||
import_textstat()
|
||||
import_mlflow()
|
||||
spacy = import_spacy()
|
||||
super().__init__()
|
||||
|
||||
self.name = name
|
||||
@@ -303,14 +305,19 @@ class MlflowCallbackHandler(BaseMetadataCallbackHandler, BaseCallbackHandler):
|
||||
)
|
||||
|
||||
self.action_records: list = []
|
||||
self.nlp = None
|
||||
try:
|
||||
self.nlp = spacy.load("en_core_web_sm")
|
||||
except OSError:
|
||||
logger.warning(
|
||||
"Run `python -m spacy download en_core_web_sm` "
|
||||
"to download en_core_web_sm model for text visualization."
|
||||
)
|
||||
self.nlp = None
|
||||
spacy = import_spacy()
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self.nlp = spacy.load("en_core_web_sm")
|
||||
except OSError:
|
||||
logger.warning(
|
||||
"Run `python -m spacy download en_core_web_sm` "
|
||||
"to download en_core_web_sm model for text visualization."
|
||||
)
|
||||
|
||||
self.metrics = {key: 0 for key in mlflow_callback_metrics()}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ from langchain_community.chat_models.javelin_ai_gateway import ChatJavelinAIGate
|
||||
from langchain_community.chat_models.jinachat import JinaChat
|
||||
from langchain_community.chat_models.konko import ChatKonko
|
||||
from langchain_community.chat_models.litellm import ChatLiteLLM
|
||||
from langchain_community.chat_models.litellm_router import ChatLiteLLMRouter
|
||||
from langchain_community.chat_models.llama_edge import LlamaEdgeChatService
|
||||
from langchain_community.chat_models.minimax import MiniMaxChat
|
||||
from langchain_community.chat_models.mlflow import ChatMlflow
|
||||
@@ -78,6 +79,7 @@ __all__ = [
|
||||
"MiniMaxChat",
|
||||
"ChatAnyscale",
|
||||
"ChatLiteLLM",
|
||||
"ChatLiteLLMRouter",
|
||||
"ErnieBotChat",
|
||||
"ChatJavelinAIGateway",
|
||||
"ChatKonko",
|
||||
|
||||
@@ -142,9 +142,10 @@ class ChatAnthropic(BaseChatModel, _AnthropicCommon):
|
||||
stream_resp = self.client.completions.create(**params, stream=True)
|
||||
for data in stream_resp:
|
||||
delta = data.completion
|
||||
yield ChatGenerationChunk(message=AIMessageChunk(content=delta))
|
||||
chunk = ChatGenerationChunk(message=AIMessageChunk(content=delta))
|
||||
yield chunk
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(delta)
|
||||
run_manager.on_llm_new_token(delta, chunk=chunk)
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
@@ -161,9 +162,10 @@ class ChatAnthropic(BaseChatModel, _AnthropicCommon):
|
||||
stream_resp = await self.async_client.completions.create(**params, stream=True)
|
||||
async for data in stream_resp:
|
||||
delta = data.completion
|
||||
yield ChatGenerationChunk(message=AIMessageChunk(content=delta))
|
||||
chunk = ChatGenerationChunk(message=AIMessageChunk(content=delta))
|
||||
yield chunk
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(delta)
|
||||
await run_manager.on_llm_new_token(delta, chunk=chunk)
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
|
||||
368
libs/community/langchain_community/chat_models/edenai.py
Normal file
368
libs/community/langchain_community/chat_models/edenai.py
Normal file
@@ -0,0 +1,368 @@
|
||||
import json
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional
|
||||
|
||||
from aiohttp import ClientSession
|
||||
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,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.pydantic_v1 import Extra, Field, SecretStr, root_validator
|
||||
from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env
|
||||
|
||||
from langchain_community.utilities.requests import Requests
|
||||
|
||||
|
||||
def _message_role(type: str) -> str:
|
||||
role_mapping = {"ai": "assistant", "human": "user", "chat": "user"}
|
||||
|
||||
if type in role_mapping:
|
||||
return role_mapping[type]
|
||||
else:
|
||||
raise ValueError(f"Unknown type: {type}")
|
||||
|
||||
|
||||
def _format_edenai_messages(messages: List[BaseMessage]) -> Dict[str, Any]:
|
||||
system = None
|
||||
formatted_messages = []
|
||||
text = messages[-1].content
|
||||
for i, message in enumerate(messages[:-1]):
|
||||
if message.type == "system":
|
||||
if i != 0:
|
||||
raise ValueError("System message must be at beginning of message list.")
|
||||
system = message.content
|
||||
else:
|
||||
formatted_messages.append(
|
||||
{
|
||||
"role": _message_role(message.type),
|
||||
"message": message.content,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"text": text,
|
||||
"previous_history": formatted_messages,
|
||||
"chatbot_global_action": system,
|
||||
}
|
||||
|
||||
|
||||
class ChatEdenAI(BaseChatModel):
|
||||
"""`EdenAI` chat large language models.
|
||||
|
||||
`EdenAI` is a versatile platform that allows you to access various language models
|
||||
from different providers such as Google, OpenAI, Cohere, Mistral and more.
|
||||
|
||||
To get started, make sure you have the environment variable ``EDENAI_API_KEY``
|
||||
set with your API key, or pass it as a named parameter to the constructor.
|
||||
|
||||
Additionally, `EdenAI` provides the flexibility to choose from a variety of models,
|
||||
including the ones like "gpt-4".
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.chat_models import ChatEdenAI
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
# Initialize `ChatEdenAI` with the desired configuration
|
||||
chat = ChatEdenAI(
|
||||
provider="openai",
|
||||
model="gpt-4",
|
||||
max_tokens=256,
|
||||
temperature=0.75)
|
||||
|
||||
# Create a list of messages to interact with the model
|
||||
messages = [HumanMessage(content="hello")]
|
||||
|
||||
# Invoke the model with the provided messages
|
||||
chat.invoke(messages)
|
||||
|
||||
`EdenAI` goes beyond mere model invocation. It empowers you with advanced features :
|
||||
|
||||
- **Multiple Providers**: access to a diverse range of llms offered by various
|
||||
providers giving you the freedom to choose the best-suited model for your use case.
|
||||
|
||||
- **Fallback Mechanism**: Set a fallback mechanism to ensure seamless operations
|
||||
even if the primary provider is unavailable, you can easily switches to an
|
||||
alternative provider.
|
||||
|
||||
- **Usage Statistics**: Track usage statistics on a per-project
|
||||
and per-API key basis.
|
||||
This feature allows you to monitor and manage resource consumption effectively.
|
||||
|
||||
- **Monitoring and Observability**: `EdenAI` provides comprehensive monitoring
|
||||
and observability tools on the platform.
|
||||
|
||||
Example of setting up a fallback mechanism:
|
||||
.. code-block:: python
|
||||
|
||||
# Initialize `ChatEdenAI` with a fallback provider
|
||||
chat_with_fallback = ChatEdenAI(
|
||||
provider="openai",
|
||||
model="gpt-4",
|
||||
max_tokens=256,
|
||||
temperature=0.75,
|
||||
fallback_provider="google")
|
||||
|
||||
you can find more details here : https://docs.edenai.co/reference/text_chat_create
|
||||
"""
|
||||
|
||||
provider: str = "openai"
|
||||
"""chat provider to use (eg: openai,google etc.)"""
|
||||
|
||||
model: Optional[str] = None
|
||||
"""
|
||||
model name for above provider (eg: 'gpt-4' for openai)
|
||||
available models are shown on https://docs.edenai.co/ under 'available providers'
|
||||
"""
|
||||
|
||||
max_tokens: int = 256
|
||||
"""Denotes the number of tokens to predict per generation."""
|
||||
|
||||
temperature: Optional[float] = 0
|
||||
"""A non-negative float that tunes the degree of randomness in generation."""
|
||||
|
||||
streaming: bool = False
|
||||
"""Whether to stream the results."""
|
||||
|
||||
fallback_providers: Optional[str] = None
|
||||
"""Providers in this will be used as fallback if the call to provider fails."""
|
||||
|
||||
edenai_api_url: str = "https://api.edenai.run/v2"
|
||||
|
||||
edenai_api_key: Optional[SecretStr] = Field(None, description="EdenAI API Token")
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
extra = Extra.forbid
|
||||
|
||||
@root_validator()
|
||||
def validate_environment(cls, values: Dict) -> Dict:
|
||||
"""Validate that api key exists in environment."""
|
||||
values["edenai_api_key"] = convert_to_secret_str(
|
||||
get_from_dict_or_env(values, "edenai_api_key", "EDENAI_API_KEY")
|
||||
)
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
def get_user_agent() -> str:
|
||||
from langchain_community import __version__
|
||||
|
||||
return f"langchain/{__version__}"
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
"""Return type of chat model."""
|
||||
return "edenai-chat"
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
"""Call out to EdenAI's chat endpoint."""
|
||||
url = f"{self.edenai_api_url}/text/chat/stream"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.edenai_api_key.get_secret_value()}",
|
||||
"User-Agent": self.get_user_agent(),
|
||||
}
|
||||
formatted_data = _format_edenai_messages(messages=messages)
|
||||
payload: Dict[str, Any] = {
|
||||
"providers": self.provider,
|
||||
"max_tokens": self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"fallback_providers": self.fallback_providers,
|
||||
**formatted_data,
|
||||
**kwargs,
|
||||
}
|
||||
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
if self.model is not None:
|
||||
payload["settings"] = {self.provider: self.model}
|
||||
|
||||
request = Requests(headers=headers)
|
||||
response = request.post(url=url, data=payload, stream=True)
|
||||
response.raise_for_status()
|
||||
|
||||
for chunk_response in response.iter_lines():
|
||||
chunk = json.loads(chunk_response.decode())
|
||||
token = chunk["text"]
|
||||
chat_generatio_chunk = ChatGenerationChunk(
|
||||
message=AIMessageChunk(content=token)
|
||||
)
|
||||
yield chat_generatio_chunk
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(token, chunk=chat_generatio_chunk)
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[ChatGenerationChunk]:
|
||||
url = f"{self.edenai_api_url}/text/chat/stream"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.edenai_api_key.get_secret_value()}",
|
||||
"User-Agent": self.get_user_agent(),
|
||||
}
|
||||
formatted_data = _format_edenai_messages(messages=messages)
|
||||
payload: Dict[str, Any] = {
|
||||
"providers": self.provider,
|
||||
"max_tokens": self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"fallback_providers": self.fallback_providers,
|
||||
**formatted_data,
|
||||
**kwargs,
|
||||
}
|
||||
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
if self.model is not None:
|
||||
payload["settings"] = {self.provider: self.model}
|
||||
|
||||
async with ClientSession() as session:
|
||||
async with session.post(url, json=payload, headers=headers) as response:
|
||||
response.raise_for_status()
|
||||
async for chunk_response in response.content:
|
||||
chunk = json.loads(chunk_response.decode())
|
||||
token = chunk["text"]
|
||||
chat_generation_chunk = ChatGenerationChunk(
|
||||
message=AIMessageChunk(content=token)
|
||||
)
|
||||
yield chat_generation_chunk
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(
|
||||
token=chunk["text"], chunk=chat_generation_chunk
|
||||
)
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
"""Call out to EdenAI's chat endpoint."""
|
||||
if self.streaming:
|
||||
stream_iter = self._stream(
|
||||
messages, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
return generate_from_stream(stream_iter)
|
||||
|
||||
url = f"{self.edenai_api_url}/text/chat"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.edenai_api_key.get_secret_value()}",
|
||||
"User-Agent": self.get_user_agent(),
|
||||
}
|
||||
formatted_data = _format_edenai_messages(messages=messages)
|
||||
payload: Dict[str, Any] = {
|
||||
"providers": self.provider,
|
||||
"max_tokens": self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"fallback_providers": self.fallback_providers,
|
||||
**formatted_data,
|
||||
**kwargs,
|
||||
}
|
||||
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
if self.model is not None:
|
||||
payload["settings"] = {self.provider: self.model}
|
||||
|
||||
request = Requests(headers=headers)
|
||||
response = request.post(url=url, data=payload)
|
||||
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
provider_response = data[self.provider]
|
||||
|
||||
if self.fallback_providers:
|
||||
fallback_response = data.get(self.fallback_providers)
|
||||
if fallback_response:
|
||||
provider_response = fallback_response
|
||||
|
||||
if provider_response.get("status") == "fail":
|
||||
err_msg = provider_response.get("error", {}).get("message")
|
||||
raise Exception(err_msg)
|
||||
|
||||
return ChatResult(
|
||||
generations=[
|
||||
ChatGeneration(
|
||||
message=AIMessage(content=provider_response["generated_text"])
|
||||
)
|
||||
],
|
||||
llm_output=data,
|
||||
)
|
||||
|
||||
async def _agenerate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
if self.streaming:
|
||||
stream_iter = self._astream(
|
||||
messages, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
return await agenerate_from_stream(stream_iter)
|
||||
|
||||
url = f"{self.edenai_api_url}/text/chat"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.edenai_api_key.get_secret_value()}",
|
||||
"User-Agent": self.get_user_agent(),
|
||||
}
|
||||
formatted_data = _format_edenai_messages(messages=messages)
|
||||
payload: Dict[str, Any] = {
|
||||
"providers": self.provider,
|
||||
"max_tokens": self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"fallback_providers": self.fallback_providers,
|
||||
**formatted_data,
|
||||
**kwargs,
|
||||
}
|
||||
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
if self.model is not None:
|
||||
payload["settings"] = {self.provider: self.model}
|
||||
|
||||
async with ClientSession() as session:
|
||||
async with session.post(url, json=payload, headers=headers) as response:
|
||||
response.raise_for_status()
|
||||
data = await response.json()
|
||||
provider_response = data[self.provider]
|
||||
|
||||
if self.fallback_providers:
|
||||
fallback_response = data.get(self.fallback_providers)
|
||||
if fallback_response:
|
||||
provider_response = fallback_response
|
||||
|
||||
if provider_response.get("status") == "fail":
|
||||
err_msg = provider_response.get("error", {}).get("message")
|
||||
raise Exception(err_msg)
|
||||
|
||||
return ChatResult(
|
||||
generations=[
|
||||
ChatGeneration(
|
||||
message=AIMessage(
|
||||
content=provider_response["generated_text"]
|
||||
)
|
||||
)
|
||||
],
|
||||
llm_output=data,
|
||||
)
|
||||
221
libs/community/langchain_community/chat_models/litellm_router.py
Normal file
221
libs/community/langchain_community/chat_models/litellm_router.py
Normal file
@@ -0,0 +1,221 @@
|
||||
"""LiteLLM Router as LangChain Model."""
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
)
|
||||
|
||||
from langchain_core.callbacks.manager import (
|
||||
AsyncCallbackManagerForLLMRun,
|
||||
CallbackManagerForLLMRun,
|
||||
)
|
||||
from langchain_core.language_models.chat_models import (
|
||||
agenerate_from_stream,
|
||||
generate_from_stream,
|
||||
)
|
||||
from langchain_core.messages import (
|
||||
AIMessageChunk,
|
||||
BaseMessage,
|
||||
)
|
||||
from langchain_core.outputs import (
|
||||
ChatGeneration,
|
||||
ChatGenerationChunk,
|
||||
ChatResult,
|
||||
)
|
||||
|
||||
from langchain_community.chat_models.litellm import (
|
||||
ChatLiteLLM,
|
||||
_convert_delta_to_message_chunk,
|
||||
_convert_dict_to_message,
|
||||
)
|
||||
|
||||
token_usage_key_name = "token_usage"
|
||||
model_extra_key_name = "model_extra"
|
||||
|
||||
|
||||
def get_llm_output(usage: Any, **params: Any) -> dict:
|
||||
"""Get llm output from usage and params."""
|
||||
llm_output = {token_usage_key_name: usage}
|
||||
# copy over metadata (metadata came from router completion call)
|
||||
metadata = params["metadata"]
|
||||
for key in metadata:
|
||||
if key not in llm_output:
|
||||
# if token usage in metadata, prefer metadata's copy of it
|
||||
llm_output[key] = metadata[key]
|
||||
return llm_output
|
||||
|
||||
|
||||
class ChatLiteLLMRouter(ChatLiteLLM):
|
||||
"""LiteLLM Router as LangChain Model."""
|
||||
|
||||
router: Any
|
||||
|
||||
def __init__(self, *, router: Any, **kwargs: Any) -> None:
|
||||
"""Construct Chat LiteLLM Router."""
|
||||
super().__init__(**kwargs)
|
||||
self.router = router
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "LiteLLMRouter"
|
||||
|
||||
def _set_model_for_completion(self) -> None:
|
||||
# use first model name (aka: model group),
|
||||
# since we can only pass one to the router completion functions
|
||||
self.model = self.router.model_list[0]["model_name"]
|
||||
|
||||
def _prepare_params_for_router(self, params: Any) -> None:
|
||||
params["model"] = self.model
|
||||
|
||||
# allow the router to set api_base based on its model choice
|
||||
api_base_key_name = "api_base"
|
||||
if api_base_key_name in params and params[api_base_key_name] is None:
|
||||
del params[api_base_key_name]
|
||||
|
||||
# add metadata so router can fill it below
|
||||
params.setdefault("metadata", {})
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = 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._stream(
|
||||
messages, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
return generate_from_stream(stream_iter)
|
||||
|
||||
message_dicts, params = self._create_message_dicts(messages, stop)
|
||||
params = {**params, **kwargs}
|
||||
self._set_model_for_completion()
|
||||
self._prepare_params_for_router(params)
|
||||
|
||||
response = self.router.completion(
|
||||
messages=message_dicts,
|
||||
**params,
|
||||
)
|
||||
return self._create_chat_result(response, **params)
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
default_chunk_class = AIMessageChunk
|
||||
message_dicts, params = self._create_message_dicts(messages, stop)
|
||||
params = {**params, **kwargs, "stream": True}
|
||||
self._set_model_for_completion()
|
||||
self._prepare_params_for_router(params)
|
||||
|
||||
for chunk in self.router.completion(messages=message_dicts, **params):
|
||||
if len(chunk["choices"]) == 0:
|
||||
continue
|
||||
delta = chunk["choices"][0]["delta"]
|
||||
chunk = _convert_delta_to_message_chunk(delta, default_chunk_class)
|
||||
default_chunk_class = chunk.__class__
|
||||
yield ChatGenerationChunk(message=chunk)
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(chunk.content, **params)
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[ChatGenerationChunk]:
|
||||
default_chunk_class = AIMessageChunk
|
||||
message_dicts, params = self._create_message_dicts(messages, stop)
|
||||
params = {**params, **kwargs, "stream": True}
|
||||
self._set_model_for_completion()
|
||||
self._prepare_params_for_router(params)
|
||||
|
||||
async for chunk in await self.router.acompletion(
|
||||
messages=message_dicts, **params
|
||||
):
|
||||
if len(chunk["choices"]) == 0:
|
||||
continue
|
||||
delta = chunk["choices"][0]["delta"]
|
||||
chunk = _convert_delta_to_message_chunk(delta, default_chunk_class)
|
||||
default_chunk_class = chunk.__class__
|
||||
yield ChatGenerationChunk(message=chunk)
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(chunk.content, **params)
|
||||
|
||||
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=messages, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
return await agenerate_from_stream(stream_iter)
|
||||
|
||||
message_dicts, params = self._create_message_dicts(messages, stop)
|
||||
params = {**params, **kwargs}
|
||||
self._set_model_for_completion()
|
||||
self._prepare_params_for_router(params)
|
||||
|
||||
response = await self.router.acompletion(
|
||||
messages=message_dicts,
|
||||
**params,
|
||||
)
|
||||
return self._create_chat_result(response, **params)
|
||||
|
||||
# from
|
||||
# https://github.com/langchain-ai/langchain/blob/master/libs/community/langchain_community/chat_models/openai.py
|
||||
# but modified to handle LiteLLM Usage class
|
||||
def _combine_llm_outputs(self, llm_outputs: List[Optional[dict]]) -> dict:
|
||||
overall_token_usage: dict = {}
|
||||
system_fingerprint = None
|
||||
for output in llm_outputs:
|
||||
if output is None:
|
||||
# Happens in streaming
|
||||
continue
|
||||
token_usage = output["token_usage"]
|
||||
if token_usage is not None:
|
||||
# get dict from LiteLLM Usage class
|
||||
for k, v in token_usage.dict().items():
|
||||
if k in overall_token_usage:
|
||||
overall_token_usage[k] += v
|
||||
else:
|
||||
overall_token_usage[k] = v
|
||||
if system_fingerprint is None:
|
||||
system_fingerprint = output.get("system_fingerprint")
|
||||
combined = {"token_usage": overall_token_usage, "model_name": self.model_name}
|
||||
if system_fingerprint:
|
||||
combined["system_fingerprint"] = system_fingerprint
|
||||
return combined
|
||||
|
||||
def _create_chat_result(
|
||||
self, response: Mapping[str, Any], **params: Any
|
||||
) -> ChatResult:
|
||||
from litellm.utils import Usage
|
||||
|
||||
generations = []
|
||||
for res in response["choices"]:
|
||||
message = _convert_dict_to_message(res["message"])
|
||||
gen = ChatGeneration(
|
||||
message=message,
|
||||
generation_info=dict(finish_reason=res.get("finish_reason")),
|
||||
)
|
||||
generations.append(gen)
|
||||
token_usage = response.get("usage", Usage(prompt_tokens=0, total_tokens=0))
|
||||
llm_output = get_llm_output(token_usage, **params)
|
||||
return ChatResult(generations=generations, llm_output=llm_output)
|
||||
@@ -139,7 +139,8 @@ def _make_request(
|
||||
)
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Please install YandexCloud SDK" " with `pip install yandexcloud`."
|
||||
"Please install YandexCloud SDK with `pip install yandexcloud` \
|
||||
or upgrade it to recent version."
|
||||
) from e
|
||||
if not messages:
|
||||
raise ValueError("You should provide at least one message to start the chat!")
|
||||
@@ -182,7 +183,8 @@ async def _amake_request(self: ChatYandexGPT, messages: List[BaseMessage]) -> st
|
||||
)
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Please install YandexCloud SDK" " with `pip install yandexcloud`."
|
||||
"Please install YandexCloud SDK with `pip install yandexcloud` \
|
||||
or upgrade it to recent version."
|
||||
) from e
|
||||
if not messages:
|
||||
raise ValueError("You should provide at least one message to start the chat!")
|
||||
@@ -219,7 +221,7 @@ async def _amake_request(self: ChatYandexGPT, messages: List[BaseMessage]) -> st
|
||||
def _create_retry_decorator(llm: ChatYandexGPT) -> Callable[[Any], Any]:
|
||||
from grpc import RpcError
|
||||
|
||||
min_seconds = 1
|
||||
min_seconds = llm.sleep_interval
|
||||
max_seconds = 60
|
||||
return retry(
|
||||
reraise=True,
|
||||
|
||||
@@ -59,6 +59,7 @@ from langchain_community.document_loaders.blob_loaders import (
|
||||
from langchain_community.document_loaders.blockchain import BlockchainDocumentLoader
|
||||
from langchain_community.document_loaders.brave_search import BraveSearchLoader
|
||||
from langchain_community.document_loaders.browserless import BrowserlessLoader
|
||||
from langchain_community.document_loaders.cassandra import CassandraLoader
|
||||
from langchain_community.document_loaders.chatgpt import ChatGPTLoader
|
||||
from langchain_community.document_loaders.chromium import AsyncChromiumLoader
|
||||
from langchain_community.document_loaders.college_confidential import (
|
||||
@@ -267,6 +268,7 @@ __all__ = [
|
||||
"BlockchainDocumentLoader",
|
||||
"BraveSearchLoader",
|
||||
"BrowserlessLoader",
|
||||
"CassandraLoader",
|
||||
"CSVLoader",
|
||||
"ChatGPTLoader",
|
||||
"CoNLLULoader",
|
||||
|
||||
@@ -2,12 +2,24 @@ import json
|
||||
import logging
|
||||
import threading
|
||||
from queue import Queue
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
)
|
||||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langchain_community.document_loaders.base import BaseLoader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from astrapy.db import AstraDB, AsyncAstraDB
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -19,7 +31,8 @@ class AstraDBLoader(BaseLoader):
|
||||
collection_name: str,
|
||||
token: Optional[str] = None,
|
||||
api_endpoint: Optional[str] = None,
|
||||
astra_db_client: Optional[Any] = None, # 'astrapy.db.AstraDB' if passed
|
||||
astra_db_client: Optional["AstraDB"] = None,
|
||||
async_astra_db_client: Optional["AsyncAstraDB"] = None,
|
||||
namespace: Optional[str] = None,
|
||||
filter_criteria: Optional[Dict[str, Any]] = None,
|
||||
projection: Optional[Dict[str, Any]] = None,
|
||||
@@ -36,34 +49,60 @@ class AstraDBLoader(BaseLoader):
|
||||
)
|
||||
|
||||
# Conflicting-arg checks:
|
||||
if astra_db_client is not None:
|
||||
if astra_db_client is not None or async_astra_db_client is not None:
|
||||
if token is not None or api_endpoint is not None:
|
||||
raise ValueError(
|
||||
"You cannot pass 'astra_db_client' to AstraDB if passing "
|
||||
"'token' and 'api_endpoint'."
|
||||
"You cannot pass 'astra_db_client' or 'async_astra_db_client' to "
|
||||
"AstraDB if passing 'token' and 'api_endpoint'."
|
||||
)
|
||||
|
||||
self.collection_name = collection_name
|
||||
self.filter = filter_criteria
|
||||
self.projection = projection
|
||||
self.find_options = find_options or {}
|
||||
self.nb_prefetched = nb_prefetched
|
||||
self.extraction_function = extraction_function
|
||||
|
||||
if astra_db_client is not None:
|
||||
astra_db = astra_db_client
|
||||
else:
|
||||
astra_db = astra_db_client
|
||||
async_astra_db = async_astra_db_client
|
||||
|
||||
if token and api_endpoint:
|
||||
astra_db = AstraDB(
|
||||
token=token,
|
||||
api_endpoint=api_endpoint,
|
||||
namespace=namespace,
|
||||
)
|
||||
self.collection = astra_db.collection(collection_name)
|
||||
try:
|
||||
from astrapy.db import AsyncAstraDB
|
||||
|
||||
async_astra_db = AsyncAstraDB(
|
||||
token=token,
|
||||
api_endpoint=api_endpoint,
|
||||
namespace=namespace,
|
||||
)
|
||||
except (ImportError, ModuleNotFoundError):
|
||||
pass
|
||||
if not astra_db and not async_astra_db:
|
||||
raise ValueError(
|
||||
"Must provide 'astra_db_client' or 'async_astra_db_client' or 'token' "
|
||||
"and 'api_endpoint'"
|
||||
)
|
||||
self.collection = astra_db.collection(collection_name) if astra_db else None
|
||||
if async_astra_db:
|
||||
from astrapy.db import AsyncAstraDBCollection
|
||||
|
||||
self.async_collection = AsyncAstraDBCollection(
|
||||
astra_db=async_astra_db, collection_name=collection_name
|
||||
)
|
||||
else:
|
||||
self.async_collection = None
|
||||
|
||||
def load(self) -> List[Document]:
|
||||
"""Eagerly load the content."""
|
||||
return list(self.lazy_load())
|
||||
|
||||
def lazy_load(self) -> Iterator[Document]:
|
||||
if not self.collection:
|
||||
raise ValueError("Missing AstraDB client")
|
||||
queue = Queue(self.nb_prefetched)
|
||||
t = threading.Thread(target=self.fetch_results, args=(queue,))
|
||||
t.start()
|
||||
@@ -74,6 +113,29 @@ class AstraDBLoader(BaseLoader):
|
||||
yield doc
|
||||
t.join()
|
||||
|
||||
async def aload(self) -> List[Document]:
|
||||
"""Load data into Document objects."""
|
||||
return [doc async for doc in self.alazy_load()]
|
||||
|
||||
async def alazy_load(self) -> AsyncIterator[Document]:
|
||||
if not self.async_collection:
|
||||
raise ValueError("Missing AsyncAstraDB client")
|
||||
async for doc in self.async_collection.paginated_find(
|
||||
filter=self.filter,
|
||||
options=self.find_options,
|
||||
projection=self.projection,
|
||||
sort=None,
|
||||
prefetched=True,
|
||||
):
|
||||
yield Document(
|
||||
page_content=self.extraction_function(doc),
|
||||
metadata={
|
||||
"namespace": self.async_collection.astra_db.namespace,
|
||||
"api_endpoint": self.async_collection.astra_db.base_url,
|
||||
"collection": self.collection_name,
|
||||
},
|
||||
)
|
||||
|
||||
def fetch_results(self, queue: Queue):
|
||||
self.fetch_page_result(queue)
|
||||
while self.find_options.get("pageState"):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import json
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
@@ -14,13 +13,6 @@ from langchain_core.documents import Document
|
||||
|
||||
from langchain_community.document_loaders.base import BaseLoader
|
||||
|
||||
|
||||
def default_page_content_mapper(row: Any) -> str:
|
||||
if hasattr(row, "_asdict"):
|
||||
return json.dumps(row._asdict())
|
||||
return json.dumps(row)
|
||||
|
||||
|
||||
_NOT_SET = object()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -36,7 +28,7 @@ class CassandraLoader(BaseLoader):
|
||||
session: Optional["Session"] = None,
|
||||
keyspace: Optional[str] = None,
|
||||
query: Optional[Union[str, "Statement"]] = None,
|
||||
page_content_mapper: Callable[[Any], str] = default_page_content_mapper,
|
||||
page_content_mapper: Callable[[Any], str] = str,
|
||||
metadata_mapper: Callable[[Any], dict] = lambda _: {},
|
||||
*,
|
||||
query_parameters: Union[dict, Sequence] = None,
|
||||
@@ -61,6 +53,7 @@ class CassandraLoader(BaseLoader):
|
||||
query: The query used to load the data.
|
||||
(do not use together with the table parameter)
|
||||
page_content_mapper: a function to convert a row to string page content.
|
||||
Defaults to the str representation of the row.
|
||||
query_parameters: The query parameters used when calling session.execute .
|
||||
query_timeout: The query timeout used when calling session.execute .
|
||||
query_custom_payload: The query custom_payload used when calling
|
||||
|
||||
@@ -132,6 +132,7 @@ class WebBaseLoader(BaseLoader):
|
||||
url,
|
||||
headers=self.session.headers,
|
||||
ssl=None if self.session.verify else False,
|
||||
cookies=self.session.cookies.get_dict(),
|
||||
) as response:
|
||||
return await response.text()
|
||||
except aiohttp.ClientConnectionError as e:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Sequence, Union
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
@@ -139,6 +140,11 @@ def _parse_video_id(url: str) -> Optional[str]:
|
||||
return video_id
|
||||
|
||||
|
||||
class TranscriptFormat(Enum):
|
||||
TEXT = "text"
|
||||
LINES = "lines"
|
||||
|
||||
|
||||
class YoutubeLoader(BaseLoader):
|
||||
"""Load `YouTube` transcripts."""
|
||||
|
||||
@@ -148,6 +154,7 @@ class YoutubeLoader(BaseLoader):
|
||||
add_video_info: bool = False,
|
||||
language: Union[str, Sequence[str]] = "en",
|
||||
translation: Optional[str] = None,
|
||||
transcript_format: TranscriptFormat = TranscriptFormat.TEXT,
|
||||
continue_on_failure: bool = False,
|
||||
):
|
||||
"""Initialize with YouTube video ID."""
|
||||
@@ -159,6 +166,7 @@ class YoutubeLoader(BaseLoader):
|
||||
else:
|
||||
self.language = language
|
||||
self.translation = translation
|
||||
self.transcript_format = transcript_format
|
||||
self.continue_on_failure = continue_on_failure
|
||||
|
||||
@staticmethod
|
||||
@@ -214,9 +222,19 @@ class YoutubeLoader(BaseLoader):
|
||||
|
||||
transcript_pieces = transcript.fetch()
|
||||
|
||||
transcript = " ".join([t["text"].strip(" ") for t in transcript_pieces])
|
||||
|
||||
return [Document(page_content=transcript, metadata=metadata)]
|
||||
if self.transcript_format == TranscriptFormat.TEXT:
|
||||
transcript = " ".join([t["text"].strip(" ") for t in transcript_pieces])
|
||||
return [Document(page_content=transcript, metadata=metadata)]
|
||||
elif self.transcript_format == TranscriptFormat.LINES:
|
||||
return [
|
||||
Document(
|
||||
page_content=t["text"].strip(" "),
|
||||
metadata=dict((key, t[key]) for key in t if key != "text"),
|
||||
)
|
||||
for t in transcript_pieces
|
||||
]
|
||||
else:
|
||||
raise ValueError("Unknown transcript format.")
|
||||
|
||||
def _get_video_info(self) -> dict:
|
||||
"""Get important video information.
|
||||
|
||||
@@ -20,6 +20,7 @@ from langchain_community.embeddings.aleph_alpha import (
|
||||
)
|
||||
from langchain_community.embeddings.awa import AwaEmbeddings
|
||||
from langchain_community.embeddings.azure_openai import AzureOpenAIEmbeddings
|
||||
from langchain_community.embeddings.baichuan import BaichuanTextEmbeddings
|
||||
from langchain_community.embeddings.baidu_qianfan_endpoint import (
|
||||
QianfanEmbeddingsEndpoint,
|
||||
)
|
||||
@@ -65,6 +66,7 @@ from langchain_community.embeddings.mlflow_gateway import MlflowAIGatewayEmbeddi
|
||||
from langchain_community.embeddings.modelscope_hub import ModelScopeEmbeddings
|
||||
from langchain_community.embeddings.mosaicml import MosaicMLInstructorEmbeddings
|
||||
from langchain_community.embeddings.nlpcloud import NLPCloudEmbeddings
|
||||
from langchain_community.embeddings.oci_generative_ai import OCIGenAIEmbeddings
|
||||
from langchain_community.embeddings.octoai_embeddings import OctoAIEmbeddings
|
||||
from langchain_community.embeddings.ollama import OllamaEmbeddings
|
||||
from langchain_community.embeddings.openai import OpenAIEmbeddings
|
||||
@@ -91,6 +93,7 @@ logger = logging.getLogger(__name__)
|
||||
__all__ = [
|
||||
"OpenAIEmbeddings",
|
||||
"AzureOpenAIEmbeddings",
|
||||
"BaichuanTextEmbeddings",
|
||||
"ClarifaiEmbeddings",
|
||||
"CohereEmbeddings",
|
||||
"DatabricksEmbeddings",
|
||||
@@ -144,6 +147,7 @@ __all__ = [
|
||||
"VoyageEmbeddings",
|
||||
"BookendEmbeddings",
|
||||
"VolcanoEmbeddings",
|
||||
"OCIGenAIEmbeddings",
|
||||
]
|
||||
|
||||
|
||||
|
||||
113
libs/community/langchain_community/embeddings/baichuan.py
Normal file
113
libs/community/langchain_community/embeddings/baichuan.py
Normal file
@@ -0,0 +1,113 @@
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import requests
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.pydantic_v1 import BaseModel, SecretStr, root_validator
|
||||
from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env
|
||||
|
||||
BAICHUAN_API_URL: str = "http://api.baichuan-ai.com/v1/embeddings"
|
||||
|
||||
# BaichuanTextEmbeddings is an embedding model provided by Baichuan Inc. (https://www.baichuan-ai.com/home).
|
||||
# As of today (Jan 25th, 2024) BaichuanTextEmbeddings ranks #1 in C-MTEB
|
||||
# (Chinese Multi-Task Embedding Benchmark) leaderboard.
|
||||
# Leaderboard (Under Overall -> Chinese section): https://huggingface.co/spaces/mteb/leaderboard
|
||||
|
||||
# Official Website: https://platform.baichuan-ai.com/docs/text-Embedding
|
||||
# An API-key is required to use this embedding model. You can get one by registering
|
||||
# at https://platform.baichuan-ai.com/docs/text-Embedding.
|
||||
# BaichuanTextEmbeddings support 512 token window and preduces vectors with
|
||||
# 1024 dimensions.
|
||||
|
||||
|
||||
# NOTE!! BaichuanTextEmbeddings only supports Chinese text embedding.
|
||||
# Multi-language support is coming soon.
|
||||
class BaichuanTextEmbeddings(BaseModel, Embeddings):
|
||||
"""Baichuan Text Embedding models."""
|
||||
|
||||
session: Any #: :meta private:
|
||||
model_name: str = "Baichuan-Text-Embedding"
|
||||
baichuan_api_key: Optional[SecretStr] = None
|
||||
|
||||
@root_validator(allow_reuse=True)
|
||||
def validate_environment(cls, values: Dict) -> Dict:
|
||||
"""Validate that auth token exists in environment."""
|
||||
try:
|
||||
baichuan_api_key = convert_to_secret_str(
|
||||
get_from_dict_or_env(values, "baichuan_api_key", "BAICHUAN_API_KEY")
|
||||
)
|
||||
except ValueError as original_exc:
|
||||
try:
|
||||
baichuan_api_key = convert_to_secret_str(
|
||||
get_from_dict_or_env(
|
||||
values, "baichuan_auth_token", "BAICHUAN_AUTH_TOKEN"
|
||||
)
|
||||
)
|
||||
except ValueError:
|
||||
raise original_exc
|
||||
session = requests.Session()
|
||||
session.headers.update(
|
||||
{
|
||||
"Authorization": f"Bearer {baichuan_api_key.get_secret_value()}",
|
||||
"Accept-Encoding": "identity",
|
||||
"Content-type": "application/json",
|
||||
}
|
||||
)
|
||||
values["session"] = session
|
||||
return values
|
||||
|
||||
def _embed(self, texts: List[str]) -> Optional[List[List[float]]]:
|
||||
"""Internal method to call Baichuan Embedding API and return embeddings.
|
||||
|
||||
Args:
|
||||
texts: A list of texts to embed.
|
||||
|
||||
Returns:
|
||||
A list of list of floats representing the embeddings, or None if an
|
||||
error occurs.
|
||||
"""
|
||||
try:
|
||||
response = self.session.post(
|
||||
BAICHUAN_API_URL, json={"input": texts, "model": self.model_name}
|
||||
)
|
||||
# Check if the response status code indicates success
|
||||
if response.status_code == 200:
|
||||
resp = response.json()
|
||||
embeddings = resp.get("data", [])
|
||||
# Sort resulting embeddings by index
|
||||
sorted_embeddings = sorted(embeddings, key=lambda e: e.get("index", 0))
|
||||
# Return just the embeddings
|
||||
return [result.get("embedding", []) for result in sorted_embeddings]
|
||||
else:
|
||||
# Log error or handle unsuccessful response appropriately
|
||||
print(
|
||||
f"""Error: Received status code {response.status_code} from
|
||||
embedding API"""
|
||||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
# Log the exception or handle it as needed
|
||||
print(f"Exception occurred while trying to get embeddings: {str(e)}")
|
||||
return None
|
||||
|
||||
def embed_documents(self, texts: List[str]) -> Optional[List[List[float]]]:
|
||||
"""Public method to get embeddings for a list of documents.
|
||||
|
||||
Args:
|
||||
texts: The list of texts to embed.
|
||||
|
||||
Returns:
|
||||
A list of embeddings, one for each text, or None if an error occurs.
|
||||
"""
|
||||
return self._embed(texts)
|
||||
|
||||
def embed_query(self, text: str) -> Optional[List[float]]:
|
||||
"""Public method to get embedding for a single query text.
|
||||
|
||||
Args:
|
||||
text: The text to embed.
|
||||
|
||||
Returns:
|
||||
Embeddings for the text, or None if an error occurs.
|
||||
"""
|
||||
result = self._embed([text])
|
||||
return result[0] if result is not None else None
|
||||
@@ -0,0 +1,203 @@
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Mapping, Optional
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.pydantic_v1 import BaseModel, Extra, root_validator
|
||||
|
||||
CUSTOM_ENDPOINT_PREFIX = "ocid1.generativeaiendpoint"
|
||||
|
||||
|
||||
class OCIAuthType(Enum):
|
||||
API_KEY = 1
|
||||
SECURITY_TOKEN = 2
|
||||
INSTANCE_PRINCIPAL = 3
|
||||
RESOURCE_PRINCIPAL = 4
|
||||
|
||||
|
||||
class OCIGenAIEmbeddings(BaseModel, Embeddings):
|
||||
"""OCI embedding models.
|
||||
|
||||
To authenticate, the OCI client uses the methods described in
|
||||
https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdk_authentication_methods.htm
|
||||
|
||||
The authentifcation method is passed through auth_type and should be one of:
|
||||
API_KEY (default), SECURITY_TOKEN, INSTANCE_PRINCIPLE, RESOURCE_PRINCIPLE
|
||||
|
||||
Make sure you have the required policies (profile/roles) to
|
||||
access the OCI Generative AI service. If a specific config profile is used,
|
||||
you must pass the name of the profile (~/.oci/config) through auth_profile.
|
||||
|
||||
To use, you must provide the compartment id
|
||||
along with the endpoint url, and model id
|
||||
as named parameters to the constructor.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain.embeddings import OCIGenAIEmbeddings
|
||||
|
||||
embeddings = OCIGenAIEmbeddings(
|
||||
model_id="MY_EMBEDDING_MODEL",
|
||||
service_endpoint="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com",
|
||||
compartment_id="MY_OCID"
|
||||
)
|
||||
"""
|
||||
|
||||
client: Any #: :meta private:
|
||||
|
||||
service_models: Any #: :meta private:
|
||||
|
||||
auth_type: Optional[str] = "API_KEY"
|
||||
"""Authentication type, could be
|
||||
|
||||
API_KEY,
|
||||
SECURITY_TOKEN,
|
||||
INSTANCE_PRINCIPLE,
|
||||
RESOURCE_PRINCIPLE
|
||||
|
||||
If not specified, API_KEY will be used
|
||||
"""
|
||||
|
||||
auth_profile: Optional[str] = "DEFAULT"
|
||||
"""The name of the profile in ~/.oci/config
|
||||
If not specified , DEFAULT will be used
|
||||
"""
|
||||
|
||||
model_id: str = None
|
||||
"""Id of the model to call, e.g., cohere.embed-english-light-v2.0"""
|
||||
|
||||
model_kwargs: Optional[Dict] = None
|
||||
"""Keyword arguments to pass to the model"""
|
||||
|
||||
service_endpoint: str = None
|
||||
"""service endpoint url"""
|
||||
|
||||
compartment_id: str = None
|
||||
"""OCID of compartment"""
|
||||
|
||||
truncate: Optional[str] = "END"
|
||||
"""Truncate embeddings that are too long from start or end ("NONE"|"START"|"END")"""
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
extra = Extra.forbid
|
||||
|
||||
@root_validator()
|
||||
def validate_environment(cls, values: Dict) -> Dict: # pylint: disable=no-self-argument
|
||||
"""Validate that OCI config and python package exists in environment."""
|
||||
|
||||
# Skip creating new client if passed in constructor
|
||||
if values["client"] is not None:
|
||||
return values
|
||||
|
||||
try:
|
||||
import oci
|
||||
|
||||
client_kwargs = {
|
||||
"config": {},
|
||||
"signer": None,
|
||||
"service_endpoint": values["service_endpoint"],
|
||||
"retry_strategy": oci.retry.DEFAULT_RETRY_STRATEGY,
|
||||
"timeout": (10, 240), # default timeout config for OCI Gen AI service
|
||||
}
|
||||
|
||||
if values["auth_type"] == OCIAuthType(1).name:
|
||||
client_kwargs["config"] = oci.config.from_file(
|
||||
profile_name=values["auth_profile"]
|
||||
)
|
||||
client_kwargs.pop("signer", None)
|
||||
elif values["auth_type"] == OCIAuthType(2).name:
|
||||
|
||||
def make_security_token_signer(oci_config):
|
||||
pk = oci.signer.load_private_key_from_file(
|
||||
oci_config.get("key_file"), None
|
||||
)
|
||||
with open(
|
||||
oci_config.get("security_token_file"), encoding="utf-8"
|
||||
) as f:
|
||||
st_string = f.read()
|
||||
return oci.auth.signers.SecurityTokenSigner(st_string, pk)
|
||||
|
||||
client_kwargs["config"] = oci.config.from_file(
|
||||
profile_name=values["auth_profile"]
|
||||
)
|
||||
client_kwargs["signer"] = make_security_token_signer(
|
||||
oci_config=client_kwargs["config"]
|
||||
)
|
||||
elif values["auth_type"] == OCIAuthType(3).name:
|
||||
client_kwargs[
|
||||
"signer"
|
||||
] = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
|
||||
elif values["auth_type"] == OCIAuthType(4).name:
|
||||
client_kwargs[
|
||||
"signer"
|
||||
] = oci.auth.signers.get_resource_principals_signer()
|
||||
else:
|
||||
raise ValueError("Please provide valid value to auth_type")
|
||||
|
||||
values["client"] = oci.generative_ai_inference.GenerativeAiInferenceClient(
|
||||
**client_kwargs
|
||||
)
|
||||
|
||||
except ImportError as ex:
|
||||
raise ModuleNotFoundError(
|
||||
"Could not import oci python package. "
|
||||
"Please make sure you have the oci package installed."
|
||||
) from ex
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
"Could not authenticate with OCI client. "
|
||||
"Please check if ~/.oci/config exists. "
|
||||
"If INSTANCE_PRINCIPLE or RESOURCE_PRINCIPLE is used, "
|
||||
"Please check the specified "
|
||||
"auth_profile and auth_type are valid."
|
||||
) from e
|
||||
|
||||
return values
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Mapping[str, Any]:
|
||||
"""Get the identifying parameters."""
|
||||
_model_kwargs = self.model_kwargs or {}
|
||||
return {
|
||||
**{"model_kwargs": _model_kwargs},
|
||||
}
|
||||
|
||||
def embed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||
"""Call out to OCIGenAI's embedding endpoint.
|
||||
|
||||
Args:
|
||||
texts: The list of texts to embed.
|
||||
|
||||
Returns:
|
||||
List of embeddings, one for each text.
|
||||
"""
|
||||
from oci.generative_ai_inference import models
|
||||
|
||||
if self.model_id.startswith(CUSTOM_ENDPOINT_PREFIX):
|
||||
serving_mode = models.DedicatedServingMode(endpoint_id=self.model_id)
|
||||
else:
|
||||
serving_mode = models.OnDemandServingMode(model_id=self.model_id)
|
||||
|
||||
invocation_obj = models.EmbedTextDetails(
|
||||
serving_mode=serving_mode,
|
||||
compartment_id=self.compartment_id,
|
||||
truncate=self.truncate,
|
||||
inputs=texts,
|
||||
)
|
||||
|
||||
response = self.client.embed_text(invocation_obj)
|
||||
|
||||
return response.data.embeddings
|
||||
|
||||
def embed_query(self, text: str) -> List[float]:
|
||||
"""Call out to OCIGenAI's embedding endpoint.
|
||||
|
||||
Args:
|
||||
text: The text to embed.
|
||||
|
||||
Returns:
|
||||
Embeddings for the text.
|
||||
"""
|
||||
return self.embed_documents([text])[0]
|
||||
@@ -71,9 +71,9 @@ class SelfHostedHuggingFaceEmbeddings(SelfHostedEmbeddings):
|
||||
|
||||
from langchain_community.embeddings import SelfHostedHuggingFaceEmbeddings
|
||||
import runhouse as rh
|
||||
model_name = "sentence-transformers/all-mpnet-base-v2"
|
||||
model_id = "sentence-transformers/all-mpnet-base-v2"
|
||||
gpu = rh.cluster(name="rh-a10x", instance_type="A100:1")
|
||||
hf = SelfHostedHuggingFaceEmbeddings(model_name=model_name, hardware=gpu)
|
||||
hf = SelfHostedHuggingFaceEmbeddings(model_id=model_id, hardware=gpu)
|
||||
"""
|
||||
|
||||
client: Any #: :meta private:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Any, Callable, Dict, List
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
@@ -59,6 +60,8 @@ class YandexGPTEmbeddings(BaseModel, Embeddings):
|
||||
"""The url of the API."""
|
||||
max_retries: int = 6
|
||||
"""Maximum number of retries to make when generating."""
|
||||
sleep_interval: float = 0.0
|
||||
"""Delay between API requests"""
|
||||
|
||||
@root_validator()
|
||||
def validate_environment(cls, values: Dict) -> Dict:
|
||||
@@ -154,7 +157,8 @@ def _make_request(self: YandexGPTEmbeddings, texts: List[str]):
|
||||
)
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Please install YandexCloud SDK" " with `pip install yandexcloud`."
|
||||
"Please install YandexCloud SDK with `pip install yandexcloud` \
|
||||
or upgrade it to recent version."
|
||||
) from e
|
||||
result = []
|
||||
channel_credentials = grpc.ssl_channel_credentials()
|
||||
@@ -164,6 +168,7 @@ def _make_request(self: YandexGPTEmbeddings, texts: List[str]):
|
||||
request = TextEmbeddingRequest(model_uri=self.model_uri, text=text)
|
||||
stub = EmbeddingsServiceStub(channel)
|
||||
res = stub.TextEmbedding(request, metadata=self._grpc_metadata)
|
||||
result.append(res.embedding)
|
||||
result.append(list(res.embedding))
|
||||
time.sleep(self.sleep_interval)
|
||||
|
||||
return result
|
||||
|
||||
@@ -346,6 +346,12 @@ def _import_oci_md_vllm() -> Any:
|
||||
return OCIModelDeploymentVLLM
|
||||
|
||||
|
||||
def _import_oci_gen_ai() -> Any:
|
||||
from langchain_community.llms.oci_generative_ai import OCIGenAI
|
||||
|
||||
return OCIGenAI
|
||||
|
||||
|
||||
def _import_octoai_endpoint() -> Any:
|
||||
from langchain_community.llms.octoai_endpoint import OctoAIEndpoint
|
||||
|
||||
@@ -667,6 +673,8 @@ def __getattr__(name: str) -> Any:
|
||||
return _import_oci_md_tgi()
|
||||
elif name == "OCIModelDeploymentVLLM":
|
||||
return _import_oci_md_vllm()
|
||||
elif name == "OCIGenAI":
|
||||
return _import_oci_gen_ai()
|
||||
elif name == "OctoAIEndpoint":
|
||||
return _import_octoai_endpoint()
|
||||
elif name == "Ollama":
|
||||
@@ -801,6 +809,7 @@ __all__ = [
|
||||
"NLPCloud",
|
||||
"OCIModelDeploymentTGI",
|
||||
"OCIModelDeploymentVLLM",
|
||||
"OCIGenAI",
|
||||
"Ollama",
|
||||
"OpenAI",
|
||||
"OpenAIChat",
|
||||
@@ -891,6 +900,7 @@ def get_type_to_cls_dict() -> Dict[str, Callable[[], Type[BaseLLM]]]:
|
||||
"nlpcloud": _import_nlpcloud,
|
||||
"oci_model_deployment_tgi_endpoint": _import_oci_md_tgi,
|
||||
"oci_model_deployment_vllm_endpoint": _import_oci_md_vllm,
|
||||
"oci_generative_ai": _import_oci_gen_ai,
|
||||
"ollama": _import_ollama,
|
||||
"openai": _import_openai,
|
||||
"openlm": _import_openlm,
|
||||
|
||||
@@ -34,6 +34,8 @@ from langchain_community.utilities.anthropic import (
|
||||
if TYPE_CHECKING:
|
||||
from botocore.config import Config
|
||||
|
||||
AMAZON_BEDROCK_TRACE_KEY = "amazon-bedrock-trace"
|
||||
GUARDRAILS_BODY_KEY = "amazon-bedrock-guardrailAssessment"
|
||||
HUMAN_PROMPT = "\n\nHuman:"
|
||||
ASSISTANT_PROMPT = "\n\nAssistant:"
|
||||
ALTERNATION_ERROR = (
|
||||
@@ -117,21 +119,26 @@ class LLMInputOutputAdapter:
|
||||
return input_body
|
||||
|
||||
@classmethod
|
||||
def prepare_output(cls, provider: str, response: Any) -> str:
|
||||
def prepare_output(cls, provider: str, response: Any) -> dict:
|
||||
if provider == "anthropic":
|
||||
response_body = json.loads(response.get("body").read().decode())
|
||||
return response_body.get("completion")
|
||||
text = response_body.get("completion")
|
||||
else:
|
||||
response_body = json.loads(response.get("body").read())
|
||||
|
||||
if provider == "ai21":
|
||||
return response_body.get("completions")[0].get("data").get("text")
|
||||
elif provider == "cohere":
|
||||
return response_body.get("generations")[0].get("text")
|
||||
elif provider == "meta":
|
||||
return response_body.get("generation")
|
||||
else:
|
||||
return response_body.get("results")[0].get("outputText")
|
||||
if provider == "ai21":
|
||||
text = response_body.get("completions")[0].get("data").get("text")
|
||||
elif provider == "cohere":
|
||||
text = response_body.get("generations")[0].get("text")
|
||||
elif provider == "meta":
|
||||
text = response_body.get("generation")
|
||||
else:
|
||||
text = response_body.get("results")[0].get("outputText")
|
||||
|
||||
return {
|
||||
"text": text,
|
||||
"body": response_body,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def prepare_output_stream(
|
||||
@@ -160,8 +167,15 @@ class LLMInputOutputAdapter:
|
||||
chunk_obj["is_finished"] or chunk_obj[output_key] == "<EOS_TOKEN>"
|
||||
):
|
||||
return
|
||||
|
||||
yield GenerationChunk(text=chunk_obj[output_key])
|
||||
# chunk obj format varies with provider
|
||||
yield GenerationChunk(
|
||||
text=chunk_obj[output_key],
|
||||
generation_info={
|
||||
GUARDRAILS_BODY_KEY: chunk_obj.get(GUARDRAILS_BODY_KEY)
|
||||
if GUARDRAILS_BODY_KEY in chunk_obj
|
||||
else None,
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def aprepare_output_stream(
|
||||
@@ -235,6 +249,53 @@ class BedrockBase(BaseModel, ABC):
|
||||
"cohere": "stop_sequences",
|
||||
}
|
||||
|
||||
guardrails: Optional[Mapping[str, Any]] = {
|
||||
"id": None,
|
||||
"version": None,
|
||||
"trace": False,
|
||||
}
|
||||
"""
|
||||
An optional dictionary to configure guardrails for Bedrock.
|
||||
|
||||
This field 'guardrails' consists of two keys: 'id' and 'version',
|
||||
which should be strings, but are initialized to None. It's used to
|
||||
determine if specific guardrails are enabled and properly set.
|
||||
|
||||
Type:
|
||||
Optional[Mapping[str, str]]: A mapping with 'id' and 'version' keys.
|
||||
|
||||
Example:
|
||||
llm = Bedrock(model_id="<model_id>", client=<bedrock_client>,
|
||||
model_kwargs={},
|
||||
guardrails={
|
||||
"id": "<guardrail_id>",
|
||||
"version": "<guardrail_version>"})
|
||||
|
||||
To enable tracing for guardrails, set the 'trace' key to True and pass a callback handler to the
|
||||
'run_manager' parameter of the 'generate', '_call' methods.
|
||||
|
||||
Example:
|
||||
llm = Bedrock(model_id="<model_id>", client=<bedrock_client>,
|
||||
model_kwargs={},
|
||||
guardrails={
|
||||
"id": "<guardrail_id>",
|
||||
"version": "<guardrail_version>",
|
||||
"trace": True},
|
||||
callbacks=[BedrockAsyncCallbackHandler()])
|
||||
|
||||
[https://python.langchain.com/docs/modules/callbacks/] for more information on callback handlers.
|
||||
|
||||
class BedrockAsyncCallbackHandler(AsyncCallbackHandler):
|
||||
async def on_llm_error(
|
||||
self,
|
||||
error: BaseException,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
reason = kwargs.get("reason")
|
||||
if reason == "GUARDRAIL_INTERVENED":
|
||||
...Logic to handle guardrail intervention...
|
||||
""" # noqa: E501
|
||||
|
||||
@root_validator()
|
||||
def validate_environment(cls, values: Dict) -> Dict:
|
||||
"""Validate that AWS credentials to and python package exists in environment."""
|
||||
@@ -298,6 +359,47 @@ class BedrockBase(BaseModel, ABC):
|
||||
def _model_is_anthropic(self) -> bool:
|
||||
return self._get_provider() == "anthropic"
|
||||
|
||||
@property
|
||||
def _guardrails_enabled(self) -> bool:
|
||||
"""
|
||||
Determines if guardrails are enabled and correctly configured.
|
||||
Checks if 'guardrails' is a dictionary with non-empty 'id' and 'version' keys.
|
||||
Checks if 'guardrails.trace' is true.
|
||||
|
||||
Returns:
|
||||
bool: True if guardrails are correctly configured, False otherwise.
|
||||
Raises:
|
||||
TypeError: If 'guardrails' lacks 'id' or 'version' keys.
|
||||
"""
|
||||
try:
|
||||
return (
|
||||
isinstance(self.guardrails, dict)
|
||||
and bool(self.guardrails["id"])
|
||||
and bool(self.guardrails["version"])
|
||||
)
|
||||
|
||||
except KeyError as e:
|
||||
raise TypeError(
|
||||
"Guardrails must be a dictionary with 'id' and 'version' keys."
|
||||
) from e
|
||||
|
||||
def _get_guardrails_canonical(self) -> Dict[str, Any]:
|
||||
"""
|
||||
The canonical way to pass in guardrails to the bedrock service
|
||||
adheres to the following format:
|
||||
|
||||
"amazon-bedrock-guardrailDetails": {
|
||||
"guardrailId": "string",
|
||||
"guardrailVersion": "string"
|
||||
}
|
||||
"""
|
||||
return {
|
||||
"amazon-bedrock-guardrailDetails": {
|
||||
"guardrailId": self.guardrails.get("id"),
|
||||
"guardrailVersion": self.guardrails.get("version"),
|
||||
}
|
||||
}
|
||||
|
||||
def _prepare_input_and_invoke(
|
||||
self,
|
||||
prompt: str,
|
||||
@@ -309,29 +411,81 @@ class BedrockBase(BaseModel, ABC):
|
||||
|
||||
provider = self._get_provider()
|
||||
params = {**_model_kwargs, **kwargs}
|
||||
if self._guardrails_enabled:
|
||||
params.update(self._get_guardrails_canonical())
|
||||
input_body = LLMInputOutputAdapter.prepare_input(provider, prompt, params)
|
||||
body = json.dumps(input_body)
|
||||
accept = "application/json"
|
||||
contentType = "application/json"
|
||||
|
||||
request_options = {
|
||||
"body": body,
|
||||
"modelId": self.model_id,
|
||||
"accept": accept,
|
||||
"contentType": contentType,
|
||||
}
|
||||
|
||||
if self._guardrails_enabled:
|
||||
request_options["guardrail"] = "ENABLED"
|
||||
if self.guardrails.get("trace"):
|
||||
request_options["trace"] = "ENABLED"
|
||||
|
||||
try:
|
||||
response = self.client.invoke_model(
|
||||
body=body,
|
||||
modelId=self.model_id,
|
||||
accept=accept,
|
||||
contentType=contentType,
|
||||
)
|
||||
text = LLMInputOutputAdapter.prepare_output(provider, response)
|
||||
response = self.client.invoke_model(**request_options)
|
||||
|
||||
text, body = LLMInputOutputAdapter.prepare_output(
|
||||
provider, response
|
||||
).values()
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error raised by bedrock service: {e}").with_traceback(
|
||||
e.__traceback__
|
||||
)
|
||||
raise ValueError(f"Error raised by bedrock service: {e}")
|
||||
|
||||
if stop is not None:
|
||||
text = enforce_stop_tokens(text, stop)
|
||||
|
||||
# Verify and raise a callback error if any intervention occurs or a signal is
|
||||
# sent from a Bedrock service,
|
||||
# such as when guardrails are triggered.
|
||||
services_trace = self._get_bedrock_services_signal(body)
|
||||
|
||||
if services_trace.get("signal") and run_manager is not None:
|
||||
run_manager.on_llm_error(
|
||||
Exception(
|
||||
f"Error raised by bedrock service: {services_trace.get('reason')}"
|
||||
),
|
||||
**services_trace,
|
||||
)
|
||||
|
||||
return text
|
||||
|
||||
def _get_bedrock_services_signal(self, body: dict) -> dict:
|
||||
"""
|
||||
This function checks the response body for an interrupt flag or message that indicates
|
||||
whether any of the Bedrock services have intervened in the processing flow. It is
|
||||
primarily used to identify modifications or interruptions imposed by these services
|
||||
during the request-response cycle with a Large Language Model (LLM).
|
||||
""" # noqa: E501
|
||||
|
||||
if (
|
||||
self._guardrails_enabled
|
||||
and self.guardrails.get("trace")
|
||||
and self._is_guardrails_intervention(body)
|
||||
):
|
||||
return {
|
||||
"signal": True,
|
||||
"reason": "GUARDRAIL_INTERVENED",
|
||||
"trace": body.get(AMAZON_BEDROCK_TRACE_KEY),
|
||||
}
|
||||
|
||||
return {
|
||||
"signal": False,
|
||||
"reason": None,
|
||||
"trace": None,
|
||||
}
|
||||
|
||||
def _is_guardrails_intervention(self, body: dict) -> bool:
|
||||
return body.get(GUARDRAILS_BODY_KEY) == "GUARDRAIL_INTERVENED"
|
||||
|
||||
def _prepare_input_and_invoke_stream(
|
||||
self,
|
||||
prompt: str,
|
||||
@@ -356,16 +510,28 @@ class BedrockBase(BaseModel, ABC):
|
||||
_model_kwargs["stream"] = True
|
||||
|
||||
params = {**_model_kwargs, **kwargs}
|
||||
|
||||
if self._guardrails_enabled:
|
||||
params.update(self._get_guardrails_canonical())
|
||||
|
||||
input_body = LLMInputOutputAdapter.prepare_input(provider, prompt, params)
|
||||
body = json.dumps(input_body)
|
||||
|
||||
request_options = {
|
||||
"body": body,
|
||||
"modelId": self.model_id,
|
||||
"accept": "application/json",
|
||||
"contentType": "application/json",
|
||||
}
|
||||
|
||||
if self._guardrails_enabled:
|
||||
request_options["guardrail"] = "ENABLED"
|
||||
if self.guardrails.get("trace"):
|
||||
request_options["trace"] = "ENABLED"
|
||||
|
||||
try:
|
||||
response = self.client.invoke_model_with_response_stream(
|
||||
body=body,
|
||||
modelId=self.model_id,
|
||||
accept="application/json",
|
||||
contentType="application/json",
|
||||
)
|
||||
response = self.client.invoke_model_with_response_stream(**request_options)
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error raised by bedrock service: {e}")
|
||||
|
||||
@@ -373,6 +539,9 @@ class BedrockBase(BaseModel, ABC):
|
||||
provider, response, stop
|
||||
):
|
||||
yield chunk
|
||||
# verify and raise callback error if any middleware intervened
|
||||
self._get_bedrock_services_signal(chunk.generation_info)
|
||||
|
||||
if run_manager is not None:
|
||||
run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
|
||||
@@ -536,7 +705,9 @@ class Bedrock(LLM, BedrockBase):
|
||||
completion += chunk.text
|
||||
return completion
|
||||
|
||||
return self._prepare_input_and_invoke(prompt=prompt, stop=stop, **kwargs)
|
||||
return self._prepare_input_and_invoke(
|
||||
prompt=prompt, stop=stop, run_manager=run_manager, **kwargs
|
||||
)
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
|
||||
276
libs/community/langchain_community/llms/oci_generative_ai.py
Normal file
276
libs/community/langchain_community/llms/oci_generative_ai.py
Normal file
@@ -0,0 +1,276 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Mapping, Optional
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.language_models.llms import LLM
|
||||
from langchain_core.pydantic_v1 import BaseModel, Extra, root_validator
|
||||
|
||||
from langchain_community.llms.utils import enforce_stop_tokens
|
||||
|
||||
CUSTOM_ENDPOINT_PREFIX = "ocid1.generativeaiendpoint"
|
||||
VALID_PROVIDERS = ("cohere", "meta")
|
||||
|
||||
|
||||
class OCIAuthType(Enum):
|
||||
API_KEY = 1
|
||||
SECURITY_TOKEN = 2
|
||||
INSTANCE_PRINCIPAL = 3
|
||||
RESOURCE_PRINCIPAL = 4
|
||||
|
||||
|
||||
class OCIGenAIBase(BaseModel, ABC):
|
||||
"""Base class for OCI GenAI models"""
|
||||
|
||||
client: Any #: :meta private:
|
||||
|
||||
auth_type: Optional[str] = "API_KEY"
|
||||
"""Authentication type, could be
|
||||
|
||||
API_KEY,
|
||||
SECURITY_TOKEN,
|
||||
INSTANCE_PRINCIPLE,
|
||||
RESOURCE_PRINCIPLE
|
||||
|
||||
If not specified, API_KEY will be used
|
||||
"""
|
||||
|
||||
auth_profile: Optional[str] = "DEFAULT"
|
||||
"""The name of the profile in ~/.oci/config
|
||||
If not specified , DEFAULT will be used
|
||||
"""
|
||||
|
||||
model_id: str = None
|
||||
"""Id of the model to call, e.g., cohere.command"""
|
||||
|
||||
provider: str = None
|
||||
"""Provider name of the model. Default to None,
|
||||
will try to be derived from the model_id
|
||||
otherwise, requires user input
|
||||
"""
|
||||
|
||||
model_kwargs: Optional[Dict] = None
|
||||
"""Keyword arguments to pass to the model"""
|
||||
|
||||
service_endpoint: str = None
|
||||
"""service endpoint url"""
|
||||
|
||||
compartment_id: str = None
|
||||
"""OCID of compartment"""
|
||||
|
||||
is_stream: bool = False
|
||||
"""Whether to stream back partial progress"""
|
||||
|
||||
llm_stop_sequence_mapping: Mapping[str, str] = {
|
||||
"cohere": "stop_sequences",
|
||||
"meta": "stop",
|
||||
}
|
||||
|
||||
@root_validator()
|
||||
def validate_environment(cls, values: Dict) -> Dict:
|
||||
"""Validate that OCI config and python package exists in environment."""
|
||||
|
||||
# Skip creating new client if passed in constructor
|
||||
if values["client"] is not None:
|
||||
return values
|
||||
|
||||
try:
|
||||
import oci
|
||||
|
||||
client_kwargs = {
|
||||
"config": {},
|
||||
"signer": None,
|
||||
"service_endpoint": values["service_endpoint"],
|
||||
"retry_strategy": oci.retry.DEFAULT_RETRY_STRATEGY,
|
||||
"timeout": (10, 240), # default timeout config for OCI Gen AI service
|
||||
}
|
||||
|
||||
if values["auth_type"] == OCIAuthType(1).name:
|
||||
client_kwargs["config"] = oci.config.from_file(
|
||||
profile_name=values["auth_profile"]
|
||||
)
|
||||
client_kwargs.pop("signer", None)
|
||||
elif values["auth_type"] == OCIAuthType(2).name:
|
||||
|
||||
def make_security_token_signer(oci_config):
|
||||
pk = oci.signer.load_private_key_from_file(
|
||||
oci_config.get("key_file"), None
|
||||
)
|
||||
with open(
|
||||
oci_config.get("security_token_file"), encoding="utf-8"
|
||||
) as f:
|
||||
st_string = f.read()
|
||||
return oci.auth.signers.SecurityTokenSigner(st_string, pk)
|
||||
|
||||
client_kwargs["config"] = oci.config.from_file(
|
||||
profile_name=values["auth_profile"]
|
||||
)
|
||||
client_kwargs["signer"] = make_security_token_signer(
|
||||
oci_config=client_kwargs["config"]
|
||||
)
|
||||
elif values["auth_type"] == OCIAuthType(3).name:
|
||||
client_kwargs[
|
||||
"signer"
|
||||
] = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
|
||||
elif values["auth_type"] == OCIAuthType(4).name:
|
||||
client_kwargs[
|
||||
"signer"
|
||||
] = oci.auth.signers.get_resource_principals_signer()
|
||||
else:
|
||||
raise ValueError("Please provide valid value to auth_type")
|
||||
|
||||
values["client"] = oci.generative_ai_inference.GenerativeAiInferenceClient(
|
||||
**client_kwargs
|
||||
)
|
||||
|
||||
except ImportError as ex:
|
||||
raise ModuleNotFoundError(
|
||||
"Could not import oci python package. "
|
||||
"Please make sure you have the oci package installed."
|
||||
) from ex
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
"Could not authenticate with OCI client. "
|
||||
"Please check if ~/.oci/config exists. "
|
||||
"If INSTANCE_PRINCIPLE or RESOURCE_PRINCIPLE is used, "
|
||||
"Please check the specified "
|
||||
"auth_profile and auth_type are valid."
|
||||
) from e
|
||||
|
||||
return values
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Mapping[str, Any]:
|
||||
"""Get the identifying parameters."""
|
||||
_model_kwargs = self.model_kwargs or {}
|
||||
return {
|
||||
**{"model_kwargs": _model_kwargs},
|
||||
}
|
||||
|
||||
def _get_provider(self) -> str:
|
||||
if self.provider is not None:
|
||||
provider = self.provider
|
||||
else:
|
||||
provider = self.model_id.split(".")[0].lower()
|
||||
|
||||
if provider not in VALID_PROVIDERS:
|
||||
raise ValueError(
|
||||
f"Invalid provider derived from model_id: {self.model_id} "
|
||||
"Please explicitly pass in the supported provider "
|
||||
"when using custom endpoint"
|
||||
)
|
||||
return provider
|
||||
|
||||
|
||||
class OCIGenAI(LLM, OCIGenAIBase):
|
||||
"""OCI large language models.
|
||||
|
||||
To authenticate, the OCI client uses the methods described in
|
||||
https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdk_authentication_methods.htm
|
||||
|
||||
The authentifcation method is passed through auth_type and should be one of:
|
||||
API_KEY (default), SECURITY_TOKEN, INSTANCE_PRINCIPLE, RESOURCE_PRINCIPLE
|
||||
|
||||
Make sure you have the required policies (profile/roles) to
|
||||
access the OCI Generative AI service.
|
||||
If a specific config profile is used, you must pass
|
||||
the name of the profile (from ~/.oci/config) through auth_profile.
|
||||
|
||||
To use, you must provide the compartment id
|
||||
along with the endpoint url, and model id
|
||||
as named parameters to the constructor.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.llms import OCIGenAI
|
||||
|
||||
llm = OCIGenAI(
|
||||
model_id="MY_MODEL_ID",
|
||||
service_endpoint="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com",
|
||||
compartment_id="MY_OCID"
|
||||
)
|
||||
"""
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
extra = Extra.forbid
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
"""Return type of llm."""
|
||||
return "oci"
|
||||
|
||||
def _prepare_invocation_object(
|
||||
self, prompt: str, stop: Optional[List[str]], kwargs: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
from oci.generative_ai_inference import models
|
||||
|
||||
oci_llm_request_mapping = {
|
||||
"cohere": models.CohereLlmInferenceRequest,
|
||||
"meta": models.LlamaLlmInferenceRequest,
|
||||
}
|
||||
provider = self._get_provider()
|
||||
_model_kwargs = self.model_kwargs or {}
|
||||
if stop is not None:
|
||||
_model_kwargs[self.llm_stop_sequence_mapping[provider]] = stop
|
||||
|
||||
if self.model_id.startswith(CUSTOM_ENDPOINT_PREFIX):
|
||||
serving_mode = models.DedicatedServingMode(endpoint_id=self.model_id)
|
||||
else:
|
||||
serving_mode = models.OnDemandServingMode(model_id=self.model_id)
|
||||
|
||||
inference_params = {**_model_kwargs, **kwargs}
|
||||
inference_params["prompt"] = prompt
|
||||
inference_params["is_stream"] = self.is_stream
|
||||
|
||||
invocation_obj = models.GenerateTextDetails(
|
||||
compartment_id=self.compartment_id,
|
||||
serving_mode=serving_mode,
|
||||
inference_request=oci_llm_request_mapping[provider](**inference_params),
|
||||
)
|
||||
|
||||
return invocation_obj
|
||||
|
||||
def _process_response(self, response: Any, stop: Optional[List[str]]) -> str:
|
||||
provider = self._get_provider()
|
||||
if provider == "cohere":
|
||||
text = response.data.inference_response.generated_texts[0].text
|
||||
elif provider == "meta":
|
||||
text = response.data.inference_response.choices[0].text
|
||||
else:
|
||||
raise ValueError(f"Invalid provider: {provider}")
|
||||
|
||||
if stop is not None:
|
||||
text = enforce_stop_tokens(text, stop)
|
||||
|
||||
return text
|
||||
|
||||
def _call(
|
||||
self,
|
||||
prompt: str,
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
"""Call out to OCIGenAI generate endpoint.
|
||||
|
||||
Args:
|
||||
prompt: The prompt to pass into the model.
|
||||
stop: Optional list of stop words to use when generating.
|
||||
|
||||
Returns:
|
||||
The string generated by the model.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
response = llm.invoke("Tell me a joke.")
|
||||
"""
|
||||
|
||||
invocation_obj = self._prepare_invocation_object(prompt, stop, kwargs)
|
||||
response = self.client.generate_text(invocation_obj)
|
||||
return self._process_response(response, stop)
|
||||
@@ -64,6 +64,10 @@ class _OllamaCommon(BaseLanguageModel):
|
||||
It is recommended to set this value to the number of physical
|
||||
CPU cores your system has (as opposed to the logical number of cores)."""
|
||||
|
||||
num_predict: Optional[int] = None
|
||||
"""Maximum number of tokens to predict when generating text.
|
||||
(Default: 128, -1 = infinite generation, -2 = fill context)"""
|
||||
|
||||
repeat_last_n: Optional[int] = None
|
||||
"""Sets how far back for the model to look back to prevent
|
||||
repetition. (Default: 64, 0 = disabled, -1 = num_ctx)"""
|
||||
@@ -126,6 +130,7 @@ class _OllamaCommon(BaseLanguageModel):
|
||||
"num_ctx": self.num_ctx,
|
||||
"num_gpu": self.num_gpu,
|
||||
"num_thread": self.num_thread,
|
||||
"num_predict": self.num_predict,
|
||||
"repeat_last_n": self.repeat_last_n,
|
||||
"repeat_penalty": self.repeat_penalty,
|
||||
"temperature": self.temperature,
|
||||
@@ -279,7 +284,10 @@ class _OllamaCommon(BaseLanguageModel):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
url=api_url,
|
||||
headers={"Content-Type": "application/json"},
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
**(self.headers if isinstance(self.headers, dict) else {}),
|
||||
},
|
||||
json=request_payload,
|
||||
timeout=self.timeout,
|
||||
) as response:
|
||||
|
||||
@@ -770,6 +770,7 @@ class AzureOpenAI(BaseOpenAI):
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.llms import AzureOpenAI
|
||||
|
||||
openai = AzureOpenAI(model_name="gpt-3.5-turbo-instruct")
|
||||
"""
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ class _BaseYandexGPT(Serializable):
|
||||
"""The url of the API."""
|
||||
max_retries: int = 6
|
||||
"""Maximum number of retries to make when generating."""
|
||||
sleep_interval: float = 1.0
|
||||
"""Delay between API requests"""
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
@@ -195,7 +197,8 @@ def _make_request(
|
||||
)
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Please install YandexCloud SDK" " with `pip install yandexcloud`."
|
||||
"Please install YandexCloud SDK with `pip install yandexcloud` \
|
||||
or upgrade it to recent version."
|
||||
) from e
|
||||
channel_credentials = grpc.ssl_channel_credentials()
|
||||
channel = grpc.secure_channel(self.url, channel_credentials)
|
||||
@@ -235,7 +238,8 @@ async def _amake_request(self: YandexGPT, prompt: str) -> str:
|
||||
)
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Please install YandexCloud SDK" " with `pip install yandexcloud`."
|
||||
"Please install YandexCloud SDK with `pip install yandexcloud` \
|
||||
or upgrade it to recent version."
|
||||
) from e
|
||||
operation_api_url = "operation.api.cloud.yandex.net:443"
|
||||
channel_credentials = grpc.ssl_channel_credentials()
|
||||
@@ -269,7 +273,7 @@ async def _amake_request(self: YandexGPT, prompt: str) -> str:
|
||||
def _create_retry_decorator(llm: YandexGPT) -> Callable[[Any], Any]:
|
||||
from grpc import RpcError
|
||||
|
||||
min_seconds = 1
|
||||
min_seconds = llm.sleep_interval
|
||||
max_seconds = 60
|
||||
return retry(
|
||||
reraise=True,
|
||||
|
||||
@@ -1,38 +1,6 @@
|
||||
from langchain_core.tools import BaseTool
|
||||
|
||||
from langchain_community.utils.openai_functions import (
|
||||
FunctionDescription,
|
||||
ToolDescription,
|
||||
convert_pydantic_to_openai_function,
|
||||
from langchain_core.utils.function_calling import (
|
||||
format_tool_to_openai_function,
|
||||
format_tool_to_openai_tool,
|
||||
)
|
||||
|
||||
|
||||
def format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:
|
||||
"""Format tool into the OpenAI function API."""
|
||||
if tool.args_schema:
|
||||
return convert_pydantic_to_openai_function(
|
||||
tool.args_schema, name=tool.name, description=tool.description
|
||||
)
|
||||
else:
|
||||
return {
|
||||
"name": tool.name,
|
||||
"description": tool.description,
|
||||
"parameters": {
|
||||
# This is a hack to get around the fact that some tools
|
||||
# do not expose an args_schema, and expect an argument
|
||||
# which is a string.
|
||||
# And Open AI does not support an array type for the
|
||||
# parameters.
|
||||
"properties": {
|
||||
"__arg1": {"title": "__arg1", "type": "string"},
|
||||
},
|
||||
"required": ["__arg1"],
|
||||
"type": "object",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def format_tool_to_openai_tool(tool: BaseTool) -> ToolDescription:
|
||||
"""Format tool into the OpenAI function API."""
|
||||
function = format_tool_to_openai_function(tool)
|
||||
return {"type": "function", "function": function}
|
||||
__all__ = ["format_tool_to_openai_function", "format_tool_to_openai_tool"]
|
||||
|
||||
@@ -44,7 +44,7 @@ class DataForSeoAPISearchResults(BaseTool):
|
||||
"""Tool that queries the DataForSeo Google Search API
|
||||
and get back json."""
|
||||
|
||||
name: str = "DataForSeo-Results-JSON"
|
||||
name: str = "dataforseo_results_json"
|
||||
description: str = (
|
||||
"A comprehensive Google Search API provided by DataForSeo."
|
||||
"This tool is useful for obtaining real-time data on current events "
|
||||
|
||||
@@ -42,7 +42,7 @@ class DuckDuckGoSearchRun(BaseTool):
|
||||
class DuckDuckGoSearchResults(BaseTool):
|
||||
"""Tool that queries the DuckDuckGo search API and gets back json."""
|
||||
|
||||
name: str = "DuckDuckGo Results JSON"
|
||||
name: str = "duckduckgo_results_json"
|
||||
description: str = (
|
||||
"A wrapper around Duck Duck Go Search. "
|
||||
"Useful for when you need to answer questions about current events. "
|
||||
|
||||
@@ -11,7 +11,7 @@ from langchain_community.utilities.golden_query import GoldenQueryAPIWrapper
|
||||
class GoldenQueryRun(BaseTool):
|
||||
"""Tool that adds the capability to query using the Golden API and get back JSON."""
|
||||
|
||||
name: str = "Golden-Query"
|
||||
name: str = "golden_query"
|
||||
description: str = (
|
||||
"A wrapper around Golden Query API."
|
||||
" Useful for getting entities that match"
|
||||
|
||||
@@ -11,7 +11,7 @@ from langchain_community.utilities.google_lens import GoogleLensAPIWrapper
|
||||
class GoogleLensQueryRun(BaseTool):
|
||||
"""Tool that queries the Google Lens API."""
|
||||
|
||||
name: str = "google_Lens"
|
||||
name: str = "google_lens"
|
||||
description: str = (
|
||||
"A wrapper around Google Lens Search. "
|
||||
"Useful for when you need to get information related"
|
||||
|
||||
@@ -31,7 +31,7 @@ class GoogleSearchRun(BaseTool):
|
||||
class GoogleSearchResults(BaseTool):
|
||||
"""Tool that queries the Google Search API and gets back json."""
|
||||
|
||||
name: str = "Google Search Results JSON"
|
||||
name: str = "google_search_results_json"
|
||||
description: str = (
|
||||
"A wrapper around Google Search. "
|
||||
"Useful for when you need to answer questions about current events. "
|
||||
|
||||
@@ -35,7 +35,7 @@ class TrainableLLM(Protocol):
|
||||
class Memorize(BaseTool):
|
||||
"""Tool that trains a language model."""
|
||||
|
||||
name: str = "Memorize"
|
||||
name: str = "memorize"
|
||||
description: str = (
|
||||
"Useful whenever you observed novel information "
|
||||
"from previous conversation history, "
|
||||
|
||||
@@ -11,7 +11,7 @@ from langchain_community.utilities.merriam_webster import MerriamWebsterAPIWrapp
|
||||
class MerriamWebsterQueryRun(BaseTool):
|
||||
"""Tool that searches the Merriam-Webster API."""
|
||||
|
||||
name: str = "MerriamWebster"
|
||||
name: str = "merriam_webster"
|
||||
description: str = (
|
||||
"A wrapper around Merriam-Webster. "
|
||||
"Useful for when you need to get the definition of a word."
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
from langchain_core.callbacks import (
|
||||
AsyncCallbackManagerForToolRun,
|
||||
CallbackManagerForToolRun,
|
||||
@@ -11,6 +12,11 @@ from langchain_core.tools import BaseTool
|
||||
from langchain_community.utilities.metaphor_search import MetaphorSearchAPIWrapper
|
||||
|
||||
|
||||
@deprecated(
|
||||
since="0.0.15",
|
||||
removal="0.2.0",
|
||||
alternative="langchain_exa.ExaSearchResults",
|
||||
)
|
||||
class MetaphorSearchResults(BaseTool):
|
||||
"""Tool that queries the Metaphor Search API and gets back json."""
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class OpenWeatherMapQueryRun(BaseTool):
|
||||
default_factory=OpenWeatherMapAPIWrapper
|
||||
)
|
||||
|
||||
name: str = "OpenWeatherMap"
|
||||
name: str = "open_weather_map"
|
||||
description: str = (
|
||||
"A wrapper around OpenWeatherMap API. "
|
||||
"Useful for fetching current weather information for a specified location. "
|
||||
|
||||
@@ -10,7 +10,7 @@ from langchain_community.utilities.pubmed import PubMedAPIWrapper
|
||||
class PubmedQueryRun(BaseTool):
|
||||
"""Tool that searches the PubMed API."""
|
||||
|
||||
name: str = "PubMed"
|
||||
name: str = "pub_med"
|
||||
description: str = (
|
||||
"A wrapper around PubMed. "
|
||||
"Useful for when you need to answer questions about medicine, health, "
|
||||
|
||||
@@ -1,44 +1,6 @@
|
||||
"""Different methods for rendering Tools to be passed to LLMs.
|
||||
|
||||
Depending on the LLM you are using and the prompting strategy you are using,
|
||||
you may want Tools to be rendered in a different way.
|
||||
This module contains various ways to render tools.
|
||||
"""
|
||||
from langchain_core.tools import BaseTool
|
||||
|
||||
from langchain_community.utils.openai_functions import (
|
||||
FunctionDescription,
|
||||
ToolDescription,
|
||||
convert_pydantic_to_openai_function,
|
||||
from langchain_core.utils.function_calling import (
|
||||
format_tool_to_openai_function,
|
||||
format_tool_to_openai_tool,
|
||||
)
|
||||
|
||||
|
||||
def format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:
|
||||
"""Format tool into the OpenAI function API."""
|
||||
if tool.args_schema:
|
||||
return convert_pydantic_to_openai_function(
|
||||
tool.args_schema, name=tool.name, description=tool.description
|
||||
)
|
||||
else:
|
||||
return {
|
||||
"name": tool.name,
|
||||
"description": tool.description,
|
||||
"parameters": {
|
||||
# This is a hack to get around the fact that some tools
|
||||
# do not expose an args_schema, and expect an argument
|
||||
# which is a string.
|
||||
# And Open AI does not support an array type for the
|
||||
# parameters.
|
||||
"properties": {
|
||||
"__arg1": {"title": "__arg1", "type": "string"},
|
||||
},
|
||||
"required": ["__arg1"],
|
||||
"type": "object",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def format_tool_to_openai_tool(tool: BaseTool) -> ToolDescription:
|
||||
"""Format tool into the OpenAI function API."""
|
||||
function = format_tool_to_openai_function(tool)
|
||||
return {"type": "function", "function": function}
|
||||
__all__ = ["format_tool_to_openai_function", "format_tool_to_openai_tool"]
|
||||
|
||||
@@ -43,7 +43,7 @@ class SearxSearchRun(BaseTool):
|
||||
class SearxSearchResults(BaseTool):
|
||||
"""Tool that queries a Searx instance and gets back json."""
|
||||
|
||||
name: str = "Searx-Search-Results"
|
||||
name: str = "searx_search_results"
|
||||
description: str = (
|
||||
"A meta search engine."
|
||||
"Useful for when you need to answer questions about current events."
|
||||
|
||||
@@ -11,7 +11,7 @@ from langchain_community.utilities.stackexchange import StackExchangeAPIWrapper
|
||||
class StackExchangeTool(BaseTool):
|
||||
"""Tool that uses StackExchange"""
|
||||
|
||||
name: str = "StackExchange"
|
||||
name: str = "stack_exchange"
|
||||
description: str = (
|
||||
"A wrapper around StackExchange. "
|
||||
"Useful for when you need to answer specific programming questions"
|
||||
|
||||
@@ -12,7 +12,7 @@ class SteamWebAPIQueryRun(BaseTool):
|
||||
"""Tool that searches the Steam Web API."""
|
||||
|
||||
mode: str
|
||||
name: str = "Steam"
|
||||
name: str = "steam"
|
||||
description: str = (
|
||||
"A wrapper around Steam Web API."
|
||||
"Steam Tool is useful for fetching User profiles and stats, Game data and more!"
|
||||
|
||||
@@ -49,7 +49,7 @@ class SteamshipImageGenerationTool(BaseTool):
|
||||
steamship: Steamship
|
||||
return_urls: Optional[bool] = False
|
||||
|
||||
name: str = "GenerateImage"
|
||||
name: str = "generate_image"
|
||||
description: str = (
|
||||
"Useful for when you need to generate an image."
|
||||
"Input: A detailed text-2-image prompt describing an image"
|
||||
|
||||
@@ -11,7 +11,7 @@ from langchain_community.utilities.wikipedia import WikipediaAPIWrapper
|
||||
class WikipediaQueryRun(BaseTool):
|
||||
"""Tool that searches the Wikipedia API."""
|
||||
|
||||
name: str = "Wikipedia"
|
||||
name: str = "wikipedia"
|
||||
description: str = (
|
||||
"A wrapper around Wikipedia. "
|
||||
"Useful for when you need to answer general questions about "
|
||||
|
||||
@@ -204,6 +204,12 @@ def _import_faiss() -> Any:
|
||||
return FAISS
|
||||
|
||||
|
||||
def _import_hanavector() -> Any:
|
||||
from langchain_community.vectorstores.hanavector import HanaDB
|
||||
|
||||
return HanaDB
|
||||
|
||||
|
||||
def _import_hologres() -> Any:
|
||||
from langchain_community.vectorstores.hologres import Hologres
|
||||
|
||||
@@ -527,6 +533,8 @@ def __getattr__(name: str) -> Any:
|
||||
return _import_epsilla()
|
||||
elif name == "FAISS":
|
||||
return _import_faiss()
|
||||
elif name == "HanaDB":
|
||||
return _import_hanavector()
|
||||
elif name == "Hologres":
|
||||
return _import_hologres()
|
||||
elif name == "KDBAI":
|
||||
@@ -645,6 +653,7 @@ __all__ = [
|
||||
"ElasticsearchStore",
|
||||
"Epsilla",
|
||||
"FAISS",
|
||||
"HanaDB",
|
||||
"Hologres",
|
||||
"KDBAI",
|
||||
"LanceDB",
|
||||
|
||||
@@ -109,6 +109,12 @@ class Bagel(VectorStore):
|
||||
import bagel # noqa: F401
|
||||
except ImportError:
|
||||
raise ImportError("Please install bagel `pip install betabageldb`.")
|
||||
|
||||
if self._embedding_function and query_embeddings is None and query_texts:
|
||||
texts = list(query_texts)
|
||||
query_embeddings = self._embedding_function.embed_documents(texts)
|
||||
query_texts = None
|
||||
|
||||
return self._cluster.find(
|
||||
query_texts=query_texts,
|
||||
query_embeddings=query_embeddings,
|
||||
|
||||
575
libs/community/langchain_community/vectorstores/hanavector.py
Normal file
575
libs/community/langchain_community/vectorstores/hanavector.py
Normal file
@@ -0,0 +1,575 @@
|
||||
"""SAP HANA Cloud Vector Engine"""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import re
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
Type,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.runnables.config import run_in_executor
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
from langchain_community.vectorstores.utils import (
|
||||
DistanceStrategy,
|
||||
maximal_marginal_relevance,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from hdbcli import dbapi
|
||||
|
||||
HANA_DISTANCE_FUNCTION: dict = {
|
||||
DistanceStrategy.COSINE: ("COSINE_SIMILARITY", "DESC"),
|
||||
DistanceStrategy.EUCLIDEAN_DISTANCE: ("L2DISTANCE", "ASC"),
|
||||
}
|
||||
|
||||
default_distance_strategy = DistanceStrategy.COSINE
|
||||
default_table_name: str = "EMBEDDINGS"
|
||||
default_content_column: str = "VEC_TEXT"
|
||||
default_metadata_column: str = "VEC_META"
|
||||
default_vector_column: str = "VEC_VECTOR"
|
||||
default_vector_column_length: int = -1 # -1 means dynamic length
|
||||
|
||||
|
||||
class HanaDB(VectorStore):
|
||||
"""SAP HANA Cloud Vector Engine
|
||||
|
||||
The prerequisite for using this class is the installation of the ``hdbcli``
|
||||
Python package.
|
||||
|
||||
The HanaDB vectorstore can be created by providing an embedding function and
|
||||
an existing database connection. Optionally, the names of the table and the
|
||||
columns to use.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
connection: dbapi.Connection,
|
||||
embedding: Embeddings,
|
||||
distance_strategy: DistanceStrategy = default_distance_strategy,
|
||||
table_name: str = default_table_name,
|
||||
content_column: str = default_content_column,
|
||||
metadata_column: str = default_metadata_column,
|
||||
vector_column: str = default_vector_column,
|
||||
vector_column_length: int = default_vector_column_length,
|
||||
):
|
||||
# Check if the hdbcli package is installed
|
||||
if importlib.util.find_spec("hdbcli") is None:
|
||||
raise ImportError(
|
||||
"Could not import hdbcli python package. "
|
||||
"Please install it with `pip install hdbcli`."
|
||||
)
|
||||
|
||||
valid_distance = False
|
||||
for key in HANA_DISTANCE_FUNCTION.keys():
|
||||
if key is distance_strategy:
|
||||
valid_distance = True
|
||||
if not valid_distance:
|
||||
raise ValueError(
|
||||
"Unsupported distance_strategy: {}".format(distance_strategy)
|
||||
)
|
||||
|
||||
self.connection = connection
|
||||
self.embedding = embedding
|
||||
self.distance_strategy = distance_strategy
|
||||
self.table_name = HanaDB._sanitize_name(table_name)
|
||||
self.content_column = HanaDB._sanitize_name(content_column)
|
||||
self.metadata_column = HanaDB._sanitize_name(metadata_column)
|
||||
self.vector_column = HanaDB._sanitize_name(vector_column)
|
||||
self.vector_column_length = HanaDB._sanitize_int(vector_column_length)
|
||||
|
||||
# Check if the table exists, and eventually create it
|
||||
if not self._table_exists(self.table_name):
|
||||
sql_str = (
|
||||
f"CREATE TABLE {self.table_name}("
|
||||
f"{self.content_column} NCLOB, "
|
||||
f"{self.metadata_column} NCLOB, "
|
||||
f"{self.vector_column} REAL_VECTOR "
|
||||
)
|
||||
if self.vector_column_length == -1:
|
||||
sql_str += ");"
|
||||
else:
|
||||
sql_str += f"({self.vector_column_length}));"
|
||||
|
||||
try:
|
||||
cur = self.connection.cursor()
|
||||
cur.execute(sql_str)
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
# Check if the needed columns exist and have the correct type
|
||||
self._check_column(self.table_name, self.content_column, ["NCLOB", "NVARCHAR"])
|
||||
self._check_column(self.table_name, self.metadata_column, ["NCLOB", "NVARCHAR"])
|
||||
self._check_column(
|
||||
self.table_name,
|
||||
self.vector_column,
|
||||
["REAL_VECTOR"],
|
||||
self.vector_column_length,
|
||||
)
|
||||
|
||||
def _table_exists(self, table_name) -> bool:
|
||||
sql_str = (
|
||||
"SELECT COUNT(*) FROM SYS.TABLES WHERE SCHEMA_NAME = CURRENT_SCHEMA"
|
||||
" AND TABLE_NAME = ?"
|
||||
)
|
||||
try:
|
||||
cur = self.connection.cursor()
|
||||
cur.execute(sql_str, (table_name))
|
||||
if cur.has_result_set():
|
||||
rows = cur.fetchall()
|
||||
if rows[0][0] == 1:
|
||||
return True
|
||||
finally:
|
||||
cur.close()
|
||||
return False
|
||||
|
||||
def _check_column(self, table_name, column_name, column_type, column_length=None):
|
||||
sql_str = (
|
||||
"SELECT DATA_TYPE_NAME, LENGTH FROM SYS.TABLE_COLUMNS WHERE "
|
||||
"SCHEMA_NAME = CURRENT_SCHEMA "
|
||||
"AND TABLE_NAME = ? AND COLUMN_NAME = ?"
|
||||
)
|
||||
try:
|
||||
cur = self.connection.cursor()
|
||||
cur.execute(sql_str, (table_name, column_name))
|
||||
if cur.has_result_set():
|
||||
rows = cur.fetchall()
|
||||
if len(rows) == 0:
|
||||
raise AttributeError(f"Column {column_name} does not exist")
|
||||
# Check data type
|
||||
if rows[0][0] not in column_type:
|
||||
raise AttributeError(
|
||||
f"Column {column_name} has the wrong type: {rows[0][0]}"
|
||||
)
|
||||
# Check length, if parameter was provided
|
||||
if column_length is not None:
|
||||
if rows[0][1] != column_length:
|
||||
raise AttributeError(
|
||||
f"Column {column_name} has the wrong length: {rows[0][1]}"
|
||||
)
|
||||
else:
|
||||
raise AttributeError(f"Column {column_name} does not exist")
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
@property
|
||||
def embeddings(self) -> Embeddings:
|
||||
return self.embedding
|
||||
|
||||
def _sanitize_name(input_str: str) -> str:
|
||||
# Remove characters that are not alphanumeric or underscores
|
||||
return re.sub(r"[^a-zA-Z0-9_]", "", input_str)
|
||||
|
||||
def _sanitize_int(input_int: any) -> int:
|
||||
value = int(str(input_int))
|
||||
if value < -1:
|
||||
raise ValueError(f"Value ({value}) must not be smaller than -1")
|
||||
return int(str(input_int))
|
||||
|
||||
def _sanitize_list_float(embedding: List[float]) -> List[float]:
|
||||
for value in embedding:
|
||||
if not isinstance(value, float):
|
||||
raise ValueError(f"Value ({value}) does not have type float")
|
||||
return embedding
|
||||
|
||||
# Compile pattern only once, for better performance
|
||||
_compiled_pattern = re.compile("^[_a-zA-Z][_a-zA-Z0-9]*$")
|
||||
|
||||
def _sanitize_metadata_keys(metadata: dict) -> dict:
|
||||
for key in metadata.keys():
|
||||
if not HanaDB._compiled_pattern.match(key):
|
||||
raise ValueError(f"Invalid metadata key {key}")
|
||||
|
||||
return metadata
|
||||
|
||||
def add_texts(
|
||||
self,
|
||||
texts: Iterable[str],
|
||||
metadatas: Optional[List[dict]] = None,
|
||||
embeddings: Optional[List[List[float]]] = None,
|
||||
) -> List[str]:
|
||||
"""Add more texts to the vectorstore.
|
||||
|
||||
Args:
|
||||
texts (Iterable[str]): Iterable of strings/text to add to the vectorstore.
|
||||
metadatas (Optional[List[dict]], optional): Optional list of metadatas.
|
||||
Defaults to None.
|
||||
embeddings (Optional[List[List[float]]], optional): Optional pre-generated
|
||||
embeddings. Defaults to None.
|
||||
|
||||
Returns:
|
||||
List[str]: empty list
|
||||
"""
|
||||
# Create all embeddings of the texts beforehand to improve performance
|
||||
if embeddings is None:
|
||||
embeddings = self.embedding.embed_documents(list(texts))
|
||||
|
||||
cur = self.connection.cursor()
|
||||
try:
|
||||
# Insert data into the table
|
||||
for i, text in enumerate(texts):
|
||||
# Use provided values by default or fallback
|
||||
metadata = metadatas[i] if metadatas else {}
|
||||
embedding = (
|
||||
embeddings[i]
|
||||
if embeddings
|
||||
else self.embedding.embed_documents([text])[0]
|
||||
)
|
||||
sql_str = (
|
||||
f"INSERT INTO {self.table_name} ({self.content_column}, "
|
||||
f"{self.metadata_column}, {self.vector_column}) "
|
||||
f"VALUES (?, ?, TO_REAL_VECTOR (?));"
|
||||
)
|
||||
cur.execute(
|
||||
sql_str,
|
||||
(
|
||||
text,
|
||||
json.dumps(HanaDB._sanitize_metadata_keys(metadata)),
|
||||
f"[{','.join(map(str, embedding))}]",
|
||||
),
|
||||
)
|
||||
finally:
|
||||
cur.close()
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def from_texts(
|
||||
cls: Type[HanaDB],
|
||||
texts: List[str],
|
||||
embedding: Embeddings,
|
||||
metadatas: Optional[List[dict]] = None,
|
||||
connection: dbapi.Connection = None,
|
||||
distance_strategy: DistanceStrategy = default_distance_strategy,
|
||||
table_name: str = default_table_name,
|
||||
content_column: str = default_content_column,
|
||||
metadata_column: str = default_metadata_column,
|
||||
vector_column: str = default_vector_column,
|
||||
vector_column_length: int = default_vector_column_length,
|
||||
):
|
||||
"""Create a HanaDB instance from raw documents.
|
||||
This is a user-friendly interface that:
|
||||
1. Embeds documents.
|
||||
2. Creates a table if it does not yet exist.
|
||||
3. Adds the documents to the table.
|
||||
This is intended to be a quick way to get started.
|
||||
"""
|
||||
|
||||
instance = cls(
|
||||
connection=connection,
|
||||
embedding=embedding,
|
||||
distance_strategy=distance_strategy,
|
||||
table_name=table_name,
|
||||
content_column=content_column,
|
||||
metadata_column=metadata_column,
|
||||
vector_column=vector_column,
|
||||
vector_column_length=vector_column_length, # -1 means dynamic length
|
||||
)
|
||||
instance.add_texts(texts, metadatas)
|
||||
return instance
|
||||
|
||||
def similarity_search(
|
||||
self, query: str, k: int = 4, filter: Optional[dict] = None
|
||||
) -> List[Document]:
|
||||
"""Return docs most similar to query.
|
||||
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filter: A dictionary of metadata fields and values to filter by.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
List of Documents most similar to the query
|
||||
"""
|
||||
docs_and_scores = self.similarity_search_with_score(
|
||||
query=query, k=k, filter=filter
|
||||
)
|
||||
return [doc for doc, _ in docs_and_scores]
|
||||
|
||||
def similarity_search_with_score(
|
||||
self, query: str, k: int = 4, filter: Optional[dict] = None
|
||||
) -> List[Tuple[Document, float]]:
|
||||
"""Return documents and score values most similar to query.
|
||||
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filter: A dictionary of metadata fields and values to filter by.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
List of tuples (containing a Document and a score) that are
|
||||
most similar to the query
|
||||
"""
|
||||
embedding = self.embedding.embed_query(query)
|
||||
return self.similarity_search_with_score_by_vector(
|
||||
embedding=embedding, k=k, filter=filter
|
||||
)
|
||||
|
||||
def similarity_search_with_score_and_vector_by_vector(
|
||||
self, embedding: List[float], k: int = 4, filter: Optional[dict] = None
|
||||
) -> List[Tuple[Document, float, List[float]]]:
|
||||
"""Return docs most similar to the given embedding.
|
||||
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filter: A dictionary of metadata fields and values to filter by.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
List of Documents most similar to the query and
|
||||
score and the document's embedding vector for each
|
||||
"""
|
||||
result = []
|
||||
k = HanaDB._sanitize_int(k)
|
||||
embedding = HanaDB._sanitize_list_float(embedding)
|
||||
distance_func_name = HANA_DISTANCE_FUNCTION[self.distance_strategy][0]
|
||||
embedding_as_str = ",".join(map(str, embedding))
|
||||
sql_str = (
|
||||
f"SELECT TOP {k}"
|
||||
f" {self.content_column}, " # row[0]
|
||||
f" {self.metadata_column}, " # row[1]
|
||||
f" TO_NVARCHAR({self.vector_column}), " # row[2]
|
||||
f" {distance_func_name}({self.vector_column}, TO_REAL_VECTOR "
|
||||
f" (ARRAY({embedding_as_str}))) AS CS " # row[3]
|
||||
f"FROM {self.table_name}"
|
||||
)
|
||||
order_str = f" order by CS {HANA_DISTANCE_FUNCTION[self.distance_strategy][1]}"
|
||||
where_str, query_tuple = self._create_where_by_filter(filter)
|
||||
sql_str = sql_str + where_str
|
||||
sql_str = sql_str + order_str
|
||||
try:
|
||||
cur = self.connection.cursor()
|
||||
cur.execute(sql_str, query_tuple)
|
||||
if cur.has_result_set():
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
js = json.loads(row[1])
|
||||
doc = Document(page_content=row[0], metadata=js)
|
||||
result_vector = HanaDB._parse_float_array_from_string(row[2])
|
||||
result.append((doc, row[3], result_vector))
|
||||
finally:
|
||||
cur.close()
|
||||
return result
|
||||
|
||||
def similarity_search_with_score_by_vector(
|
||||
self, embedding: List[float], k: int = 4, filter: Optional[dict] = None
|
||||
) -> List[Tuple[Document, float]]:
|
||||
"""Return docs most similar to the given embedding.
|
||||
|
||||
Args:
|
||||
query: Text to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filter: A dictionary of metadata fields and values to filter by.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
List of Documents most similar to the query and score for each
|
||||
"""
|
||||
whole_result = self.similarity_search_with_score_and_vector_by_vector(
|
||||
embedding=embedding, k=k, filter=filter
|
||||
)
|
||||
return [(result_item[0], result_item[1]) for result_item in whole_result]
|
||||
|
||||
def similarity_search_by_vector(
|
||||
self, embedding: List[float], k: int = 4, filter: Optional[dict] = None
|
||||
) -> List[Document]:
|
||||
"""Return docs most similar to embedding vector.
|
||||
|
||||
Args:
|
||||
embedding: Embedding to look up documents similar to.
|
||||
k: Number of Documents to return. Defaults to 4.
|
||||
filter: A dictionary of metadata fields and values to filter by.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
List of Documents most similar to the query vector.
|
||||
"""
|
||||
docs_and_scores = self.similarity_search_with_score_by_vector(
|
||||
embedding=embedding, k=k, filter=filter
|
||||
)
|
||||
return [doc for doc, _ in docs_and_scores]
|
||||
|
||||
def _create_where_by_filter(self, filter):
|
||||
query_tuple = []
|
||||
where_str = ""
|
||||
if filter:
|
||||
for i, key in enumerate(filter.keys()):
|
||||
if i == 0:
|
||||
where_str += " WHERE "
|
||||
else:
|
||||
where_str += " AND "
|
||||
|
||||
where_str += f" JSON_VALUE({self.metadata_column}, '$.{key}') = ?"
|
||||
|
||||
if isinstance(filter[key], bool):
|
||||
if filter[key]:
|
||||
query_tuple.append("true")
|
||||
else:
|
||||
query_tuple.append("false")
|
||||
elif isinstance(filter[key], int) or isinstance(filter[key], str):
|
||||
query_tuple.append(filter[key])
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unsupported filter data-type: {type(filter[key])}"
|
||||
)
|
||||
|
||||
return where_str, query_tuple
|
||||
|
||||
def delete(
|
||||
self, ids: Optional[List[str]] = None, filter: Optional[dict] = None
|
||||
) -> Optional[bool]:
|
||||
"""Delete entries by filter with metadata values
|
||||
|
||||
Args:
|
||||
ids: Deletion with ids is not supported! A ValueError will be raised.
|
||||
filter: A dictionary of metadata fields and values to filter by.
|
||||
An empty filter ({}) will delete all entries in the table.
|
||||
|
||||
Returns:
|
||||
Optional[bool]: True, if deletion is technically successful.
|
||||
Deletion of zero entries, due to non-matching filters is a success.
|
||||
"""
|
||||
|
||||
if ids is not None:
|
||||
raise ValueError("Deletion via ids is not supported")
|
||||
|
||||
if filter is None:
|
||||
raise ValueError("Parameter 'filter' is required when calling 'delete'")
|
||||
|
||||
where_str, query_tuple = self._create_where_by_filter(filter)
|
||||
sql_str = f"DELETE FROM {self.table_name} {where_str}"
|
||||
|
||||
try:
|
||||
cur = self.connection.cursor()
|
||||
cur.execute(sql_str, query_tuple)
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
return True
|
||||
|
||||
async def adelete(
|
||||
self, ids: Optional[List[str]] = None, filter: Optional[dict] = None
|
||||
) -> Optional[bool]:
|
||||
"""Delete by vector ID or other criteria.
|
||||
|
||||
Args:
|
||||
ids: List of ids to delete.
|
||||
|
||||
Returns:
|
||||
Optional[bool]: True if deletion is successful,
|
||||
False otherwise, None if not implemented.
|
||||
"""
|
||||
return await run_in_executor(None, self.delete, ids=ids, filter=filter)
|
||||
|
||||
def max_marginal_relevance_search(
|
||||
self,
|
||||
query: str,
|
||||
k: int = 4,
|
||||
fetch_k: int = 20,
|
||||
lambda_mult: float = 0.5,
|
||||
filter: Optional[dict] = None,
|
||||
) -> List[Document]:
|
||||
"""Return docs selected using the maximal marginal relevance.
|
||||
|
||||
Maximal marginal relevance optimizes for similarity to query AND diversity
|
||||
among selected documents.
|
||||
|
||||
Args:
|
||||
query: search query text.
|
||||
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
|
||||
of diversity among the results with 0 corresponding
|
||||
to maximum diversity and 1 to minimum diversity.
|
||||
Defaults to 0.5.
|
||||
filter: Filter on metadata properties, e.g.
|
||||
{
|
||||
"str_property": "foo",
|
||||
"int_property": 123
|
||||
}
|
||||
Returns:
|
||||
List of Documents selected by maximal marginal relevance.
|
||||
"""
|
||||
embedding = self.embedding.embed_query(query)
|
||||
return self.max_marginal_relevance_search_by_vector(
|
||||
embedding=embedding,
|
||||
k=k,
|
||||
fetch_k=fetch_k,
|
||||
lambda_mult=lambda_mult,
|
||||
filter=filter,
|
||||
)
|
||||
|
||||
def _parse_float_array_from_string(array_as_string: str) -> List[float]:
|
||||
array_wo_brackets = array_as_string[1:-1]
|
||||
return [float(x) for x in array_wo_brackets.split(",")]
|
||||
|
||||
def max_marginal_relevance_search_by_vector(
|
||||
self,
|
||||
embedding: List[float],
|
||||
k: int = 4,
|
||||
fetch_k: int = 20,
|
||||
lambda_mult: float = 0.5,
|
||||
filter: Optional[dict] = None,
|
||||
) -> List[Document]:
|
||||
whole_result = self.similarity_search_with_score_and_vector_by_vector(
|
||||
embedding=embedding, k=fetch_k, filter=filter
|
||||
)
|
||||
embeddings = [result_item[2] for result_item in whole_result]
|
||||
mmr_doc_indexes = maximal_marginal_relevance(
|
||||
np.array(embedding), embeddings, lambda_mult=lambda_mult, k=k
|
||||
)
|
||||
|
||||
return [whole_result[i][0] for i in mmr_doc_indexes]
|
||||
|
||||
async def amax_marginal_relevance_search_by_vector(
|
||||
self,
|
||||
embedding: List[float],
|
||||
k: int = 4,
|
||||
fetch_k: int = 20,
|
||||
lambda_mult: float = 0.5,
|
||||
) -> List[Document]:
|
||||
"""Return docs selected using the maximal marginal relevance."""
|
||||
return await run_in_executor(
|
||||
None,
|
||||
self.max_marginal_relevance_search_by_vector,
|
||||
embedding=embedding,
|
||||
k=k,
|
||||
fetch_k=fetch_k,
|
||||
lambda_mult=lambda_mult,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _cosine_relevance_score_fn(distance: float) -> float:
|
||||
return distance
|
||||
|
||||
def _select_relevance_score_fn(self) -> Callable[[float], float]:
|
||||
"""
|
||||
The 'correct' relevance function
|
||||
may differ depending on a few things, including:
|
||||
- the distance / similarity metric used by the VectorStore
|
||||
- the scale of your embeddings (OpenAI's are unit normed. Many others are not!)
|
||||
- embedding dimensionality
|
||||
- etc.
|
||||
|
||||
Vectorstores should define their own selection based method of relevance.
|
||||
"""
|
||||
if self.distance_strategy == DistanceStrategy.COSINE:
|
||||
return HanaDB._cosine_relevance_score_fn
|
||||
elif self.distance_strategy == DistanceStrategy.EUCLIDEAN_DISTANCE:
|
||||
return HanaDB._euclidean_relevance_score_fn
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unsupported distance_strategy: {}".format(self.distance_strategy)
|
||||
)
|
||||
63
libs/community/poetry.lock
generated
63
libs/community/poetry.lock
generated
@@ -1173,6 +1173,7 @@ files = [
|
||||
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"},
|
||||
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"},
|
||||
{file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"},
|
||||
{file = "contourpy-1.1.0-cp310-cp310-win32.whl", hash = "sha256:9b2dd2ca3ac561aceef4c7c13ba654aaa404cf885b187427760d7f7d4c57cff8"},
|
||||
{file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"},
|
||||
{file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"},
|
||||
{file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"},
|
||||
@@ -1181,6 +1182,7 @@ files = [
|
||||
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"},
|
||||
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"},
|
||||
{file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"},
|
||||
{file = "contourpy-1.1.0-cp311-cp311-win32.whl", hash = "sha256:edb989d31065b1acef3828a3688f88b2abb799a7db891c9e282df5ec7e46221b"},
|
||||
{file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"},
|
||||
{file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"},
|
||||
{file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"},
|
||||
@@ -1189,6 +1191,7 @@ files = [
|
||||
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"},
|
||||
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"},
|
||||
{file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"},
|
||||
{file = "contourpy-1.1.0-cp38-cp38-win32.whl", hash = "sha256:108dfb5b3e731046a96c60bdc46a1a0ebee0760418951abecbe0fc07b5b93b27"},
|
||||
{file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"},
|
||||
{file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"},
|
||||
{file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"},
|
||||
@@ -1197,6 +1200,7 @@ files = [
|
||||
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"},
|
||||
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"},
|
||||
{file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"},
|
||||
{file = "contourpy-1.1.0-cp39-cp39-win32.whl", hash = "sha256:71551f9520f008b2950bef5f16b0e3587506ef4f23c734b71ffb7b89f8721999"},
|
||||
{file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"},
|
||||
{file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"},
|
||||
{file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"},
|
||||
@@ -2957,6 +2961,29 @@ files = [
|
||||
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hdbcli"
|
||||
version = "2.19.21"
|
||||
description = "SAP HANA Python Client"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "hdbcli-2.19.21-cp27-cp27m-macosx_10_7_x86_64.whl", hash = "sha256:3028f04b86de2d9834a69f3fec2abb58201be3f1cbc357a63af18d4becaab1d3"},
|
||||
{file = "hdbcli-2.19.21-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f5e5ad76e77eff67ffad4f7db4a9cbe3e6b9c0399e39bd31ffeb4136d2192bc0"},
|
||||
{file = "hdbcli-2.19.21-cp27-cp27m-manylinux2014_ppc64le.whl", hash = "sha256:a8ceca28c6b80c5e6f8fc80a3517d7e843b9c3288f8b03c49316be68468d3848"},
|
||||
{file = "hdbcli-2.19.21-cp27-cp27m-win_amd64.whl", hash = "sha256:c963a8fa2f3405024051812048479bdd527d730351473f354d85e7fd933bf7ce"},
|
||||
{file = "hdbcli-2.19.21-cp27-cp27mu-macosx_10_7_x86_64.whl", hash = "sha256:98e72291fd5c226b22636274c3ccadb93ff2e3b54b98bff3f37e402ecfd73151"},
|
||||
{file = "hdbcli-2.19.21-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9773cc00cfd72ac7c2ad102560ca747bd5077437bed8bbb812071fa0ceb195a2"},
|
||||
{file = "hdbcli-2.19.21-cp27-cp27mu-manylinux2014_ppc64le.whl", hash = "sha256:ba5cf42ea026a1b1677c2c8bdbf2e6b77fbbabb7506671485740e675a6a5345a"},
|
||||
{file = "hdbcli-2.19.21-cp34-abi3-macosx_10_11_x86_64.whl", hash = "sha256:fac185d39a7a143a3c505c3e4260d0fc1b244589d4bea126e248e70e9e994e2b"},
|
||||
{file = "hdbcli-2.19.21-cp34-abi3-manylinux1_x86_64.whl", hash = "sha256:3c20763ba687acab151680c296c9daddbbbb7107a9790cf953da9bc527e373b9"},
|
||||
{file = "hdbcli-2.19.21-cp34-abi3-manylinux2014_ppc64le.whl", hash = "sha256:e20a3f60039875d03165c5790993952f5e2ec8efe141e051f7e154d96afc79a4"},
|
||||
{file = "hdbcli-2.19.21-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:7c7c50e89fe03be434460d407f2b74196eadde21db4046d52175a22b879ffa28"},
|
||||
{file = "hdbcli-2.19.21-cp36-abi3-win32.whl", hash = "sha256:d8529099b535b2c02ddb923ef8006132cf548e358f0bb0afdef3d4d81adc74d0"},
|
||||
{file = "hdbcli-2.19.21-cp36-abi3-win_amd64.whl", hash = "sha256:7c631a467f15cbb0d91655c2059b3c421e2fa0451ffeb500a3461aa4456e3fa2"},
|
||||
{file = "hdbcli-2.19.21-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:f8607479efef3dea5fc4181806a20ffe6552ef0212efc371c93a15bf2d50c3b4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hologres-vector"
|
||||
version = "0.0.6"
|
||||
@@ -3917,7 +3944,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.1.14"
|
||||
version = "0.1.16"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = ">=3.8.1,<4.0"
|
||||
@@ -4164,6 +4191,16 @@ files = [
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
|
||||
@@ -4962,13 +4999,13 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
|
||||
|
||||
[[package]]
|
||||
name = "oci"
|
||||
version = "2.118.0"
|
||||
version = "2.119.1"
|
||||
description = "Oracle Cloud Infrastructure Python SDK"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "oci-2.118.0-py3-none-any.whl", hash = "sha256:766170a9b4c93053ba3fe5ae63c0ab48fdd71b4d17709742a2b45249f0829872"},
|
||||
{file = "oci-2.118.0.tar.gz", hash = "sha256:1004726c4dad6c02f967b7bc4e733ff552451a2914cb542c380756c7d46bb938"},
|
||||
{file = "oci-2.119.1-py3-none-any.whl", hash = "sha256:64b6012f3c2b70cf7fb5f58a1a4b4458d8f4d41ea1b79a5d9f8ca4beb2dfa225"},
|
||||
{file = "oci-2.119.1.tar.gz", hash = "sha256:992df963382f378b93634826956677f3c13407ca1b828c4eaf1cfd18f19fae33"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -6723,6 +6760,7 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
@@ -6730,8 +6768,15 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{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_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"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
@@ -6748,6 +6793,7 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
@@ -6755,6 +6801,7 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
@@ -7726,7 +7773,9 @@ python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-win32.whl", hash = "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8"},
|
||||
{file = "SQLAlchemy-2.0.23-cp310-cp310-win_amd64.whl", hash = "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6"},
|
||||
@@ -7763,7 +7812,9 @@ files = [
|
||||
{file = "SQLAlchemy-2.0.23-cp38-cp38-win_amd64.whl", hash = "sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9585b646ffb048c0250acc7dad92536591ffe35dba624bb8fd9b471e25212a35"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cc1d21576f958c42d9aec68eba5c1a7d715e5fc07825a629015fe8e3b0657fb0"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-win32.whl", hash = "sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884"},
|
||||
{file = "SQLAlchemy-2.0.23-cp39-cp39-win_amd64.whl", hash = "sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b"},
|
||||
@@ -9175,9 +9226,9 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
||||
|
||||
[extras]
|
||||
cli = ["typer"]
|
||||
extended-testing = ["aiosqlite", "aleph-alpha-client", "anthropic", "arxiv", "assemblyai", "atlassian-python-api", "azure-ai-documentintelligence", "beautifulsoup4", "bibtexparser", "cassio", "chardet", "cohere", "dashvector", "databricks-vectorsearch", "datasets", "dgml-utils", "elasticsearch", "esprima", "faiss-cpu", "feedparser", "fireworks-ai", "geopandas", "gitpython", "google-cloud-documentai", "gql", "gradientai", "hologres-vector", "html2text", "javelin-sdk", "jinja2", "jq", "jsonschema", "lxml", "markdownify", "motor", "msal", "mwparserfromhell", "mwxml", "newspaper3k", "numexpr", "openai", "openapi-pydantic", "oracle-ads", "pandas", "pdfminer-six", "pgvector", "praw", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "rank-bm25", "rapidfuzz", "rapidocr-onnxruntime", "requests-toolbelt", "rspace_client", "scikit-learn", "sqlite-vss", "streamlit", "sympy", "telethon", "timescale-vector", "tqdm", "upstash-redis", "xata", "xmltodict", "zhipuai"]
|
||||
extended-testing = ["aiosqlite", "aleph-alpha-client", "anthropic", "arxiv", "assemblyai", "atlassian-python-api", "azure-ai-documentintelligence", "beautifulsoup4", "bibtexparser", "cassio", "chardet", "cohere", "dashvector", "databricks-vectorsearch", "datasets", "dgml-utils", "elasticsearch", "esprima", "faiss-cpu", "feedparser", "fireworks-ai", "geopandas", "gitpython", "google-cloud-documentai", "gql", "gradientai", "hdbcli", "hologres-vector", "html2text", "javelin-sdk", "jinja2", "jq", "jsonschema", "lxml", "markdownify", "motor", "msal", "mwparserfromhell", "mwxml", "newspaper3k", "numexpr", "oci", "openai", "openapi-pydantic", "oracle-ads", "pandas", "pdfminer-six", "pgvector", "praw", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "rank-bm25", "rapidfuzz", "rapidocr-onnxruntime", "requests-toolbelt", "rspace_client", "scikit-learn", "sqlite-vss", "streamlit", "sympy", "telethon", "timescale-vector", "tqdm", "upstash-redis", "xata", "xmltodict", "zhipuai"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.8.1,<4.0"
|
||||
content-hash = "73184aec5978e0de5b99029724164fa76394beb6359b59763ca488a258b0df4d"
|
||||
content-hash = "064816bab088c1f6ff9902cb998291581b66a6d7762f965ff805b4e0b9b2e7e9"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langchain-community"
|
||||
version = "0.0.15"
|
||||
version = "0.0.16"
|
||||
description = "Community contributed LangChain integrations."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
@@ -9,7 +9,7 @@ repository = "https://github.com/langchain-ai/langchain"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.8.1,<4.0"
|
||||
langchain-core = ">=0.1.14,<0.2"
|
||||
langchain-core = ">=0.1.16,<0.2"
|
||||
SQLAlchemy = ">=1.4,<3"
|
||||
requests = "^2"
|
||||
PyYAML = ">=5.3"
|
||||
@@ -88,6 +88,8 @@ azure-ai-documentintelligence = {version = "^1.0.0b1", optional = true}
|
||||
oracle-ads = {version = "^2.9.1", optional = true}
|
||||
zhipuai = {version = "^1.0.7", optional = true}
|
||||
elasticsearch = {version = "^8.12.0", optional = true}
|
||||
hdbcli = {version = "^2.19.21", optional = true}
|
||||
oci = {version = "^2.119.1", optional = true}
|
||||
|
||||
[tool.poetry.group.test]
|
||||
optional = true
|
||||
@@ -251,6 +253,8 @@ extended_testing = [
|
||||
"oracle-ads",
|
||||
"zhipuai",
|
||||
"elasticsearch",
|
||||
"hdbcli",
|
||||
"oci"
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Test EdenAI API wrapper."""
|
||||
from typing import List
|
||||
|
||||
import pytest
|
||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
||||
from langchain_core.outputs import ChatGeneration, LLMResult
|
||||
|
||||
from langchain_community.chat_models.edenai import (
|
||||
ChatEdenAI,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.scheduled
|
||||
def test_chat_edenai() -> None:
|
||||
"""Test ChatEdenAI wrapper."""
|
||||
chat = ChatEdenAI(
|
||||
provider="openai", model="gpt-3.5-turbo", temperature=0, max_tokens=1000
|
||||
)
|
||||
message = HumanMessage(content="Who are you ?")
|
||||
response = chat([message])
|
||||
assert isinstance(response, AIMessage)
|
||||
assert isinstance(response.content, str)
|
||||
|
||||
|
||||
@pytest.mark.scheduled
|
||||
def test_edenai_generate() -> None:
|
||||
"""Test generate method of edenai."""
|
||||
chat = ChatEdenAI(provider="google")
|
||||
chat_messages: List[List[BaseMessage]] = [
|
||||
[HumanMessage(content="What is the meaning of life?")]
|
||||
]
|
||||
messages_copy = [messages.copy() for messages in chat_messages]
|
||||
result: LLMResult = chat.generate(chat_messages)
|
||||
assert isinstance(result, LLMResult)
|
||||
for response in result.generations[0]:
|
||||
assert isinstance(response, ChatGeneration)
|
||||
assert isinstance(response.text, str)
|
||||
assert response.text == response.message.content
|
||||
assert chat_messages == messages_copy
|
||||
|
||||
|
||||
@pytest.mark.scheduled
|
||||
async def test_edenai_async_generate() -> None:
|
||||
"""Test async generation."""
|
||||
chat = ChatEdenAI(provider="google", max_tokens=50)
|
||||
message = HumanMessage(content="Hello")
|
||||
result: LLMResult = await chat.agenerate([[message], [message]])
|
||||
assert isinstance(result, LLMResult)
|
||||
for response in result.generations[0]:
|
||||
assert isinstance(response, ChatGeneration)
|
||||
assert isinstance(response.text, str)
|
||||
assert response.text == response.message.content
|
||||
|
||||
|
||||
@pytest.mark.scheduled
|
||||
def test_edenai_streaming() -> None:
|
||||
"""Test streaming EdenAI chat."""
|
||||
llm = ChatEdenAI(provider="openai", max_tokens=50)
|
||||
|
||||
for chunk in llm.stream("Generate a high fantasy story."):
|
||||
assert isinstance(chunk.content, str)
|
||||
|
||||
|
||||
@pytest.mark.scheduled
|
||||
async def test_edenai_astream() -> None:
|
||||
"""Test streaming from EdenAI."""
|
||||
llm = ChatEdenAI(provider="openai", max_tokens=50)
|
||||
|
||||
async for token in llm.astream("Generate a high fantasy story."):
|
||||
assert isinstance(token.content, str)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user