mirror of
https://github.com/hwchase17/langchain.git
synced 2025-04-29 20:35:43 +00:00
Compare commits
36 Commits
langchain-
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
bdb7c4a8b3 | ||
|
868f07f8f4 | ||
|
3072e4610a | ||
|
9ff5b5d282 | ||
|
7e926520d5 | ||
|
d614842d23 | ||
|
ff1602f0fd | ||
|
aee7988a94 | ||
|
a2863f8757 | ||
|
3fb0a55122 | ||
|
5fb8fd863a | ||
|
79a537d308 | ||
|
ba2518995d | ||
|
04a899ebe3 | ||
|
a82d987f09 | ||
|
a60fd06784 | ||
|
629b7a5a43 | ||
|
ab871a7b39 | ||
|
d30c56a8c1 | ||
|
09c1991e96 | ||
|
a7903280dd | ||
|
d0f0d1f966 | ||
|
403fae8eec | ||
|
d6b50ad3f6 | ||
|
10a9c24dae | ||
|
8fc7a723b9 | ||
|
f4863f82e2 | ||
|
ae4b6380d9 | ||
|
ffbc64c72a | ||
|
6b0b317cb5 | ||
|
21962e2201 | ||
|
1eb0bdadfa | ||
|
7ecdac5240 | ||
|
faef3e5d50 | ||
|
d4fc734250 | ||
|
4bc70766b5 |
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,8 +1,8 @@
|
|||||||
Thank you for contributing to LangChain!
|
Thank you for contributing to LangChain!
|
||||||
|
|
||||||
- [ ] **PR title**: "package: description"
|
- [ ] **PR title**: "package: description"
|
||||||
- Where "package" is whichever of langchain, community, core, etc. is being modified. Use "docs: ..." for purely docs changes, "infra: ..." for CI changes.
|
- Where "package" is whichever of langchain, core, etc. is being modified. Use "docs: ..." for purely docs changes, "infra: ..." for CI changes.
|
||||||
- Example: "community: add foobar LLM"
|
- Example: "core: add foobar LLM"
|
||||||
|
|
||||||
|
|
||||||
- [ ] **PR message**: ***Delete this entire checklist*** and replace with
|
- [ ] **PR message**: ***Delete this entire checklist*** and replace with
|
||||||
@ -24,6 +24,5 @@ Additional guidelines:
|
|||||||
- Please do not add dependencies to pyproject.toml files (even optional ones) unless they are required for unit tests.
|
- Please do not add dependencies to pyproject.toml files (even optional ones) unless they are required for unit tests.
|
||||||
- Most PRs should not touch more than one package.
|
- Most PRs should not touch more than one package.
|
||||||
- Changes should be backwards compatible.
|
- Changes should be backwards compatible.
|
||||||
- If you are adding something to community, do not re-import it in langchain.
|
|
||||||
|
|
||||||
If no one reviews your PR within a few days, please @-mention one of baskaryan, eyurtsev, ccurme, vbarda, hwchase17.
|
If no one reviews your PR within a few days, please @-mention one of baskaryan, eyurtsev, ccurme, vbarda, hwchase17.
|
||||||
|
15
.github/scripts/check_diff.py
vendored
15
.github/scripts/check_diff.py
vendored
@ -16,7 +16,6 @@ LANGCHAIN_DIRS = [
|
|||||||
"libs/core",
|
"libs/core",
|
||||||
"libs/text-splitters",
|
"libs/text-splitters",
|
||||||
"libs/langchain",
|
"libs/langchain",
|
||||||
"libs/community",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# when set to True, we are ignoring core dependents
|
# when set to True, we are ignoring core dependents
|
||||||
@ -38,8 +37,8 @@ IGNORED_PARTNERS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
PY_312_MAX_PACKAGES = [
|
PY_312_MAX_PACKAGES = [
|
||||||
"libs/partners/huggingface", # https://github.com/pytorch/pytorch/issues/130249
|
|
||||||
"libs/partners/voyageai",
|
"libs/partners/voyageai",
|
||||||
|
"libs/partners/chroma", # https://github.com/chroma-core/chroma/issues/4382
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -134,12 +133,6 @@ def _get_configs_for_single_dir(job: str, dir_: str) -> List[Dict[str, str]]:
|
|||||||
elif dir_ == "libs/langchain" and job == "extended-tests":
|
elif dir_ == "libs/langchain" and job == "extended-tests":
|
||||||
py_versions = ["3.9", "3.13"]
|
py_versions = ["3.9", "3.13"]
|
||||||
|
|
||||||
elif dir_ == "libs/community" and job == "extended-tests":
|
|
||||||
py_versions = ["3.9", "3.12"]
|
|
||||||
|
|
||||||
elif dir_ == "libs/community" and job == "compile-integration-tests":
|
|
||||||
# community integration deps are slow in 3.12
|
|
||||||
py_versions = ["3.9", "3.11"]
|
|
||||||
elif dir_ == ".":
|
elif dir_ == ".":
|
||||||
# unable to install with 3.13 because tokenizers doesn't support 3.13 yet
|
# unable to install with 3.13 because tokenizers doesn't support 3.13 yet
|
||||||
py_versions = ["3.9", "3.12"]
|
py_versions = ["3.9", "3.12"]
|
||||||
@ -184,11 +177,6 @@ def _get_pydantic_test_configs(
|
|||||||
else "0"
|
else "0"
|
||||||
)
|
)
|
||||||
|
|
||||||
custom_mins = {
|
|
||||||
# depends on pydantic-settings 2.4 which requires pydantic 2.7
|
|
||||||
"libs/community": 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
max_pydantic_minor = min(
|
max_pydantic_minor = min(
|
||||||
int(dir_max_pydantic_minor),
|
int(dir_max_pydantic_minor),
|
||||||
int(core_max_pydantic_minor),
|
int(core_max_pydantic_minor),
|
||||||
@ -196,7 +184,6 @@ def _get_pydantic_test_configs(
|
|||||||
min_pydantic_minor = max(
|
min_pydantic_minor = max(
|
||||||
int(dir_min_pydantic_minor),
|
int(dir_min_pydantic_minor),
|
||||||
int(core_min_pydantic_minor),
|
int(core_min_pydantic_minor),
|
||||||
custom_mins.get(dir_, 0),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
configs = [
|
configs = [
|
||||||
|
2
.github/scripts/get_min_versions.py
vendored
2
.github/scripts/get_min_versions.py
vendored
@ -22,7 +22,6 @@ import re
|
|||||||
|
|
||||||
MIN_VERSION_LIBS = [
|
MIN_VERSION_LIBS = [
|
||||||
"langchain-core",
|
"langchain-core",
|
||||||
"langchain-community",
|
|
||||||
"langchain",
|
"langchain",
|
||||||
"langchain-text-splitters",
|
"langchain-text-splitters",
|
||||||
"numpy",
|
"numpy",
|
||||||
@ -35,7 +34,6 @@ SKIP_IF_PULL_REQUEST = [
|
|||||||
"langchain-core",
|
"langchain-core",
|
||||||
"langchain-text-splitters",
|
"langchain-text-splitters",
|
||||||
"langchain",
|
"langchain",
|
||||||
"langchain-community",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
6
.github/scripts/prep_api_docs_build.py
vendored
6
.github/scripts/prep_api_docs_build.py
vendored
@ -20,6 +20,8 @@ def get_target_dir(package_name: str) -> Path:
|
|||||||
base_path = Path("langchain/libs")
|
base_path = Path("langchain/libs")
|
||||||
if package_name_short == "experimental":
|
if package_name_short == "experimental":
|
||||||
return base_path / "experimental"
|
return base_path / "experimental"
|
||||||
|
if package_name_short == "community":
|
||||||
|
return base_path / "community"
|
||||||
return base_path / "partners" / package_name_short
|
return base_path / "partners" / package_name_short
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +71,7 @@ def main():
|
|||||||
clean_target_directories([
|
clean_target_directories([
|
||||||
p
|
p
|
||||||
for p in package_yaml["packages"]
|
for p in package_yaml["packages"]
|
||||||
if p["repo"].startswith("langchain-ai/")
|
if (p["repo"].startswith("langchain-ai/") or p.get("include_in_api_ref"))
|
||||||
and p["repo"] != "langchain-ai/langchain"
|
and p["repo"] != "langchain-ai/langchain"
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ def main():
|
|||||||
p
|
p
|
||||||
for p in package_yaml["packages"]
|
for p in package_yaml["packages"]
|
||||||
if not p.get("disabled", False)
|
if not p.get("disabled", False)
|
||||||
and p["repo"].startswith("langchain-ai/")
|
and (p["repo"].startswith("langchain-ai/") or p.get("include_in_api_ref"))
|
||||||
and p["repo"] != "langchain-ai/langchain"
|
and p["repo"] != "langchain-ai/langchain"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
1
.github/workflows/.codespell-exclude
vendored
1
.github/workflows/.codespell-exclude
vendored
@ -1,4 +1,3 @@
|
|||||||
libs/community/langchain_community/llms/yuan2.py
|
|
||||||
"NotIn": "not in",
|
"NotIn": "not in",
|
||||||
- `/checkin`: Check-in
|
- `/checkin`: Check-in
|
||||||
docs/docs/integrations/providers/trulens.mdx
|
docs/docs/integrations/providers/trulens.mdx
|
||||||
|
5
.github/workflows/_integration_test.yml
vendored
5
.github/workflows/_integration_test.yml
vendored
@ -34,11 +34,6 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: uv sync --group test --group test_integration
|
run: uv sync --group test --group test_integration
|
||||||
|
|
||||||
- name: Install deps outside pyproject
|
|
||||||
if: ${{ startsWith(inputs.working-directory, 'libs/community/') }}
|
|
||||||
shell: bash
|
|
||||||
run: VIRTUAL_ENV=.venv uv pip install "boto3<2" "google-cloud-aiplatform<2"
|
|
||||||
|
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
|
2
.github/workflows/_test_doc_imports.yml
vendored
2
.github/workflows/_test_doc_imports.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install langchain editable
|
- name: Install langchain editable
|
||||||
run: |
|
run: |
|
||||||
VIRTUAL_ENV=.venv uv pip install langchain-experimental -e libs/core libs/langchain libs/community
|
VIRTUAL_ENV=.venv uv pip install langchain-experimental langchain-community -e libs/core libs/langchain
|
||||||
|
|
||||||
- name: Check doc imports
|
- name: Check doc imports
|
||||||
shell: bash
|
shell: bash
|
||||||
|
17
.github/workflows/api_doc_build.yml
vendored
17
.github/workflows/api_doc_build.yml
vendored
@ -26,7 +26,20 @@ jobs:
|
|||||||
id: get-unsorted-repos
|
id: get-unsorted-repos
|
||||||
uses: mikefarah/yq@master
|
uses: mikefarah/yq@master
|
||||||
with:
|
with:
|
||||||
cmd: yq '.packages[].repo' langchain/libs/packages.yml
|
cmd: |
|
||||||
|
yq '
|
||||||
|
.packages[]
|
||||||
|
| select(
|
||||||
|
(
|
||||||
|
(.repo | test("^langchain-ai/"))
|
||||||
|
and
|
||||||
|
(.repo != "langchain-ai/langchain")
|
||||||
|
)
|
||||||
|
or
|
||||||
|
(.include_in_api_ref // false)
|
||||||
|
)
|
||||||
|
| .repo
|
||||||
|
' langchain/libs/packages.yml
|
||||||
|
|
||||||
- name: Parse YAML and checkout repos
|
- name: Parse YAML and checkout repos
|
||||||
env:
|
env:
|
||||||
@ -38,11 +51,9 @@ jobs:
|
|||||||
|
|
||||||
# Checkout each unique repository that is in langchain-ai org
|
# Checkout each unique repository that is in langchain-ai org
|
||||||
for repo in $REPOS; do
|
for repo in $REPOS; do
|
||||||
if [[ "$repo" != "langchain-ai/langchain" && "$repo" == langchain-ai/* ]]; then
|
|
||||||
REPO_NAME=$(echo $repo | cut -d'/' -f2)
|
REPO_NAME=$(echo $repo | cut -d'/' -f2)
|
||||||
echo "Checking out $repo to $REPO_NAME"
|
echo "Checking out $repo to $REPO_NAME"
|
||||||
git clone --depth 1 https://github.com/$repo.git $REPO_NAME
|
git clone --depth 1 https://github.com/$repo.git $REPO_NAME
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Setup python ${{ env.PYTHON_VERSION }}
|
- name: Setup python ${{ env.PYTHON_VERSION }}
|
||||||
|
@ -7,12 +7,6 @@ repos:
|
|||||||
entry: make -C libs/core format
|
entry: make -C libs/core format
|
||||||
files: ^libs/core/
|
files: ^libs/core/
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
- id: community
|
|
||||||
name: format community
|
|
||||||
language: system
|
|
||||||
entry: make -C libs/community format
|
|
||||||
files: ^libs/community/
|
|
||||||
pass_filenames: false
|
|
||||||
- id: langchain
|
- id: langchain
|
||||||
name: format langchain
|
name: format langchain
|
||||||
language: system
|
language: system
|
||||||
|
@ -107,7 +107,7 @@ outputs will appear as part of the [AIMessage](/docs/concepts/messages/#aimessag
|
|||||||
response object. See for example:
|
response object. See for example:
|
||||||
|
|
||||||
- Generating [audio outputs](/docs/integrations/chat/openai/#audio-generation-preview) with OpenAI;
|
- Generating [audio outputs](/docs/integrations/chat/openai/#audio-generation-preview) with OpenAI;
|
||||||
- Generating [image outputs](/docs/integrations/chat/google_generative_ai/#image-generation) with Google Gemini.
|
- Generating [image outputs](/docs/integrations/chat/google_generative_ai/#multimodal-usage) with Google Gemini.
|
||||||
|
|
||||||
#### Tools
|
#### Tools
|
||||||
|
|
||||||
|
@ -13,23 +13,33 @@ Install `uv`: **[documentation on how to install it](https://docs.astral.sh/uv/g
|
|||||||
|
|
||||||
This repository contains multiple packages:
|
This repository contains multiple packages:
|
||||||
- `langchain-core`: Base interfaces for key abstractions as well as logic for combining them in chains (LangChain Expression Language).
|
- `langchain-core`: Base interfaces for key abstractions as well as logic for combining them in chains (LangChain Expression Language).
|
||||||
- `langchain-community`: Third-party integrations of various components.
|
|
||||||
- `langchain`: Chains, agents, and retrieval logic that makes up the cognitive architecture of your applications.
|
- `langchain`: Chains, agents, and retrieval logic that makes up the cognitive architecture of your applications.
|
||||||
- `langchain-experimental`: Components and chains that are experimental, either in the sense that the techniques are novel and still being tested, or they require giving the LLM more access than would be possible in most production systems.
|
|
||||||
- Partner integrations: Partner packages in `libs/partners` that are independently version controlled.
|
- Partner integrations: Partner packages in `libs/partners` that are independently version controlled.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
Some LangChain packages live outside the monorepo, see for example
|
||||||
|
[langchain-community](https://github.com/langchain-ai/langchain-community) for various
|
||||||
|
third-party integrations and
|
||||||
|
[langchain-experimental](https://github.com/langchain-ai/langchain-experimental) for
|
||||||
|
abstractions that are experimental (either in the sense that the techniques are novel
|
||||||
|
and still being tested, or they require giving the LLM more access than would be
|
||||||
|
possible in most production systems).
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
Each of these has its own development environment. Docs are run from the top-level makefile, but development
|
Each of these has its own development environment. Docs are run from the top-level makefile, but development
|
||||||
is split across separate test & release flows.
|
is split across separate test & release flows.
|
||||||
|
|
||||||
For this quickstart, start with langchain-community:
|
For this quickstart, start with `langchain`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd libs/community
|
cd libs/langchain
|
||||||
```
|
```
|
||||||
|
|
||||||
## Local Development Dependencies
|
## Local Development Dependencies
|
||||||
|
|
||||||
Install langchain-community development requirements (for running langchain, running examples, linting, formatting, tests, and coverage):
|
Install development requirements (for running langchain, running examples, linting, formatting, tests, and coverage):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv sync
|
uv sync
|
||||||
@ -62,22 +72,15 @@ make docker_tests
|
|||||||
|
|
||||||
There are also [integration tests and code-coverage](../testing.mdx) available.
|
There are also [integration tests and code-coverage](../testing.mdx) available.
|
||||||
|
|
||||||
### Only develop langchain_core or langchain_community
|
### Developing langchain_core
|
||||||
|
|
||||||
If you are only developing `langchain_core` or `langchain_community`, you can simply install the dependencies for the respective projects and run tests:
|
If you are only developing `langchain_core`, you can simply install the dependencies for the project and run tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd libs/core
|
cd libs/core
|
||||||
make test
|
make test
|
||||||
```
|
```
|
||||||
|
|
||||||
Or:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd libs/community
|
|
||||||
make test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Formatting and Linting
|
## Formatting and Linting
|
||||||
|
|
||||||
Run these locally before submitting a PR; the CI system will check also.
|
Run these locally before submitting a PR; the CI system will check also.
|
||||||
|
@ -1,35 +1,26 @@
|
|||||||
{
|
{
|
||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "raw",
|
"cell_type": "markdown",
|
||||||
"id": "afaf8039",
|
"id": "d982c99f",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"---\n",
|
"---\n",
|
||||||
"sidebar_label: Google AI\n",
|
"sidebar_label: Google Gemini\n",
|
||||||
"---"
|
"---"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "e49f1e0d",
|
"id": "56a6d990",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# ChatGoogleGenerativeAI\n",
|
"# ChatGoogleGenerativeAI\n",
|
||||||
"\n",
|
"\n",
|
||||||
"This docs will help you get started with Google AI [chat models](/docs/concepts/chat_models). For detailed documentation of all ChatGoogleGenerativeAI features and configurations head to the [API reference](https://python.langchain.com/api_reference/google_genai/chat_models/langchain_google_genai.chat_models.ChatGoogleGenerativeAI.html).\n",
|
"Access Google's Generative AI models, including the Gemini family, directly via the Gemini API or experiment rapidly using Google AI Studio. The `langchain-google-genai` package provides the LangChain integration for these models. This is often the best starting point for individual developers.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Google AI offers a number of different chat models. For information on the latest models, their features, context windows, etc. head to the [Google AI docs](https://ai.google.dev/gemini-api/docs/models/gemini).\n",
|
"For information on the latest models, their features, context windows, etc. head to the [Google AI docs](https://ai.google.dev/gemini-api/docs/models/gemini). All examples use the `gemini-2.0-flash` model. Gemini 2.5 Pro and 2.5 Flash can be used via `gemini-2.5-pro-preview-03-25` and `gemini-2.5-flash-preview-04-17`. All model ids can be found in the [Gemini API docs](https://ai.google.dev/gemini-api/docs/models).\n",
|
||||||
"\n",
|
"\n",
|
||||||
":::info Google AI vs Google Cloud Vertex AI\n",
|
|
||||||
"\n",
|
|
||||||
"Google's Gemini models are accessible through Google AI and through Google Cloud Vertex AI. Using Google AI just requires a Google account and an API key. Using Google Cloud Vertex AI requires a Google Cloud account (with term agreements and billing) but offers enterprise features like customer encryption key, virtual private cloud, and more.\n",
|
|
||||||
"\n",
|
|
||||||
"To learn more about the key features of the two APIs see the [Google docs](https://cloud.google.com/vertex-ai/generative-ai/docs/migrate/migrate-google-ai#google-ai).\n",
|
|
||||||
"\n",
|
|
||||||
":::\n",
|
|
||||||
"\n",
|
|
||||||
"## Overview\n",
|
|
||||||
"### Integration details\n",
|
"### Integration details\n",
|
||||||
"\n",
|
"\n",
|
||||||
"| Class | Package | Local | Serializable | [JS support](https://js.langchain.com/docs/integrations/chat/google_generativeai) | Package downloads | Package latest |\n",
|
"| Class | Package | Local | Serializable | [JS support](https://js.langchain.com/docs/integrations/chat/google_generativeai) | Package downloads | Package latest |\n",
|
||||||
@ -37,23 +28,46 @@
|
|||||||
"| [ChatGoogleGenerativeAI](https://python.langchain.com/api_reference/google_genai/chat_models/langchain_google_genai.chat_models.ChatGoogleGenerativeAI.html) | [langchain-google-genai](https://python.langchain.com/api_reference/google_genai/index.html) | ❌ | beta | ✅ |  |  |\n",
|
"| [ChatGoogleGenerativeAI](https://python.langchain.com/api_reference/google_genai/chat_models/langchain_google_genai.chat_models.ChatGoogleGenerativeAI.html) | [langchain-google-genai](https://python.langchain.com/api_reference/google_genai/index.html) | ❌ | beta | ✅ |  |  |\n",
|
||||||
"\n",
|
"\n",
|
||||||
"### Model features\n",
|
"### Model features\n",
|
||||||
|
"\n",
|
||||||
"| [Tool calling](/docs/how_to/tool_calling) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
"| [Tool calling](/docs/how_to/tool_calling) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
||||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||||
"| ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |\n",
|
"| ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |\n",
|
||||||
"\n",
|
"\n",
|
||||||
"## Setup\n",
|
"### Setup\n",
|
||||||
"\n",
|
"\n",
|
||||||
"To access Google AI models you'll need to create a Google Acount account, get a Google AI API key, and install the `langchain-google-genai` integration package.\n",
|
"To access Google AI models you'll need to create a Google Account, get a Google AI API key, and install the `langchain-google-genai` integration package.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"### Credentials\n",
|
"**1. Installation:**"
|
||||||
"\n",
|
|
||||||
"Head to https://ai.google.dev/gemini-api/docs/api-key to generate a Google AI API key. Once you've done this set the GOOGLE_API_KEY environment variable:"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"id": "433e8d2b-9519-4b49-b2c4-7ab65b046c94",
|
"id": "8d12ce35",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%pip install -U langchain-google-genai"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "60be0b38",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**2. Credentials:**\n",
|
||||||
|
"\n",
|
||||||
|
"Head to [https://ai.google.dev/gemini-api/docs/api-key](https://ai.google.dev/gemini-api/docs/api-key) (or via Google AI Studio) to generate a Google AI API key.\n",
|
||||||
|
"\n",
|
||||||
|
"### Chat Models\n",
|
||||||
|
"\n",
|
||||||
|
"Use the `ChatGoogleGenerativeAI` class to interact with Google's chat models. See the [API reference](https://python.langchain.com/api_reference/google_genai/chat_models/langchain_google_genai.chat_models.ChatGoogleGenerativeAI.html) for full details.\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"id": "fb18c875",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -66,7 +80,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "72ee0c4b-9764-423a-9dbf-95129e185210",
|
"id": "f050e8db",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"To enable automated tracing of your model calls, set your [LangSmith](https://docs.smith.langchain.com/) API key:"
|
"To enable automated tracing of your model calls, set your [LangSmith](https://docs.smith.langchain.com/) API key:"
|
||||||
@ -75,7 +89,7 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"id": "a15d341e-3e26-4ca3-830b-5aab30ed66de",
|
"id": "82cb346f",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -85,27 +99,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "0730d6a1-c893-4840-9817-5e5251676d5d",
|
"id": "273cefa0",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"### Installation\n",
|
|
||||||
"\n",
|
|
||||||
"The LangChain Google AI integration lives in the `langchain-google-genai` package:"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"id": "652d6238-1f87-422a-b135-f5abbb8652fc",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"%pip install -qU langchain-google-genai"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "a38cde65-254d-4219-a441-068766c0d4b5",
|
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Instantiation\n",
|
"## Instantiation\n",
|
||||||
@ -115,15 +109,15 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 4,
|
||||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
"id": "7d3dc0b3",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
"\n",
|
"\n",
|
||||||
"llm = ChatGoogleGenerativeAI(\n",
|
"llm = ChatGoogleGenerativeAI(\n",
|
||||||
" model=\"gemini-2.0-flash-001\",\n",
|
" model=\"gemini-2.0-flash\",\n",
|
||||||
" temperature=0,\n",
|
" temperature=0,\n",
|
||||||
" max_tokens=None,\n",
|
" max_tokens=None,\n",
|
||||||
" timeout=None,\n",
|
" timeout=None,\n",
|
||||||
@ -134,7 +128,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "2b4f3e15",
|
"id": "343a8c13",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Invocation"
|
"## Invocation"
|
||||||
@ -142,19 +136,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": 5,
|
||||||
"id": "62e0dbc3",
|
"id": "82c5708c",
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"AIMessage(content=\"J'adore la programmation.\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'model_name': 'gemini-2.0-flash-001', 'safety_ratings': []}, id='run-61cff164-40be-4f88-a2df-cca58297502f-0', usage_metadata={'input_tokens': 20, 'output_tokens': 7, 'total_tokens': 27, 'input_token_details': {'cache_read': 0}})"
|
"AIMessage(content=\"J'adore la programmation.\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'model_name': 'gemini-2.0-flash', 'safety_ratings': []}, id='run-3b28d4b8-8a62-4e6c-ad4e-b53e6e825749-0', usage_metadata={'input_tokens': 20, 'output_tokens': 7, 'total_tokens': 27, 'input_token_details': {'cache_read': 0}})"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 3,
|
"execution_count": 5,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -173,8 +165,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 6,
|
||||||
"id": "d86145b3-bfef-46e8-b227-4dda5c9c2705",
|
"id": "49d2d0c2",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -191,7 +183,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "18e2bfc0-7e78-4528-a73f-499ac150dca8",
|
"id": "ee3f6e1d",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Chaining\n",
|
"## Chaining\n",
|
||||||
@ -201,17 +193,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 5,
|
"execution_count": 7,
|
||||||
"id": "e197d1d7-a070-4c96-9f8a-a0e86d046e0b",
|
"id": "3c8407ee",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"AIMessage(content='Ich liebe Programmieren.', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'model_name': 'gemini-2.0-flash-001', 'safety_ratings': []}, id='run-dd2f8fb9-62d9-4b84-9c97-ed9c34cda313-0', usage_metadata={'input_tokens': 15, 'output_tokens': 7, 'total_tokens': 22, 'input_token_details': {'cache_read': 0}})"
|
"AIMessage(content='Ich liebe Programmieren.', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'model_name': 'gemini-2.0-flash', 'safety_ratings': []}, id='run-e5561c6b-2beb-4411-9210-4796b576a7cd-0', usage_metadata={'input_tokens': 15, 'output_tokens': 7, 'total_tokens': 22, 'input_token_details': {'cache_read': 0}})"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 5,
|
"execution_count": 7,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -241,22 +233,164 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "41c2ff10-a3ba-4f40-b3aa-7a395854849e",
|
"id": "bdae9742",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Image generation\n",
|
"## Multimodal Usage\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Some Gemini models (specifically `gemini-2.0-flash-exp`) support image generation capabilities.\n",
|
"Gemini models can accept multimodal inputs (text, images, audio, video) and, for some models, generate multimodal outputs.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"### Text to image\n",
|
"### Image Input\n",
|
||||||
"\n",
|
"\n",
|
||||||
"See a simple usage example below:"
|
"Provide image inputs along with text using a `HumanMessage` with a list content format. The `gemini-2.0-flash` model can handle images."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": null,
|
||||||
"id": "7589e14d-8d1b-4c82-965f-5558d80cb677",
|
"id": "6833fe5d",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import base64\n",
|
||||||
|
"\n",
|
||||||
|
"from langchain_core.messages import HumanMessage\n",
|
||||||
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
|
"\n",
|
||||||
|
"# Example using a public URL (remains the same)\n",
|
||||||
|
"message_url = HumanMessage(\n",
|
||||||
|
" content=[\n",
|
||||||
|
" {\n",
|
||||||
|
" \"type\": \"text\",\n",
|
||||||
|
" \"text\": \"Describe the image at the URL.\",\n",
|
||||||
|
" },\n",
|
||||||
|
" {\"type\": \"image_url\", \"image_url\": \"https://picsum.photos/seed/picsum/200/300\"},\n",
|
||||||
|
" ]\n",
|
||||||
|
")\n",
|
||||||
|
"result_url = llm.invoke([message_url])\n",
|
||||||
|
"print(f\"Response for URL image: {result_url.content}\")\n",
|
||||||
|
"\n",
|
||||||
|
"# Example using a local image file encoded in base64\n",
|
||||||
|
"image_file_path = \"/Users/philschmid/projects/google-gemini/langchain/docs/static/img/agents_vs_chains.png\"\n",
|
||||||
|
"\n",
|
||||||
|
"with open(image_file_path, \"rb\") as image_file:\n",
|
||||||
|
" encoded_image = base64.b64encode(image_file.read()).decode(\"utf-8\")\n",
|
||||||
|
"\n",
|
||||||
|
"message_local = HumanMessage(\n",
|
||||||
|
" content=[\n",
|
||||||
|
" {\"type\": \"text\", \"text\": \"Describe the local image.\"},\n",
|
||||||
|
" {\"type\": \"image_url\", \"image_url\": f\"data:image/png;base64,{encoded_image}\"},\n",
|
||||||
|
" ]\n",
|
||||||
|
")\n",
|
||||||
|
"result_local = llm.invoke([message_local])\n",
|
||||||
|
"print(f\"Response for local image: {result_local.content}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "1b422382",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Other supported `image_url` formats:\n",
|
||||||
|
"- A Google Cloud Storage URI (`gs://...`). Ensure the service account has access.\n",
|
||||||
|
"- A PIL Image object (the library handles encoding).\n",
|
||||||
|
"\n",
|
||||||
|
"### Audio Input\n",
|
||||||
|
"\n",
|
||||||
|
"Provide audio file inputs along with text. Use a model like `gemini-2.0-flash`."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "a3461836",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import base64\n",
|
||||||
|
"\n",
|
||||||
|
"from langchain_core.messages import HumanMessage\n",
|
||||||
|
"\n",
|
||||||
|
"# Ensure you have an audio file named 'example_audio.mp3' or provide the correct path.\n",
|
||||||
|
"audio_file_path = \"example_audio.mp3\"\n",
|
||||||
|
"audio_mime_type = \"audio/mpeg\"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"with open(audio_file_path, \"rb\") as audio_file:\n",
|
||||||
|
" encoded_audio = base64.b64encode(audio_file.read()).decode(\"utf-8\")\n",
|
||||||
|
"\n",
|
||||||
|
"message = HumanMessage(\n",
|
||||||
|
" content=[\n",
|
||||||
|
" {\"type\": \"text\", \"text\": \"Transcribe the audio.\"},\n",
|
||||||
|
" {\n",
|
||||||
|
" \"type\": \"media\",\n",
|
||||||
|
" \"data\": encoded_audio, # Use base64 string directly\n",
|
||||||
|
" \"mime_type\": audio_mime_type,\n",
|
||||||
|
" },\n",
|
||||||
|
" ]\n",
|
||||||
|
")\n",
|
||||||
|
"response = llm.invoke([message]) # Uncomment to run\n",
|
||||||
|
"print(f\"Response for audio: {response.content}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "0d898e27",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Video Input\n",
|
||||||
|
"\n",
|
||||||
|
"Provide video file inputs along with text. Use a model like `gemini-2.0-flash`."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "3046e74b",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import base64\n",
|
||||||
|
"\n",
|
||||||
|
"from langchain_core.messages import HumanMessage\n",
|
||||||
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
|
"\n",
|
||||||
|
"# Ensure you have a video file named 'example_video.mp4' or provide the correct path.\n",
|
||||||
|
"video_file_path = \"example_video.mp4\"\n",
|
||||||
|
"video_mime_type = \"video/mp4\"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"with open(video_file_path, \"rb\") as video_file:\n",
|
||||||
|
" encoded_video = base64.b64encode(video_file.read()).decode(\"utf-8\")\n",
|
||||||
|
"\n",
|
||||||
|
"message = HumanMessage(\n",
|
||||||
|
" content=[\n",
|
||||||
|
" {\"type\": \"text\", \"text\": \"Describe the first few frames of the video.\"},\n",
|
||||||
|
" {\n",
|
||||||
|
" \"type\": \"media\",\n",
|
||||||
|
" \"data\": encoded_video, # Use base64 string directly\n",
|
||||||
|
" \"mime_type\": video_mime_type,\n",
|
||||||
|
" },\n",
|
||||||
|
" ]\n",
|
||||||
|
")\n",
|
||||||
|
"response = llm.invoke([message]) # Uncomment to run\n",
|
||||||
|
"print(f\"Response for video: {response.content}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "2df11d89",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Image Generation (Multimodal Output)\n",
|
||||||
|
"\n",
|
||||||
|
"The `gemini-2.0-flash` model can generate text and images inline (image generation is experimental). You need to specify the desired `response_modalities`."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "c0b7180f",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -266,17 +400,12 @@
|
|||||||
"<IPython.core.display.Image object>"
|
"<IPython.core.display.Image object>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"image/png": {
|
|
||||||
"width": 300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output_type": "display_data"
|
"output_type": "display_data"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"import base64\n",
|
"import base64\n",
|
||||||
"from io import BytesIO\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"from IPython.display import Image, display\n",
|
"from IPython.display import Image, display\n",
|
||||||
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
@ -301,7 +430,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "b14c0d87-cf7e-4d88-bda1-2ab40ec0350a",
|
"id": "14bf00f1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Image and text to image\n",
|
"### Image and text to image\n",
|
||||||
@ -311,8 +440,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": null,
|
||||||
"id": "0f4ed7a5-980c-4b54-b743-0b988909744c",
|
"id": "d65e195c",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -322,11 +451,7 @@
|
|||||||
"<IPython.core.display.Image object>"
|
"<IPython.core.display.Image object>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"image/png": {
|
|
||||||
"width": 300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output_type": "display_data"
|
"output_type": "display_data"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -349,7 +474,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "a62669d8-becd-495f-8f4a-82d7c5d87969",
|
"id": "43b54d3f",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"You can also represent an input image and query in a single message by encoding the base64 data in the [data URI scheme](https://en.wikipedia.org/wiki/Data_URI_scheme):"
|
"You can also represent an input image and query in a single message by encoding the base64 data in the [data URI scheme](https://en.wikipedia.org/wiki/Data_URI_scheme):"
|
||||||
@ -357,8 +482,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 9,
|
"execution_count": null,
|
||||||
"id": "6241da43-e210-43bc-89af-b3c480ea06e9",
|
"id": "0dfc7e1e",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -368,11 +493,7 @@
|
|||||||
"<IPython.core.display.Image object>"
|
"<IPython.core.display.Image object>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"image/png": {
|
|
||||||
"width": 300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output_type": "display_data"
|
"output_type": "display_data"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -403,7 +524,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "cfe228d3-6773-4283-9788-87bdf6912b1c",
|
"id": "789818d7",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"You can also use LangGraph to manage the conversation history for you as in [this tutorial](/docs/tutorials/chatbot/)."
|
"You can also use LangGraph to manage the conversation history for you as in [this tutorial](/docs/tutorials/chatbot/)."
|
||||||
@ -411,7 +532,313 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "d1ee55bc-ffc8-4cfa-801c-993953a08cfd",
|
"id": "b037e2dc",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Tool Calling\n",
|
||||||
|
"\n",
|
||||||
|
"You can equip the model with tools to call."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "b0d759f9",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[{'name': 'get_weather', 'args': {'location': 'San Francisco'}, 'id': 'a6248087-74c5-4b7c-9250-f335e642927c', 'type': 'tool_call'}]\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"AIMessage(content=\"OK. It's sunny in San Francisco.\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'model_name': 'gemini-2.0-flash', 'safety_ratings': []}, id='run-ac5bb52c-e244-4c72-9fbc-fb2a9cd7a72e-0', usage_metadata={'input_tokens': 29, 'output_tokens': 11, 'total_tokens': 40, 'input_token_details': {'cache_read': 0}})"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 28,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from langchain_core.tools import tool\n",
|
||||||
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"# Define the tool\n",
|
||||||
|
"@tool(description=\"Get the current weather in a given location\")\n",
|
||||||
|
"def get_weather(location: str) -> str:\n",
|
||||||
|
" return \"It's sunny.\"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"# Initialize the model and bind the tool\n",
|
||||||
|
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\")\n",
|
||||||
|
"llm_with_tools = llm.bind_tools([get_weather])\n",
|
||||||
|
"\n",
|
||||||
|
"# Invoke the model with a query that should trigger the tool\n",
|
||||||
|
"query = \"What's the weather in San Francisco?\"\n",
|
||||||
|
"ai_msg = llm_with_tools.invoke(query)\n",
|
||||||
|
"\n",
|
||||||
|
"# Check the tool calls in the response\n",
|
||||||
|
"print(ai_msg.tool_calls)\n",
|
||||||
|
"\n",
|
||||||
|
"# Example tool call message would be needed here if you were actually running the tool\n",
|
||||||
|
"from langchain_core.messages import ToolMessage\n",
|
||||||
|
"\n",
|
||||||
|
"tool_message = ToolMessage(\n",
|
||||||
|
" content=get_weather(*ai_msg.tool_calls[0][\"args\"]),\n",
|
||||||
|
" tool_call_id=ai_msg.tool_calls[0][\"id\"],\n",
|
||||||
|
")\n",
|
||||||
|
"llm_with_tools.invoke([ai_msg, tool_message]) # Example of passing tool result back"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "91d42b86",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Structured Output\n",
|
||||||
|
"\n",
|
||||||
|
"Force the model to respond with a specific structure using Pydantic models."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
|
"id": "7457dbe4",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"name='Abraham Lincoln' height_m=1.93\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||||
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"# Define the desired structure\n",
|
||||||
|
"class Person(BaseModel):\n",
|
||||||
|
" \"\"\"Information about a person.\"\"\"\n",
|
||||||
|
"\n",
|
||||||
|
" name: str = Field(..., description=\"The person's name\")\n",
|
||||||
|
" height_m: float = Field(..., description=\"The person's height in meters\")\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"# Initialize the model\n",
|
||||||
|
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\", temperature=0)\n",
|
||||||
|
"structured_llm = llm.with_structured_output(Person)\n",
|
||||||
|
"\n",
|
||||||
|
"# Invoke the model with a query asking for structured information\n",
|
||||||
|
"result = structured_llm.invoke(\n",
|
||||||
|
" \"Who was the 16th president of the USA, and how tall was he in meters?\"\n",
|
||||||
|
")\n",
|
||||||
|
"print(result)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "90d4725e",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"## Token Usage Tracking\n",
|
||||||
|
"\n",
|
||||||
|
"Access token usage information from the response metadata."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 18,
|
||||||
|
"id": "edcc003e",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Prompt engineering is the art and science of crafting effective text prompts to elicit desired and accurate responses from large language models.\n",
|
||||||
|
"\n",
|
||||||
|
"Usage Metadata:\n",
|
||||||
|
"{'input_tokens': 10, 'output_tokens': 24, 'total_tokens': 34, 'input_token_details': {'cache_read': 0}}\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
|
"\n",
|
||||||
|
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\")\n",
|
||||||
|
"\n",
|
||||||
|
"result = llm.invoke(\"Explain the concept of prompt engineering in one sentence.\")\n",
|
||||||
|
"\n",
|
||||||
|
"print(result.content)\n",
|
||||||
|
"print(\"\\nUsage Metadata:\")\n",
|
||||||
|
"print(result.usage_metadata)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "28950dbc",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Built-in tools\n",
|
||||||
|
"\n",
|
||||||
|
"Google Gemini supports a variety of built-in tools ([google search](https://ai.google.dev/gemini-api/docs/grounding/search-suggestions), [code execution](https://ai.google.dev/gemini-api/docs/code-execution?lang=python)), which can be bound to the model in the usual way."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "dd074816",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"The next total solar eclipse visible in the United States will occur on August 23, 2044. However, the path of totality will only pass through Montana, North Dakota, and South Dakota.\n",
|
||||||
|
"\n",
|
||||||
|
"For a total solar eclipse that crosses a significant portion of the continental U.S., you'll have to wait until August 12, 2045. This eclipse will start in California and end in Florida.\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from google.ai.generativelanguage_v1beta.types import Tool as GenAITool\n",
|
||||||
|
"\n",
|
||||||
|
"resp = llm.invoke(\n",
|
||||||
|
" \"When is the next total solar eclipse in US?\",\n",
|
||||||
|
" tools=[GenAITool(google_search={})],\n",
|
||||||
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"print(resp.content)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 43,
|
||||||
|
"id": "6964be2d",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Executable code: print(2*2)\n",
|
||||||
|
"\n",
|
||||||
|
"Code execution result: 4\n",
|
||||||
|
"\n",
|
||||||
|
"2*2 is 4.\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"/Users/philschmid/projects/google-gemini/langchain/.venv/lib/python3.9/site-packages/langchain_google_genai/chat_models.py:580: UserWarning: \n",
|
||||||
|
" ⚠️ Warning: Output may vary each run. \n",
|
||||||
|
" - 'executable_code': Always present. \n",
|
||||||
|
" - 'execution_result' & 'image_url': May be absent for some queries. \n",
|
||||||
|
"\n",
|
||||||
|
" Validate before using in production.\n",
|
||||||
|
"\n",
|
||||||
|
" warnings.warn(\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from google.ai.generativelanguage_v1beta.types import Tool as GenAITool\n",
|
||||||
|
"\n",
|
||||||
|
"resp = llm.invoke(\n",
|
||||||
|
" \"What is 2*2, use python\",\n",
|
||||||
|
" tools=[GenAITool(code_execution={})],\n",
|
||||||
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"for c in resp.content:\n",
|
||||||
|
" if isinstance(c, dict):\n",
|
||||||
|
" if c[\"type\"] == \"code_execution_result\":\n",
|
||||||
|
" print(f\"Code execution result: {c['code_execution_result']}\")\n",
|
||||||
|
" elif c[\"type\"] == \"executable_code\":\n",
|
||||||
|
" print(f\"Executable code: {c['executable_code']}\")\n",
|
||||||
|
" else:\n",
|
||||||
|
" print(c)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "a27e6ff4",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Native Async\n",
|
||||||
|
"\n",
|
||||||
|
"Use asynchronous methods for non-blocking calls."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 17,
|
||||||
|
"id": "c6803e57",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Async Invoke Result: The sky is blue due to a phenomenon called **Rayle...\n",
|
||||||
|
"\n",
|
||||||
|
"Async Stream Result:\n",
|
||||||
|
"The thread is free, it does not wait,\n",
|
||||||
|
"For answers slow, or tasks of fate.\n",
|
||||||
|
"A promise made, a future bright,\n",
|
||||||
|
"It moves ahead, with all its might.\n",
|
||||||
|
"\n",
|
||||||
|
"A callback waits, a signal sent,\n",
|
||||||
|
"When data's read, or job is spent.\n",
|
||||||
|
"Non-blocking code, a graceful dance,\n",
|
||||||
|
"Responsive apps, a fleeting glance.\n",
|
||||||
|
"\n",
|
||||||
|
"Async Batch Results: ['1 + 1 = 2', '2 + 2 = 4']\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||||
|
"\n",
|
||||||
|
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\")\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"async def run_async_calls():\n",
|
||||||
|
" # Async invoke\n",
|
||||||
|
" result_ainvoke = await llm.ainvoke(\"Why is the sky blue?\")\n",
|
||||||
|
" print(\"Async Invoke Result:\", result_ainvoke.content[:50] + \"...\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Async stream\n",
|
||||||
|
" print(\"\\nAsync Stream Result:\")\n",
|
||||||
|
" async for chunk in llm.astream(\n",
|
||||||
|
" \"Write a short poem about asynchronous programming.\"\n",
|
||||||
|
" ):\n",
|
||||||
|
" print(chunk.content, end=\"\", flush=True)\n",
|
||||||
|
" print(\"\\n\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Async batch\n",
|
||||||
|
" results_abatch = await llm.abatch([\"What is 1+1?\", \"What is 2+2?\"])\n",
|
||||||
|
" print(\"Async Batch Results:\", [res.content for res in results_abatch])\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"await run_async_calls()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "99204b32",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Safety Settings\n",
|
"## Safety Settings\n",
|
||||||
@ -421,8 +848,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 14,
|
"execution_count": null,
|
||||||
"id": "238b2f96-e573-4fac-bbf2-7e52ad926833",
|
"id": "d4c14039",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -442,7 +869,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "5805d40c-deb8-4924-8e72-a294a0482fc9",
|
"id": "dea38fb1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"For an enumeration of the categories and thresholds available, see Google's [safety setting types](https://ai.google.dev/api/python/google/generativeai/types/SafetySettingDict)."
|
"For an enumeration of the categories and thresholds available, see Google's [safety setting types](https://ai.google.dev/api/python/google/generativeai/types/SafetySettingDict)."
|
||||||
@ -450,7 +877,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "3a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
"id": "d6d0e853",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## API reference\n",
|
"## API reference\n",
|
||||||
@ -461,7 +888,7 @@
|
|||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3 (ipykernel)",
|
"display_name": ".venv",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "python3"
|
||||||
},
|
},
|
||||||
@ -475,7 +902,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.4"
|
"version": "3.9.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
@ -1413,6 +1413,23 @@
|
|||||||
"second_output_message = llm.invoke(history)"
|
"second_output_message = llm.invoke(history)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "90c18d18-b25c-4509-a639-bd652b92f518",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Flex processing\n",
|
||||||
|
"\n",
|
||||||
|
"OpenAI offers a variety of [service tiers](https://platform.openai.com/docs/guides/flex-processing). The \"flex\" tier offers cheaper pricing for requests, with the trade-off that responses may take longer and resources might not always be available. This approach is best suited for non-critical tasks, including model testing, data enhancement, or jobs that can be run asynchronously.\n",
|
||||||
|
"\n",
|
||||||
|
"To use it, initialize the model with `service_tier=\"flex\"`:\n",
|
||||||
|
"```python\n",
|
||||||
|
"llm = ChatOpenAI(model=\"o4-mini\", service_tier=\"flex\")\n",
|
||||||
|
"```\n",
|
||||||
|
"\n",
|
||||||
|
"Note that this is a beta feature that is only available for a subset of models. See OpenAI [docs](https://platform.openai.com/docs/guides/flex-processing) for more detail."
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "a796d728-971b-408b-88d5-440015bbb941",
|
"id": "a796d728-971b-408b-88d5-440015bbb941",
|
||||||
@ -1420,7 +1437,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"## API reference\n",
|
"## API reference\n",
|
||||||
"\n",
|
"\n",
|
||||||
"For detailed documentation of all ChatOpenAI features and configurations head to the API reference: https://python.langchain.com/api_reference/openai/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html"
|
"For detailed documentation of all ChatOpenAI features and configurations head to the [API reference](https://python.langchain.com/api_reference/openai/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html)."
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -34,33 +34,46 @@
|
|||||||
"id": "juAmbgoWD17u"
|
"id": "juAmbgoWD17u"
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"The AstraDB Document Loader returns a list of Langchain Documents from an AstraDB database.\n",
|
"The Astra DB Document Loader returns a list of Langchain `Document` objects read from an Astra DB collection.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The Loader takes the following parameters:\n",
|
"The loader takes the following parameters:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"* `api_endpoint`: Astra DB API endpoint. Looks like `https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com`\n",
|
"* `api_endpoint`: Astra DB API endpoint. Looks like `https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com`\n",
|
||||||
"* `token`: AstraDB token. Looks like `AstraCS:6gBhNmsk135....`\n",
|
"* `token`: Astra DB token. Looks like `AstraCS:aBcD0123...`\n",
|
||||||
"* `collection_name` : AstraDB collection name\n",
|
"* `collection_name` : AstraDB collection name\n",
|
||||||
"* `namespace`: (Optional) AstraDB namespace\n",
|
"* `namespace`: (Optional) AstraDB namespace (called _keyspace_ in Astra DB)\n",
|
||||||
"* `filter_criteria`: (Optional) Filter used in the find query\n",
|
"* `filter_criteria`: (Optional) Filter used in the find query\n",
|
||||||
"* `projection`: (Optional) Projection used in the find query\n",
|
"* `projection`: (Optional) Projection used in the find query\n",
|
||||||
"* `find_options`: (Optional) Options used in the find query\n",
|
"* `limit`: (Optional) Maximum number of documents to retrieve\n",
|
||||||
"* `nb_prefetched`: (Optional) Number of documents pre-fetched by the loader\n",
|
|
||||||
"* `extraction_function`: (Optional) A function to convert the AstraDB document to the LangChain `page_content` string. Defaults to `json.dumps`\n",
|
"* `extraction_function`: (Optional) A function to convert the AstraDB document to the LangChain `page_content` string. Defaults to `json.dumps`\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The following metadata is set to the LangChain Documents metadata output:\n",
|
"The loader sets the following metadata for the documents it reads:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"```python\n",
|
"```python\n",
|
||||||
"{\n",
|
"metadata={\n",
|
||||||
" metadata : {\n",
|
|
||||||
" \"namespace\": \"...\", \n",
|
" \"namespace\": \"...\", \n",
|
||||||
" \"api_endpoint\": \"...\", \n",
|
" \"api_endpoint\": \"...\", \n",
|
||||||
" \"collection\": \"...\"\n",
|
" \"collection\": \"...\"\n",
|
||||||
"}\n",
|
"}\n",
|
||||||
"}\n",
|
|
||||||
"```"
|
"```"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Setup"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"!pip install \"langchain-astradb>=0.6,<0.7\""
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"attachments": {},
|
"attachments": {},
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@ -71,24 +84,43 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 2,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_community.document_loaders import AstraDBLoader"
|
"from langchain_astradb import AstraDBLoader"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"[**API Reference:** `AstraDBLoader`](https://python.langchain.com/api_reference/astradb/document_loaders/langchain_astradb.document_loaders.AstraDBLoader.html#langchain_astradb.document_loaders.AstraDBLoader)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 3,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2024-01-08T12:41:22.643335Z",
|
"end_time": "2024-01-08T12:41:22.643335Z",
|
||||||
"start_time": "2024-01-08T12:40:57.759116Z"
|
"start_time": "2024-01-08T12:40:57.759116Z"
|
||||||
},
|
},
|
||||||
"collapsed": false
|
"collapsed": false,
|
||||||
|
"jupyter": {
|
||||||
|
"outputs_hidden": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdin",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"ASTRA_DB_API_ENDPOINT = https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com\n",
|
||||||
|
"ASTRA_DB_APPLICATION_TOKEN = ········\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"from getpass import getpass\n",
|
"from getpass import getpass\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -98,7 +130,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 6,
|
"execution_count": 4,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2024-01-08T12:42:25.395162Z",
|
"end_time": "2024-01-08T12:42:25.395162Z",
|
||||||
@ -112,19 +144,22 @@
|
|||||||
" token=ASTRA_DB_APPLICATION_TOKEN,\n",
|
" token=ASTRA_DB_APPLICATION_TOKEN,\n",
|
||||||
" collection_name=\"movie_reviews\",\n",
|
" collection_name=\"movie_reviews\",\n",
|
||||||
" projection={\"title\": 1, \"reviewtext\": 1},\n",
|
" projection={\"title\": 1, \"reviewtext\": 1},\n",
|
||||||
" find_options={\"limit\": 10},\n",
|
" limit=10,\n",
|
||||||
")"
|
")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": 5,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2024-01-08T12:42:30.236489Z",
|
"end_time": "2024-01-08T12:42:30.236489Z",
|
||||||
"start_time": "2024-01-08T12:42:29.612133Z"
|
"start_time": "2024-01-08T12:42:29.612133Z"
|
||||||
},
|
},
|
||||||
"collapsed": false
|
"collapsed": false,
|
||||||
|
"jupyter": {
|
||||||
|
"outputs_hidden": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -133,7 +168,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": 6,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2024-01-08T12:42:31.369394Z",
|
"end_time": "2024-01-08T12:42:31.369394Z",
|
||||||
@ -144,10 +179,10 @@
|
|||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"Document(page_content='{\"_id\": \"659bdffa16cbc4586b11a423\", \"title\": \"Dangerous Men\", \"reviewtext\": \"\\\\\"Dangerous Men,\\\\\" the picture\\'s production notes inform, took 26 years to reach the big screen. After having seen it, I wonder: What was the rush?\"}', metadata={'namespace': 'default_keyspace', 'api_endpoint': 'https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com', 'collection': 'movie_reviews'})"
|
"Document(metadata={'namespace': 'default_keyspace', 'api_endpoint': 'https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com', 'collection': 'movie_reviews'}, page_content='{\"_id\": \"659bdffa16cbc4586b11a423\", \"title\": \"Dangerous Men\", \"reviewtext\": \"\\\\\"Dangerous Men,\\\\\" the picture\\'s production notes inform, took 26 years to reach the big screen. After having seen it, I wonder: What was the rush?\"}')"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 8,
|
"execution_count": 7,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -179,7 +214,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.9.18"
|
"version": "3.12.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
@ -49,7 +49,14 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_community.document_loaders import BrowserbaseLoader"
|
"import os\n",
|
||||||
|
"\n",
|
||||||
|
"from langchain_community.document_loaders import BrowserbaseLoader\n",
|
||||||
|
"\n",
|
||||||
|
"load_dotenv()\n",
|
||||||
|
"\n",
|
||||||
|
"BROWSERBASE_API_KEY = os.getenv(\"BROWSERBASE_API_KEY\")\n",
|
||||||
|
"BROWSERBASE_PROJECT_ID = os.getenv(\"BROWSERBASE_PROJECT_ID\")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,6 +66,8 @@
|
|||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"loader = BrowserbaseLoader(\n",
|
"loader = BrowserbaseLoader(\n",
|
||||||
|
" api_key=BROWSERBASE_API_KEY,\n",
|
||||||
|
" project_id=BROWSERBASE_PROJECT_ID,\n",
|
||||||
" urls=[\n",
|
" urls=[\n",
|
||||||
" \"https://example.com\",\n",
|
" \"https://example.com\",\n",
|
||||||
" ],\n",
|
" ],\n",
|
||||||
@ -78,52 +87,11 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"- `urls` Required. A list of URLs to fetch.\n",
|
"- `urls` Required. A list of URLs to fetch.\n",
|
||||||
"- `text_content` Retrieve only text content. Default is `False`.\n",
|
"- `text_content` Retrieve only text content. Default is `False`.\n",
|
||||||
"- `api_key` Optional. Browserbase API key. Default is `BROWSERBASE_API_KEY` env variable.\n",
|
"- `api_key` Browserbase API key. Default is `BROWSERBASE_API_KEY` env variable.\n",
|
||||||
"- `project_id` Optional. Browserbase Project ID. Default is `BROWSERBASE_PROJECT_ID` env variable.\n",
|
"- `project_id` Browserbase Project ID. Default is `BROWSERBASE_PROJECT_ID` env variable.\n",
|
||||||
"- `session_id` Optional. Provide an existing Session ID.\n",
|
"- `session_id` Optional. Provide an existing Session ID.\n",
|
||||||
"- `proxy` Optional. Enable/Disable Proxies."
|
"- `proxy` Optional. Enable/Disable Proxies."
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## Loading images\n",
|
|
||||||
"\n",
|
|
||||||
"You can also load screenshots of webpages (as bytes) for multi-modal models.\n",
|
|
||||||
"\n",
|
|
||||||
"Full example using GPT-4V:"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"from browserbase import Browserbase\n",
|
|
||||||
"from browserbase.helpers.gpt4 import GPT4VImage, GPT4VImageDetail\n",
|
|
||||||
"from langchain_core.messages import HumanMessage\n",
|
|
||||||
"from langchain_openai import ChatOpenAI\n",
|
|
||||||
"\n",
|
|
||||||
"chat = ChatOpenAI(model=\"gpt-4-vision-preview\", max_tokens=256)\n",
|
|
||||||
"browser = Browserbase()\n",
|
|
||||||
"\n",
|
|
||||||
"screenshot = browser.screenshot(\"https://browserbase.com\")\n",
|
|
||||||
"\n",
|
|
||||||
"result = chat.invoke(\n",
|
|
||||||
" [\n",
|
|
||||||
" HumanMessage(\n",
|
|
||||||
" content=[\n",
|
|
||||||
" {\"type\": \"text\", \"text\": \"What color is the logo?\"},\n",
|
|
||||||
" GPT4VImage(screenshot, GPT4VImageDetail.auto),\n",
|
|
||||||
" ]\n",
|
|
||||||
" )\n",
|
|
||||||
" ]\n",
|
|
||||||
")\n",
|
|
||||||
"\n",
|
|
||||||
"print(result.content)"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
@ -3112,8 +3112,8 @@
|
|||||||
"|------------|---------|\n",
|
"|------------|---------|\n",
|
||||||
"| langchain_astradb.cache | [AstraDBCache](https://python.langchain.com/api_reference/astradb/cache/langchain_astradb.cache.AstraDBCache.html) |\n",
|
"| langchain_astradb.cache | [AstraDBCache](https://python.langchain.com/api_reference/astradb/cache/langchain_astradb.cache.AstraDBCache.html) |\n",
|
||||||
"| langchain_astradb.cache | [AstraDBSemanticCache](https://python.langchain.com/api_reference/astradb/cache/langchain_astradb.cache.AstraDBSemanticCache.html) |\n",
|
"| langchain_astradb.cache | [AstraDBSemanticCache](https://python.langchain.com/api_reference/astradb/cache/langchain_astradb.cache.AstraDBSemanticCache.html) |\n",
|
||||||
"| langchain_community.cache | [AstraDBCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.AstraDBCache.html) |\n",
|
"| langchain_community.cache | [AstraDBCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.AstraDBCache.html) (deprecated since `langchain-community==0.0.28`) |\n",
|
||||||
"| langchain_community.cache | [AstraDBSemanticCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.AstraDBSemanticCache.html) |\n",
|
"| langchain_community.cache | [AstraDBSemanticCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.AstraDBSemanticCache.html) (deprecated since `langchain-community==0.0.28`) |\n",
|
||||||
"| langchain_community.cache | [AzureCosmosDBSemanticCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.AzureCosmosDBSemanticCache.html) |\n",
|
"| langchain_community.cache | [AzureCosmosDBSemanticCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.AzureCosmosDBSemanticCache.html) |\n",
|
||||||
"| langchain_community.cache | [CassandraCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.CassandraCache.html) |\n",
|
"| langchain_community.cache | [CassandraCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.CassandraCache.html) |\n",
|
||||||
"| langchain_community.cache | [CassandraSemanticCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.CassandraSemanticCache.html) |\n",
|
"| langchain_community.cache | [CassandraSemanticCache](https://python.langchain.com/api_reference/community/cache/langchain_community.cache.CassandraSemanticCache.html) |\n",
|
||||||
|
@ -17,22 +17,22 @@
|
|||||||
"id": "f507f58b-bf22-4a48-8daf-68d869bcd1ba",
|
"id": "f507f58b-bf22-4a48-8daf-68d869bcd1ba",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Setting up\n",
|
"## Setup\n",
|
||||||
"\n",
|
"\n",
|
||||||
"To run this notebook you need a running Astra DB. Get the connection secrets on your Astra dashboard:\n",
|
"To run this notebook you need a running Astra DB. Get the connection secrets on your Astra dashboard:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"- the API Endpoint looks like `https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com`;\n",
|
"- the API Endpoint looks like `https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com`;\n",
|
||||||
"- the Token looks like `AstraCS:6gBhNmsk135...`."
|
"- the Database Token looks like `AstraCS:aBcD0123...`."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 1,
|
||||||
"id": "d7092199",
|
"id": "d7092199",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"%pip install --upgrade --quiet \"astrapy>=0.7.1 langchain-community\" "
|
"!pip install \"langchain-astradb>=0.6,<0.7\""
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -45,12 +45,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 2,
|
||||||
"id": "163d97f0",
|
"id": "163d97f0",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdin",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"ASTRA_DB_API_ENDPOINT = https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com\n",
|
"ASTRA_DB_API_ENDPOINT = https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com\n",
|
||||||
@ -65,14 +65,6 @@
|
|||||||
"ASTRA_DB_APPLICATION_TOKEN = getpass.getpass(\"ASTRA_DB_APPLICATION_TOKEN = \")"
|
"ASTRA_DB_APPLICATION_TOKEN = getpass.getpass(\"ASTRA_DB_APPLICATION_TOKEN = \")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "55860b2d",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"Depending on whether local or cloud-based Astra DB, create the corresponding database connection \"Session\" object."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "36c163e8",
|
"id": "36c163e8",
|
||||||
@ -83,12 +75,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 3,
|
||||||
"id": "d15e3302",
|
"id": "d15e3302",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_community.chat_message_histories import AstraDBChatMessageHistory\n",
|
"from langchain_astradb import AstraDBChatMessageHistory\n",
|
||||||
"\n",
|
"\n",
|
||||||
"message_history = AstraDBChatMessageHistory(\n",
|
"message_history = AstraDBChatMessageHistory(\n",
|
||||||
" session_id=\"test-session\",\n",
|
" session_id=\"test-session\",\n",
|
||||||
@ -98,22 +90,31 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"message_history.add_user_message(\"hi!\")\n",
|
"message_history.add_user_message(\"hi!\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"message_history.add_ai_message(\"whats up?\")"
|
"message_history.add_ai_message(\"hello, how are you?\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "53acb4a8-d536-4a58-9fee-7d70033d9c81",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"[**API Reference:** `AstraDBChatMessageHistory`](https://python.langchain.com/api_reference/astradb/chat_message_histories/langchain_astradb.chat_message_histories.AstraDBChatMessageHistory.html#langchain_astradb.chat_message_histories.AstraDBChatMessageHistory)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": 4,
|
||||||
"id": "64fc465e",
|
"id": "64fc465e",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"[HumanMessage(content='hi!'), AIMessage(content='whats up?')]"
|
"[HumanMessage(content='hi!', additional_kwargs={}, response_metadata={}),\n",
|
||||||
|
" AIMessage(content='hello, how are you?', additional_kwargs={}, response_metadata={})]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 3,
|
"execution_count": 4,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -139,7 +140,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.12"
|
"version": "3.12.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
## Installation and Setup
|
## Installation and Setup
|
||||||
|
|
||||||
We need to install the `hdbcli` python package.
|
We need to install the `langchain-hana` python package.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install hdbcli
|
pip install langchain-hana
|
||||||
```
|
```
|
||||||
|
|
||||||
## Vectorstore
|
## Vectorstore
|
||||||
@ -21,5 +21,5 @@ pip install hdbcli
|
|||||||
See a [usage example](/docs/integrations/vectorstores/sap_hanavector).
|
See a [usage example](/docs/integrations/vectorstores/sap_hanavector).
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from langchain_community.vectorstores.hanavector import HanaDB
|
from langchain_hana import HanaDB
|
||||||
```
|
```
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Astra DB (Cassandra)\n",
|
"# Astra DB\n",
|
||||||
"\n",
|
"\n",
|
||||||
">[DataStax Astra DB](https://docs.datastax.com/en/astra/home/astra.html) is a serverless vector-capable database built on `Cassandra` and made conveniently available through an easy-to-use JSON API.\n",
|
">[DataStax Astra DB](https://docs.datastax.com/en/astra/home/astra.html) is a serverless vector-capable database built on `Cassandra` and made conveniently available through an easy-to-use JSON API.\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -16,32 +16,46 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Creating an Astra DB vector store\n",
|
"## Creating an Astra DB vector store\n",
|
||||||
"First we'll want to create an Astra DB VectorStore and seed it with some data. We've created a small demo set of documents that contain summaries of movies.\n",
|
"First, create an Astra DB vector store and seed it with some data.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"NOTE: The self-query retriever requires you to have `lark` installed (`pip install lark`). We also need the `astrapy` package."
|
"We've created a small demo set of documents containing movie summaries.\n",
|
||||||
|
"\n",
|
||||||
|
"NOTE: The self-query retriever requires the `lark` package installed (`pip install lark`)."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 5,
|
"execution_count": null,
|
||||||
"metadata": {},
|
"metadata": {
|
||||||
|
"scrolled": true
|
||||||
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"%pip install --upgrade --quiet lark astrapy langchain-openai"
|
"!pip install \"langchain-astradb>=0.6,<0.7\" \\\n",
|
||||||
|
" \"langchain_openai>=0.3,<0.4\" \\\n",
|
||||||
|
" \"lark>=1.2,<2.0\""
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"We want to use `OpenAIEmbeddings` so we have to get the OpenAI API Key."
|
"In this example, you'll use the `OpenAIEmbeddings`. Please enter an OpenAI API Key."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 1,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdin",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"OpenAI API Key: ········\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"import os\n",
|
"import os\n",
|
||||||
"from getpass import getpass\n",
|
"from getpass import getpass\n",
|
||||||
@ -69,14 +83,23 @@
|
|||||||
"Create the Astra DB VectorStore:\n",
|
"Create the Astra DB VectorStore:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"- the API Endpoint looks like `https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com`\n",
|
"- the API Endpoint looks like `https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com`\n",
|
||||||
"- the Token looks like `AstraCS:6gBhNmsk135....`"
|
"- the Token looks like `AstraCS:aBcD0123...`"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 2,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdin",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"ASTRA_DB_API_ENDPOINT = https://01234567-89ab-cdef-0123-456789abcdef-us-east1.apps.astra.datastax.com\n",
|
||||||
|
"ASTRA_DB_APPLICATION_TOKEN = ········\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"ASTRA_DB_API_ENDPOINT = input(\"ASTRA_DB_API_ENDPOINT = \")\n",
|
"ASTRA_DB_API_ENDPOINT = input(\"ASTRA_DB_API_ENDPOINT = \")\n",
|
||||||
"ASTRA_DB_APPLICATION_TOKEN = getpass(\"ASTRA_DB_APPLICATION_TOKEN = \")"
|
"ASTRA_DB_APPLICATION_TOKEN = getpass(\"ASTRA_DB_APPLICATION_TOKEN = \")"
|
||||||
@ -84,11 +107,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 3,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_community.vectorstores import AstraDB\n",
|
"from langchain_astradb import AstraDBVectorStore\n",
|
||||||
"from langchain_core.documents import Document\n",
|
"from langchain_core.documents import Document\n",
|
||||||
"\n",
|
"\n",
|
||||||
"docs = [\n",
|
"docs = [\n",
|
||||||
@ -101,11 +124,13 @@
|
|||||||
" metadata={\"year\": 2010, \"director\": \"Christopher Nolan\", \"rating\": 8.2},\n",
|
" metadata={\"year\": 2010, \"director\": \"Christopher Nolan\", \"rating\": 8.2},\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" Document(\n",
|
" Document(\n",
|
||||||
" page_content=\"A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea\",\n",
|
" page_content=\"A psychologist / detective gets lost in a series of dreams within dreams \"\n",
|
||||||
|
" \"within dreams and Inception reused the idea\",\n",
|
||||||
" metadata={\"year\": 2006, \"director\": \"Satoshi Kon\", \"rating\": 8.6},\n",
|
" metadata={\"year\": 2006, \"director\": \"Satoshi Kon\", \"rating\": 8.6},\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" Document(\n",
|
" Document(\n",
|
||||||
" page_content=\"A bunch of normal-sized women are supremely wholesome and some men pine after them\",\n",
|
" page_content=\"A bunch of normal-sized women are supremely wholesome and some men \"\n",
|
||||||
|
" \"pine after them\",\n",
|
||||||
" metadata={\"year\": 2019, \"director\": \"Greta Gerwig\", \"rating\": 8.3},\n",
|
" metadata={\"year\": 2019, \"director\": \"Greta Gerwig\", \"rating\": 8.3},\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" Document(\n",
|
" Document(\n",
|
||||||
@ -123,7 +148,7 @@
|
|||||||
" ),\n",
|
" ),\n",
|
||||||
"]\n",
|
"]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"vectorstore = AstraDB.from_documents(\n",
|
"vectorstore = AstraDBVectorStore.from_documents(\n",
|
||||||
" docs,\n",
|
" docs,\n",
|
||||||
" embeddings,\n",
|
" embeddings,\n",
|
||||||
" collection_name=\"astra_self_query_demo\",\n",
|
" collection_name=\"astra_self_query_demo\",\n",
|
||||||
@ -136,13 +161,16 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Creating our self-querying retriever\n",
|
"## Creating a self-querying retriever\n",
|
||||||
"Now we can instantiate our retriever. To do this we'll need to provide some information upfront about the metadata fields that our documents support and a short description of the document contents."
|
"\n",
|
||||||
|
"Now you can instantiate the retriever.\n",
|
||||||
|
"\n",
|
||||||
|
"To do this, you need to provide some information upfront about the metadata fields that the documents support, along with a short description of the documents' contents."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 4,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -174,7 +202,11 @@
|
|||||||
"llm = OpenAI(temperature=0)\n",
|
"llm = OpenAI(temperature=0)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"retriever = SelfQueryRetriever.from_llm(\n",
|
"retriever = SelfQueryRetriever.from_llm(\n",
|
||||||
" llm, vectorstore, document_content_description, metadata_field_info, verbose=True\n",
|
" llm,\n",
|
||||||
|
" vectorstore,\n",
|
||||||
|
" document_content_description,\n",
|
||||||
|
" metadata_field_info,\n",
|
||||||
|
" verbose=True,\n",
|
||||||
")"
|
")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -183,14 +215,29 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Testing it out\n",
|
"## Testing it out\n",
|
||||||
"And now we can try actually using our retriever!"
|
"\n",
|
||||||
|
"Now you can try actually using our retriever:"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 5,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[Document(id='d7b9ec1edafa467caab524455e8c1f5d', metadata={'year': 1993, 'rating': 7.7, 'genre': 'science fiction'}, page_content='A bunch of scientists bring back dinosaurs and mayhem breaks loose'),\n",
|
||||||
|
" Document(id='8ad04ef2a73d4f74897a51e49be1a8d2', metadata={'year': 1995, 'genre': 'animated'}, page_content='Toys come alive and have a blast doing so'),\n",
|
||||||
|
" Document(id='5b07e600d3494506952b60e0a45a0546', metadata={'year': 1979, 'director': 'Andrei Tarkovsky', 'genre': 'science fiction', 'rating': 9.9}, page_content='Three men walk into the Zone, three men walk out of the Zone'),\n",
|
||||||
|
" Document(id='a0cef19e27c341929098ac4793602829', metadata={'year': 2006, 'director': 'Satoshi Kon', 'rating': 8.6}, page_content='A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 5,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# This example only specifies a relevant query\n",
|
"# This example only specifies a relevant query\n",
|
||||||
"retriever.invoke(\"What are some movies about dinosaurs?\")"
|
"retriever.invoke(\"What are some movies about dinosaurs?\")"
|
||||||
@ -198,9 +245,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 6,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[Document(id='5b07e600d3494506952b60e0a45a0546', metadata={'year': 1979, 'director': 'Andrei Tarkovsky', 'genre': 'science fiction', 'rating': 9.9}, page_content='Three men walk into the Zone, three men walk out of the Zone'),\n",
|
||||||
|
" Document(id='a0cef19e27c341929098ac4793602829', metadata={'year': 2006, 'director': 'Satoshi Kon', 'rating': 8.6}, page_content='A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 6,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# This example specifies a filter\n",
|
"# This example specifies a filter\n",
|
||||||
"retriever.invoke(\"I want to watch a movie rated higher than 8.5\")"
|
"retriever.invoke(\"I want to watch a movie rated higher than 8.5\")"
|
||||||
@ -208,9 +267,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 7,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[Document(id='0539843fd203484c9be486c2a0e2454c', metadata={'year': 2019, 'director': 'Greta Gerwig', 'rating': 8.3}, page_content='A bunch of normal-sized women are supremely wholesome and some men pine after them')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 7,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# This example only specifies a query and a filter\n",
|
"# This example only specifies a query and a filter\n",
|
||||||
"retriever.invoke(\"Has Greta Gerwig directed any movies about women\")"
|
"retriever.invoke(\"Has Greta Gerwig directed any movies about women\")"
|
||||||
@ -218,9 +288,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 8,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[Document(id='a0cef19e27c341929098ac4793602829', metadata={'year': 2006, 'director': 'Satoshi Kon', 'rating': 8.6}, page_content='A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea'),\n",
|
||||||
|
" Document(id='5b07e600d3494506952b60e0a45a0546', metadata={'year': 1979, 'director': 'Andrei Tarkovsky', 'genre': 'science fiction', 'rating': 9.9}, page_content='Three men walk into the Zone, three men walk out of the Zone')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 8,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# This example specifies a composite filter\n",
|
"# This example specifies a composite filter\n",
|
||||||
"retriever.invoke(\"What's a highly rated (above 8.5), science fiction movie ?\")"
|
"retriever.invoke(\"What's a highly rated (above 8.5), science fiction movie ?\")"
|
||||||
@ -228,9 +310,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 9,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[Document(id='8ad04ef2a73d4f74897a51e49be1a8d2', metadata={'year': 1995, 'genre': 'animated'}, page_content='Toys come alive and have a blast doing so')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 9,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# This example specifies a query and composite filter\n",
|
"# This example specifies a query and composite filter\n",
|
||||||
"retriever.invoke(\n",
|
"retriever.invoke(\n",
|
||||||
@ -242,20 +335,20 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Filter k\n",
|
"## Set a limit ('k')\n",
|
||||||
"\n",
|
"\n",
|
||||||
"We can also use the self query retriever to specify `k`: the number of documents to fetch.\n",
|
"you can also use the self-query retriever to specify `k`, the number of documents to fetch.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"We can do this by passing `enable_limit=True` to the constructor."
|
"You achieve this by passing `enable_limit=True` to the constructor."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 10,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"retriever = SelfQueryRetriever.from_llm(\n",
|
"retriever_k = SelfQueryRetriever.from_llm(\n",
|
||||||
" llm,\n",
|
" llm,\n",
|
||||||
" vectorstore,\n",
|
" vectorstore,\n",
|
||||||
" document_content_description,\n",
|
" document_content_description,\n",
|
||||||
@ -267,12 +360,24 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 11,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[Document(id='d7b9ec1edafa467caab524455e8c1f5d', metadata={'year': 1993, 'rating': 7.7, 'genre': 'science fiction'}, page_content='A bunch of scientists bring back dinosaurs and mayhem breaks loose'),\n",
|
||||||
|
" Document(id='8ad04ef2a73d4f74897a51e49be1a8d2', metadata={'year': 1995, 'genre': 'animated'}, page_content='Toys come alive and have a blast doing so')]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 11,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# This example only specifies a relevant query\n",
|
"# This example only specifies a relevant query\n",
|
||||||
"retriever.invoke(\"What are two movies about dinosaurs?\")"
|
"retriever_k.invoke(\"What are two movies about dinosaurs?\")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -293,7 +398,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 12,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false,
|
"collapsed": false,
|
||||||
"jupyter": {
|
"jupyter": {
|
||||||
@ -322,7 +427,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.12"
|
"version": "3.12.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
@ -1,13 +1,76 @@
|
|||||||
{
|
{
|
||||||
"cells": [
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "8543d632",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"---\n",
|
||||||
|
"sidebar_label: Google Gemini\n",
|
||||||
|
"keywords: [google gemini embeddings]\n",
|
||||||
|
"---"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "afab8b36-10bb-4795-bc98-75ab2d2081bb",
|
"id": "afab8b36-10bb-4795-bc98-75ab2d2081bb",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Google Generative AI Embeddings\n",
|
"# Google Generative AI Embeddings (AI Studio & Gemini API)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Connect to Google's generative AI embeddings service using the `GoogleGenerativeAIEmbeddings` class, found in the [langchain-google-genai](https://pypi.org/project/langchain-google-genai/) package."
|
"Connect to Google's generative AI embeddings service using the `GoogleGenerativeAIEmbeddings` class, found in the [langchain-google-genai](https://pypi.org/project/langchain-google-genai/) package.\n",
|
||||||
|
"\n",
|
||||||
|
"This will help you get started with Google's Generative AI embedding models (like Gemini) using LangChain. For detailed documentation on `GoogleGenerativeAIEmbeddings` features and configuration options, please refer to the [API reference](https://python.langchain.com/v0.2/api_reference/google_genai/embeddings/langchain_google_genai.embeddings.GoogleGenerativeAIEmbeddings.html).\n",
|
||||||
|
"\n",
|
||||||
|
"## Overview\n",
|
||||||
|
"### Integration details\n",
|
||||||
|
"\n",
|
||||||
|
"import { ItemTable } from \"@theme/FeatureTables\";\n",
|
||||||
|
"\n",
|
||||||
|
"<ItemTable category=\"text_embedding\" item=\"Google Gemini\" />\n",
|
||||||
|
"\n",
|
||||||
|
"## Setup\n",
|
||||||
|
"\n",
|
||||||
|
"To access Google Generative AI embedding models you'll need to create a Google Cloud project, enable the Generative Language API, get an API key, and install the `langchain-google-genai` integration package.\n",
|
||||||
|
"\n",
|
||||||
|
"### Credentials\n",
|
||||||
|
"\n",
|
||||||
|
"To use Google Generative AI models, you must have an API key. You can create one in Google AI Studio. See the [Google documentation](https://ai.google.dev/gemini-api/docs/api-key) for instructions.\n",
|
||||||
|
"\n",
|
||||||
|
"Once you have a key, set it as an environment variable `GOOGLE_API_KEY`:\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "47652620",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import getpass\n",
|
||||||
|
"import os\n",
|
||||||
|
"\n",
|
||||||
|
"if not os.getenv(\"GOOGLE_API_KEY\"):\n",
|
||||||
|
" os.environ[\"GOOGLE_API_KEY\"] = getpass.getpass(\"Enter your Google API key: \")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "67283790",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"To enable automated tracing of your model calls, set your [LangSmith](https://docs.smith.langchain.com/) API key:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "eccf1968",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# os.environ[\"LANGSMITH_TRACING\"] = \"true\"\n",
|
||||||
|
"# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -28,28 +91,6 @@
|
|||||||
"%pip install --upgrade --quiet langchain-google-genai"
|
"%pip install --upgrade --quiet langchain-google-genai"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "25f3f88e-164e-400d-b371-9fa488baba19",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## Credentials"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"id": "ec89153f-8999-4aab-a21b-0bfba1cc3893",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"import getpass\n",
|
|
||||||
"import os\n",
|
|
||||||
"\n",
|
|
||||||
"if \"GOOGLE_API_KEY\" not in os.environ:\n",
|
|
||||||
" os.environ[\"GOOGLE_API_KEY\"] = getpass.getpass(\"Provide your Google API key here\")"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "f2437b22-e364-418a-8c13-490a026cb7b5",
|
"id": "f2437b22-e364-418a-8c13-490a026cb7b5",
|
||||||
@ -60,17 +101,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 6,
|
"execution_count": 20,
|
||||||
"id": "eedc551e-a1f3-4fd8-8d65-4e0784c4441b",
|
"id": "eedc551e-a1f3-4fd8-8d65-4e0784c4441b",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"[0.05636945, 0.0048285457, -0.0762591, -0.023642512, 0.05329321]"
|
"[-0.024917153641581535,\n",
|
||||||
|
" 0.012005362659692764,\n",
|
||||||
|
" -0.003886754624545574,\n",
|
||||||
|
" -0.05774897709488869,\n",
|
||||||
|
" 0.0020742062479257584]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 6,
|
"execution_count": 20,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -78,7 +123,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"from langchain_google_genai import GoogleGenerativeAIEmbeddings\n",
|
"from langchain_google_genai import GoogleGenerativeAIEmbeddings\n",
|
||||||
"\n",
|
"\n",
|
||||||
"embeddings = GoogleGenerativeAIEmbeddings(model=\"models/text-embedding-004\")\n",
|
"embeddings = GoogleGenerativeAIEmbeddings(model=\"models/gemini-embedding-exp-03-07\")\n",
|
||||||
"vector = embeddings.embed_query(\"hello, world!\")\n",
|
"vector = embeddings.embed_query(\"hello, world!\")\n",
|
||||||
"vector[:5]"
|
"vector[:5]"
|
||||||
]
|
]
|
||||||
@ -95,17 +140,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": 5,
|
||||||
"id": "6ec53aba-404f-4778-acd9-5d6664e79ed2",
|
"id": "6ec53aba-404f-4778-acd9-5d6664e79ed2",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"(3, 768)"
|
"(3, 3072)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 7,
|
"execution_count": 5,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -121,6 +166,56 @@
|
|||||||
"len(vectors), len(vectors[0])"
|
"len(vectors), len(vectors[0])"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "c362bfbf",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Indexing and Retrieval\n",
|
||||||
|
"\n",
|
||||||
|
"Embedding models are often used in retrieval-augmented generation (RAG) flows, both as part of indexing data as well as later retrieving it. For more detailed instructions, please see our [RAG tutorials](/docs/tutorials/).\n",
|
||||||
|
"\n",
|
||||||
|
"Below, see how to index and retrieve data using the `embeddings` object we initialized above. In this example, we will index and retrieve a sample document in the `InMemoryVectorStore`."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 21,
|
||||||
|
"id": "606a7f65",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"'LangChain is the framework for building context-aware reasoning applications'"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 21,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Create a vector store with a sample text\n",
|
||||||
|
"from langchain_core.vectorstores import InMemoryVectorStore\n",
|
||||||
|
"\n",
|
||||||
|
"text = \"LangChain is the framework for building context-aware reasoning applications\"\n",
|
||||||
|
"\n",
|
||||||
|
"vectorstore = InMemoryVectorStore.from_texts(\n",
|
||||||
|
" [text],\n",
|
||||||
|
" embedding=embeddings,\n",
|
||||||
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"# Use the vectorstore as a retriever\n",
|
||||||
|
"retriever = vectorstore.as_retriever()\n",
|
||||||
|
"\n",
|
||||||
|
"# Retrieve the most similar text\n",
|
||||||
|
"retrieved_documents = retriever.invoke(\"What is LangChain?\")\n",
|
||||||
|
"\n",
|
||||||
|
"# show the retrieved document's content\n",
|
||||||
|
"retrieved_documents[0].page_content"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "1482486f-5617-498a-8a44-1974d3212dda",
|
"id": "1482486f-5617-498a-8a44-1974d3212dda",
|
||||||
@ -129,70 +224,74 @@
|
|||||||
"## Task type\n",
|
"## Task type\n",
|
||||||
"`GoogleGenerativeAIEmbeddings` optionally support a `task_type`, which currently must be one of:\n",
|
"`GoogleGenerativeAIEmbeddings` optionally support a `task_type`, which currently must be one of:\n",
|
||||||
"\n",
|
"\n",
|
||||||
"- task_type_unspecified\n",
|
"- `SEMANTIC_SIMILARITY`: Used to generate embeddings that are optimized to assess text similarity.\n",
|
||||||
"- retrieval_query\n",
|
"- `CLASSIFICATION`: Used to generate embeddings that are optimized to classify texts according to preset labels.\n",
|
||||||
"- retrieval_document\n",
|
"- `CLUSTERING`: Used to generate embeddings that are optimized to cluster texts based on their similarities.\n",
|
||||||
"- semantic_similarity\n",
|
"- `RETRIEVAL_DOCUMENT`, `RETRIEVAL_QUERY`, `QUESTION_ANSWERING`, and `FACT_VERIFICATION`: Used to generate embeddings that are optimized for document search or information retrieval.\n",
|
||||||
"- classification\n",
|
"- `CODE_RETRIEVAL_QUERY`: Used to retrieve a code block based on a natural language query, such as sort an array or reverse a linked list. Embeddings of the code blocks are computed using `RETRIEVAL_DOCUMENT`.\n",
|
||||||
"- clustering\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"By default, we use `retrieval_document` in the `embed_documents` method and `retrieval_query` in the `embed_query` method. If you provide a task type, we will use that for all methods."
|
"By default, we use `RETRIEVAL_DOCUMENT` in the `embed_documents` method and `RETRIEVAL_QUERY` in the `embed_query` method. If you provide a task type, we will use that for all methods."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 15,
|
"execution_count": null,
|
||||||
"id": "a223bb25-2b1b-418e-a570-2f543083132e",
|
"id": "b7acc5c2",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [],
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"Note: you may need to restart the kernel to use updated packages.\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"%pip install --upgrade --quiet matplotlib scikit-learn"
|
"%pip install --upgrade --quiet matplotlib scikit-learn"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 33,
|
"execution_count": 19,
|
||||||
"id": "f1f077db-8eb4-49f7-8866-471a8528dcdb",
|
"id": "f1f077db-8eb4-49f7-8866-471a8528dcdb",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Document 1\n",
|
||||||
|
"Cosine similarity with query: 0.7892893360164779\n",
|
||||||
|
"---\n",
|
||||||
|
"Document 2\n",
|
||||||
|
"Cosine similarity with query: 0.5438283285204146\n",
|
||||||
|
"---\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
|
"from langchain_google_genai import GoogleGenerativeAIEmbeddings\n",
|
||||||
|
"from sklearn.metrics.pairwise import cosine_similarity\n",
|
||||||
|
"\n",
|
||||||
"query_embeddings = GoogleGenerativeAIEmbeddings(\n",
|
"query_embeddings = GoogleGenerativeAIEmbeddings(\n",
|
||||||
" model=\"models/embedding-001\", task_type=\"retrieval_query\"\n",
|
" model=\"models/gemini-embedding-exp-03-07\", task_type=\"RETRIEVAL_QUERY\"\n",
|
||||||
")\n",
|
")\n",
|
||||||
"doc_embeddings = GoogleGenerativeAIEmbeddings(\n",
|
"doc_embeddings = GoogleGenerativeAIEmbeddings(\n",
|
||||||
" model=\"models/embedding-001\", task_type=\"retrieval_document\"\n",
|
" model=\"models/gemini-embedding-exp-03-07\", task_type=\"RETRIEVAL_DOCUMENT\"\n",
|
||||||
")"
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"q_embed = query_embeddings.embed_query(\"What is the capital of France?\")\n",
|
||||||
|
"d_embed = doc_embeddings.embed_documents(\n",
|
||||||
|
" [\"The capital of France is Paris.\", \"Philipp is likes to eat pizza.\"]\n",
|
||||||
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"for i, d in enumerate(d_embed):\n",
|
||||||
|
" print(f\"Document {i+1}:\")\n",
|
||||||
|
" print(f\"Cosine similarity with query: {cosine_similarity([q_embed], [d])[0][0]}\")\n",
|
||||||
|
" print(\"---\")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "79bd4a5e-75ba-413c-befa-86167c938caf",
|
"id": "f45ea7b1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"All of these will be embedded with the 'retrieval_query' task set\n",
|
"## API Reference\n",
|
||||||
"```python\n",
|
"\n",
|
||||||
"query_vecs = [query_embeddings.embed_query(q) for q in [query, query_2, answer_1]]\n",
|
"For detailed documentation on `GoogleGenerativeAIEmbeddings` features and configuration options, please refer to the [API reference](https://python.langchain.com/api_reference/google_genai/embeddings/langchain_google_genai.embeddings.GoogleGenerativeAIEmbeddings.html).\n"
|
||||||
"```\n",
|
|
||||||
"All of these will be embedded with the 'retrieval_document' task set\n",
|
|
||||||
"```python\n",
|
|
||||||
"doc_vecs = [doc_embeddings.embed_query(q) for q in [query, query_2, answer_1]]\n",
|
|
||||||
"```"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "9e1fae5e-0f84-4812-89f5-7d4d71affbc1",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"In retrieval, relative distance matters. In the image above, you can see the difference in similarity scores between the \"relevant doc\" and \"simil stronger delta between the similar query and relevant doc on the latter case."
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -211,7 +310,7 @@
|
|||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3 (ipykernel)",
|
"display_name": ".venv",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "python3"
|
||||||
},
|
},
|
||||||
@ -225,7 +324,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.9.1"
|
"version": "3.9.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 1,
|
||||||
"id": "f7b3767b",
|
"id": "f7b3767b",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"tags": []
|
"tags": []
|
||||||
@ -83,40 +83,39 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": null,
|
||||||
"id": "851fee9f",
|
"id": "7f056cc3-628d-46ba-b394-ee1d89f8650a",
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
|
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"Download the README here and identify the link for LangChain tutorials: https://raw.githubusercontent.com/langchain-ai/langchain/master/README.md\n",
|
||||||
|
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||||
|
"Tool Calls:\n",
|
||||||
|
" terminal (call_mr86V0d6E9nQiJZT7Xw5fH0G)\n",
|
||||||
|
" Call ID: call_mr86V0d6E9nQiJZT7Xw5fH0G\n",
|
||||||
|
" Args:\n",
|
||||||
|
" commands: ['curl -o README.md https://raw.githubusercontent.com/langchain-ai/langchain/master/README.md']\n",
|
||||||
|
"Executing command:\n",
|
||||||
|
" ['curl -o README.md https://raw.githubusercontent.com/langchain-ai/langchain/master/README.md']\n",
|
||||||
|
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
||||||
|
"Name: terminal\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
|
" % Total % Received % Xferd Average Speed Time Time Time Current\n",
|
||||||
"\u001b[32;1m\u001b[1;3mQuestion: What is the task?\n",
|
" Dload Upload Total Spent Left Speed\n",
|
||||||
"Thought: We need to download the langchain.com webpage and extract all the URLs from it. Then we need to sort the URLs and return them.\n",
|
"100 5169 100 5169 0 0 114k 0 --:--:-- --:--:-- --:--:-- 114k\n",
|
||||||
"Action:\n",
|
"\n"
|
||||||
"```\n",
|
|
||||||
"{\n",
|
|
||||||
" \"action\": \"shell\",\n",
|
|
||||||
" \"action_input\": {\n",
|
|
||||||
" \"commands\": [\n",
|
|
||||||
" \"curl -s https://langchain.com | grep -o 'http[s]*://[^\\\" ]*' | sort\"\n",
|
|
||||||
" ]\n",
|
|
||||||
" }\n",
|
|
||||||
"}\n",
|
|
||||||
"```\n",
|
|
||||||
"\u001b[0m"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "stderr",
|
"name": "stderr",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"/Users/wfh/code/lc/lckg/langchain/tools/shell/tool.py:34: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
|
"/langchain/libs/community/langchain_community/tools/shell/tool.py:33: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
|
||||||
" warnings.warn(\n"
|
" warnings.warn(\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -124,50 +123,58 @@
|
|||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
|
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||||
|
"Tool Calls:\n",
|
||||||
|
" terminal (call_LF8TGrgS84WvUvaazYnVfib8)\n",
|
||||||
|
" Call ID: call_LF8TGrgS84WvUvaazYnVfib8\n",
|
||||||
|
" Args:\n",
|
||||||
|
" commands: [\"grep -i 'tutorial' README.md\"]\n",
|
||||||
|
"Executing command:\n",
|
||||||
|
" [\"grep -i 'tutorial' README.md\"]\n",
|
||||||
|
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
||||||
|
"Name: terminal\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Observation: \u001b[36;1m\u001b[1;3mhttps://blog.langchain.dev/\n",
|
"- [Tutorials](https://python.langchain.com/docs/tutorials/): Simple walkthroughs with\n",
|
||||||
"https://discord.gg/6adMQxSpJS\n",
|
"\n"
|
||||||
"https://docs.langchain.com/docs/\n",
|
|
||||||
"https://github.com/hwchase17/chat-langchain\n",
|
|
||||||
"https://github.com/hwchase17/langchain\n",
|
|
||||||
"https://github.com/hwchase17/langchainjs\n",
|
|
||||||
"https://github.com/sullivan-sean/chat-langchainjs\n",
|
|
||||||
"https://js.langchain.com/docs/\n",
|
|
||||||
"https://python.langchain.com/en/latest/\n",
|
|
||||||
"https://twitter.com/langchainai\n",
|
|
||||||
"\u001b[0m\n",
|
|
||||||
"Thought:\u001b[32;1m\u001b[1;3mThe URLs have been successfully extracted and sorted. We can return the list of URLs as the final answer.\n",
|
|
||||||
"Final Answer: [\"https://blog.langchain.dev/\", \"https://discord.gg/6adMQxSpJS\", \"https://docs.langchain.com/docs/\", \"https://github.com/hwchase17/chat-langchain\", \"https://github.com/hwchase17/langchain\", \"https://github.com/hwchase17/langchainjs\", \"https://github.com/sullivan-sean/chat-langchainjs\", \"https://js.langchain.com/docs/\", \"https://python.langchain.com/en/latest/\", \"https://twitter.com/langchainai\"]\u001b[0m\n",
|
|
||||||
"\n",
|
|
||||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"data": {
|
"name": "stderr",
|
||||||
"text/plain": [
|
"output_type": "stream",
|
||||||
"'[\"https://blog.langchain.dev/\", \"https://discord.gg/6adMQxSpJS\", \"https://docs.langchain.com/docs/\", \"https://github.com/hwchase17/chat-langchain\", \"https://github.com/hwchase17/langchain\", \"https://github.com/hwchase17/langchainjs\", \"https://github.com/sullivan-sean/chat-langchainjs\", \"https://js.langchain.com/docs/\", \"https://python.langchain.com/en/latest/\", \"https://twitter.com/langchainai\"]'"
|
"text": [
|
||||||
|
"/langchain/libs/community/langchain_community/tools/shell/tool.py:33: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
|
||||||
|
" warnings.warn(\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 3,
|
{
|
||||||
"metadata": {},
|
"name": "stdout",
|
||||||
"output_type": "execute_result"
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||||
|
"\n",
|
||||||
|
"The link for LangChain tutorials in the README is: https://python.langchain.com/docs/tutorials/\n"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain.agents import AgentType, initialize_agent\n",
|
"from langgraph.prebuilt import create_react_agent\n",
|
||||||
"from langchain_openai import ChatOpenAI\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"llm = ChatOpenAI(temperature=0)\n",
|
"tools = [shell_tool]\n",
|
||||||
|
"agent = create_react_agent(\"openai:gpt-4.1-mini\", tools)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"shell_tool.description = shell_tool.description + f\"args {shell_tool.args}\".replace(\n",
|
"input_message = {\n",
|
||||||
" \"{\", \"{{\"\n",
|
" \"role\": \"user\",\n",
|
||||||
").replace(\"}\", \"}}\")\n",
|
" \"content\": (\n",
|
||||||
"self_ask_with_search = initialize_agent(\n",
|
" \"Download the README here and identify the link for LangChain tutorials: \"\n",
|
||||||
" [shell_tool], llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True\n",
|
" \"https://raw.githubusercontent.com/langchain-ai/langchain/master/README.md\"\n",
|
||||||
")\n",
|
" ),\n",
|
||||||
"self_ask_with_search.run(\n",
|
"}\n",
|
||||||
" \"Download the langchain.com webpage and grep for all urls. Return only a sorted list of them. Be sure to use double quotes.\"\n",
|
"\n",
|
||||||
")"
|
"for step in agent.stream(\n",
|
||||||
|
" {\"messages\": [input_message]},\n",
|
||||||
|
" stream_mode=\"values\",\n",
|
||||||
|
"):\n",
|
||||||
|
" step[\"messages\"][-1].pretty_print()"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -195,7 +202,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.12"
|
"version": "3.10.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
@ -6,18 +6,16 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"# SAP HANA Cloud Vector Engine\n",
|
"# SAP HANA Cloud Vector Engine\n",
|
||||||
"\n",
|
"\n",
|
||||||
">[SAP HANA Cloud Vector Engine](https://www.sap.com/events/teched/news-guide/ai.html#article8) is a vector store fully integrated into the `SAP HANA Cloud` database.\n",
|
">[SAP HANA Cloud Vector Engine](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-vector-engine-guide/sap-hana-cloud-sap-hana-database-vector-engine-guide) is a vector store fully integrated into the `SAP HANA Cloud` database."
|
||||||
"\n",
|
|
||||||
"You'll need to install `langchain-community` with `pip install -qU langchain-community` to use this integration"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Setting up\n",
|
"## Setup\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Installation of the HANA database driver."
|
"Install the `langchain-hana` external integration package, as well as the other packages used throughout this notebook."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -26,53 +24,36 @@
|
|||||||
"metadata": {
|
"metadata": {
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Note: you may need to restart the kernel to use updated packages.\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"# Pip install necessary package\n",
|
"%pip install -qU langchain-hana"
|
||||||
"%pip install --upgrade --quiet hdbcli"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"For `OpenAIEmbeddings` we use the OpenAI API key from the environment."
|
"### Credentials\n",
|
||||||
|
"\n",
|
||||||
|
"Ensure your SAP HANA instance is running. Load your credentials from environment variables and create a connection:"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 2,
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2023-09-09T08:02:16.802456Z",
|
|
||||||
"start_time": "2023-09-09T08:02:07.065604Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"import os\n",
|
"import os\n",
|
||||||
"# Use OPENAI_API_KEY env variable\n",
|
"\n",
|
||||||
"# os.environ[\"OPENAI_API_KEY\"] = \"Your OpenAI API key\""
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"Create a database connection to a HANA Cloud instance."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 9,
|
|
||||||
"metadata": {
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2023-09-09T08:02:28.174088Z",
|
|
||||||
"start_time": "2023-09-09T08:02:28.162698Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"from dotenv import load_dotenv\n",
|
"from dotenv import load_dotenv\n",
|
||||||
"from hdbcli import dbapi\n",
|
"from hdbcli import dbapi\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -88,6 +69,92 @@
|
|||||||
")"
|
")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Learn more about SAP HANA in [What is SAP HANA?](https://www.sap.com/products/data-cloud/hana/what-is-sap-hana.html)."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Initialization\n",
|
||||||
|
"To initialize a `HanaDB` vector store, you need a database connection and an embedding instance. SAP HANA Cloud Vector Engine supports both external and internal embeddings."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"- #### Using External Embeddings\n",
|
||||||
|
"\n",
|
||||||
|
"import EmbeddingTabs from \"@theme/EmbeddingTabs\";\n",
|
||||||
|
"\n",
|
||||||
|
"<EmbeddingTabs/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# | output: false\n",
|
||||||
|
"# | echo: false\n",
|
||||||
|
"from langchain_openai import OpenAIEmbeddings\n",
|
||||||
|
"\n",
|
||||||
|
"embeddings = OpenAIEmbeddings(model=\"text-embedding-3-large\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"- #### Using Internal Embeddings"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Alternatively, you can compute embeddings directly in SAP HANA using its native `VECTOR_EMBEDDING()` function. To enable this, create an instance of `HanaInternalEmbeddings` with your internal model ID and pass it to `HanaDB`. Note that the `HanaInternalEmbeddings` instance is specifically designed for use with `HanaDB` and is not intended for use with other vector store implementations. For more information about internal embedding, see the [SAP HANA VECTOR_EMBEDDING Function](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-vector-engine-guide/vector-embedding-function-vector).\n",
|
||||||
|
"\n",
|
||||||
|
"> **Caution:** Ensure NLP is enabled in your SAP HANA Cloud instance."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from langchain_hana import HanaInternalEmbeddings\n",
|
||||||
|
"\n",
|
||||||
|
"embeddings = HanaInternalEmbeddings(internal_embedding_model_id=\"SAP_NEB.20240715\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Once you have your connection and embedding instance, create the vector store by passing them to `HanaDB` along with a table name for storing vectors:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from langchain_hana import HanaDB\n",
|
||||||
|
"\n",
|
||||||
|
"db = HanaDB(\n",
|
||||||
|
" embedding=embeddings, connection=connection, table_name=\"STATE_OF_THE_UNION\"\n",
|
||||||
|
")"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
@ -104,7 +171,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 10,
|
"execution_count": 6,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2023-09-09T08:02:25.452472Z",
|
"end_time": "2023-09-09T08:02:25.452472Z",
|
||||||
@ -122,40 +189,16 @@
|
|||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_community.document_loaders import TextLoader\n",
|
"from langchain_community.document_loaders import TextLoader\n",
|
||||||
"from langchain_community.vectorstores.hanavector import HanaDB\n",
|
|
||||||
"from langchain_core.documents import Document\n",
|
"from langchain_core.documents import Document\n",
|
||||||
"from langchain_openai import OpenAIEmbeddings\n",
|
"from langchain_openai import OpenAIEmbeddings\n",
|
||||||
"from langchain_text_splitters import CharacterTextSplitter\n",
|
"from langchain_text_splitters import CharacterTextSplitter\n",
|
||||||
"\n",
|
"\n",
|
||||||
"text_documents = TextLoader(\"../../how_to/state_of_the_union.txt\").load()\n",
|
"text_documents = TextLoader(\n",
|
||||||
|
" \"../../how_to/state_of_the_union.txt\", encoding=\"UTF-8\"\n",
|
||||||
|
").load()\n",
|
||||||
"text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=0)\n",
|
"text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=0)\n",
|
||||||
"text_chunks = text_splitter.split_documents(text_documents)\n",
|
"text_chunks = text_splitter.split_documents(text_documents)\n",
|
||||||
"print(f\"Number of document chunks: {len(text_chunks)}\")\n",
|
"print(f\"Number of document chunks: {len(text_chunks)}\")"
|
||||||
"\n",
|
|
||||||
"embeddings = OpenAIEmbeddings()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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": 11,
|
|
||||||
"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",
|
|
||||||
")"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -167,7 +210,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 12,
|
"execution_count": 7,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -176,7 +219,7 @@
|
|||||||
"[]"
|
"[]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 12,
|
"execution_count": 7,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -199,7 +242,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 13,
|
"execution_count": 8,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -235,7 +278,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 14,
|
"execution_count": 9,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -254,7 +297,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain_community.vectorstores.utils import DistanceStrategy\n",
|
"from langchain_hana.utils import DistanceStrategy\n",
|
||||||
"\n",
|
"\n",
|
||||||
"db = HanaDB(\n",
|
"db = HanaDB(\n",
|
||||||
" embedding=embeddings,\n",
|
" embedding=embeddings,\n",
|
||||||
@ -286,7 +329,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 15,
|
"execution_count": 10,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2023-09-09T08:05:23.276819Z",
|
"end_time": "2023-09-09T08:05:23.276819Z",
|
||||||
@ -336,7 +379,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 18,
|
"execution_count": 11,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -411,7 +454,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 19,
|
"execution_count": 12,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -420,7 +463,7 @@
|
|||||||
"True"
|
"True"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 19,
|
"execution_count": 12,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -443,7 +486,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 20,
|
"execution_count": 13,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -452,7 +495,7 @@
|
|||||||
"[]"
|
"[]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 20,
|
"execution_count": 13,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -471,7 +514,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 21,
|
"execution_count": 14,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -480,7 +523,7 @@
|
|||||||
"[]"
|
"[]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 21,
|
"execution_count": 14,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -508,7 +551,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 22,
|
"execution_count": 15,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -539,7 +582,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 23,
|
"execution_count": 16,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -578,13 +621,14 @@
|
|||||||
"| `$nin` | Not contained in a set of given values (not in) |\n",
|
"| `$nin` | Not contained in a set of given values (not in) |\n",
|
||||||
"| `$between` | Between the range of two boundary values |\n",
|
"| `$between` | Between the range of two boundary values |\n",
|
||||||
"| `$like` | Text equality based on the \"LIKE\" semantics in SQL (using \"%\" as wildcard) |\n",
|
"| `$like` | Text equality based on the \"LIKE\" semantics in SQL (using \"%\" as wildcard) |\n",
|
||||||
|
"| `$contains` | Filters documents containing a specific keyword |\n",
|
||||||
"| `$and` | Logical \"and\", supporting 2 or more operands |\n",
|
"| `$and` | Logical \"and\", supporting 2 or more operands |\n",
|
||||||
"| `$or` | Logical \"or\", supporting 2 or more operands |"
|
"| `$or` | Logical \"or\", supporting 2 or more operands |"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 24,
|
"execution_count": 17,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -592,15 +636,15 @@
|
|||||||
"docs = [\n",
|
"docs = [\n",
|
||||||
" Document(\n",
|
" Document(\n",
|
||||||
" page_content=\"First\",\n",
|
" page_content=\"First\",\n",
|
||||||
" metadata={\"name\": \"adam\", \"is_active\": True, \"id\": 1, \"height\": 10.0},\n",
|
" metadata={\"name\": \"Adam Smith\", \"is_active\": True, \"id\": 1, \"height\": 10.0},\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" Document(\n",
|
" Document(\n",
|
||||||
" page_content=\"Second\",\n",
|
" page_content=\"Second\",\n",
|
||||||
" metadata={\"name\": \"bob\", \"is_active\": False, \"id\": 2, \"height\": 5.7},\n",
|
" metadata={\"name\": \"Bob Johnson\", \"is_active\": False, \"id\": 2, \"height\": 5.7},\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" Document(\n",
|
" Document(\n",
|
||||||
" page_content=\"Third\",\n",
|
" page_content=\"Third\",\n",
|
||||||
" metadata={\"name\": \"jane\", \"is_active\": True, \"id\": 3, \"height\": 2.4},\n",
|
" metadata={\"name\": \"Jane Doe\", \"is_active\": True, \"id\": 3, \"height\": 2.4},\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
"]\n",
|
"]\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -632,7 +676,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 25,
|
"execution_count": 18,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -640,19 +684,19 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"Filter: {'id': {'$ne': 1}}\n",
|
"Filter: {'id': {'$ne': 1}}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
"{'name': 'Jane Doe', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
||||||
"{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
"Filter: {'id': {'$gt': 1}}\n",
|
"Filter: {'id': {'$gt': 1}}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
"{'name': 'Jane Doe', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
||||||
"{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
"Filter: {'id': {'$gte': 1}}\n",
|
"Filter: {'id': {'$gte': 1}}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
"{'name': 'Jane Doe', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
||||||
"{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
"Filter: {'id': {'$lt': 1}}\n",
|
"Filter: {'id': {'$lt': 1}}\n",
|
||||||
"<empty result>\n",
|
"<empty result>\n",
|
||||||
"Filter: {'id': {'$lte': 1}}\n",
|
"Filter: {'id': {'$lte': 1}}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n"
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -687,7 +731,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 26,
|
"execution_count": 19,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -695,13 +739,13 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"Filter: {'id': {'$between': (1, 2)}}\n",
|
"Filter: {'id': {'$between': (1, 2)}}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
"Filter: {'name': {'$in': ['adam', 'bob']}}\n",
|
"Filter: {'name': {'$in': ['Adam Smith', 'Bob Johnson']}}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
"Filter: {'name': {'$nin': ['adam', 'bob']}}\n",
|
"Filter: {'name': {'$nin': ['Adam Smith', 'Bob Johnson']}}\n",
|
||||||
"{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n"
|
"{'name': 'Jane Doe', 'is_active': True, 'id': 3, 'height': 2.4}\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -710,11 +754,11 @@
|
|||||||
"print(f\"Filter: {advanced_filter}\")\n",
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
||||||
"\n",
|
"\n",
|
||||||
"advanced_filter = {\"name\": {\"$in\": [\"adam\", \"bob\"]}}\n",
|
"advanced_filter = {\"name\": {\"$in\": [\"Adam Smith\", \"Bob Johnson\"]}}\n",
|
||||||
"print(f\"Filter: {advanced_filter}\")\n",
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
||||||
"\n",
|
"\n",
|
||||||
"advanced_filter = {\"name\": {\"$nin\": [\"adam\", \"bob\"]}}\n",
|
"advanced_filter = {\"name\": {\"$nin\": [\"Adam Smith\", \"Bob Johnson\"]}}\n",
|
||||||
"print(f\"Filter: {advanced_filter}\")\n",
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
||||||
]
|
]
|
||||||
@ -728,7 +772,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 27,
|
"execution_count": 20,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -736,10 +780,10 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"Filter: {'name': {'$like': 'a%'}}\n",
|
"Filter: {'name': {'$like': 'a%'}}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"<empty result>\n",
|
||||||
"Filter: {'name': {'$like': '%a%'}}\n",
|
"Filter: {'name': {'$like': '%a%'}}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
||||||
"{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n"
|
"{'name': 'Jane Doe', 'is_active': True, 'id': 3, 'height': 2.4}\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -753,6 +797,51 @@
|
|||||||
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Text filtering with `$contains`"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 21,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Filter: {'name': {'$contains': 'bob'}}\n",
|
||||||
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
|
"Filter: {'name': {'$contains': 'bo'}}\n",
|
||||||
|
"<empty result>\n",
|
||||||
|
"Filter: {'name': {'$contains': 'Adam Johnson'}}\n",
|
||||||
|
"<empty result>\n",
|
||||||
|
"Filter: {'name': {'$contains': 'Adam Smith'}}\n",
|
||||||
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"advanced_filter = {\"name\": {\"$contains\": \"bob\"}}\n",
|
||||||
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
||||||
|
"\n",
|
||||||
|
"advanced_filter = {\"name\": {\"$contains\": \"bo\"}}\n",
|
||||||
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
||||||
|
"\n",
|
||||||
|
"advanced_filter = {\"name\": {\"$contains\": \"Adam Johnson\"}}\n",
|
||||||
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
||||||
|
"\n",
|
||||||
|
"advanced_filter = {\"name\": {\"$contains\": \"Adam Smith\"}}\n",
|
||||||
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
@ -762,7 +851,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 28,
|
"execution_count": 22,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -770,14 +859,15 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"Filter: {'$or': [{'id': 1}, {'name': 'bob'}]}\n",
|
"Filter: {'$or': [{'id': 1}, {'name': 'bob'}]}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
|
||||||
"Filter: {'$and': [{'id': 1}, {'id': 2}]}\n",
|
"Filter: {'$and': [{'id': 1}, {'id': 2}]}\n",
|
||||||
"<empty result>\n",
|
"<empty result>\n",
|
||||||
"Filter: {'$or': [{'id': 1}, {'id': 2}, {'id': 3}]}\n",
|
"Filter: {'$or': [{'id': 1}, {'id': 2}, {'id': 3}]}\n",
|
||||||
"{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
"{'name': 'Adam Smith', 'is_active': True, 'id': 1, 'height': 10.0}\n",
|
||||||
"{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
"{'name': 'Jane Doe', 'is_active': True, 'id': 3, 'height': 2.4}\n",
|
||||||
"{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n"
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n",
|
||||||
|
"Filter: {'$and': [{'name': {'$contains': 'bob'}}, {'name': {'$contains': 'johnson'}}]}\n",
|
||||||
|
"{'name': 'Bob Johnson', 'is_active': False, 'id': 2, 'height': 5.7}\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -792,6 +882,12 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"advanced_filter = {\"$or\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}\n",
|
"advanced_filter = {\"$or\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}\n",
|
||||||
"print(f\"Filter: {advanced_filter}\")\n",
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))\n",
|
||||||
|
"\n",
|
||||||
|
"advanced_filter = {\n",
|
||||||
|
" \"$and\": [{\"name\": {\"$contains\": \"bob\"}}, {\"name\": {\"$contains\": \"johnson\"}}]\n",
|
||||||
|
"}\n",
|
||||||
|
"print(f\"Filter: {advanced_filter}\")\n",
|
||||||
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
"print_filter_result(db.similarity_search(\"just testing\", k=5, filter=advanced_filter))"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -804,13 +900,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 29,
|
"execution_count": 23,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain.memory import ConversationBufferMemory\n",
|
|
||||||
"from langchain_openai import ChatOpenAI\n",
|
|
||||||
"\n",
|
|
||||||
"# Access the vector DB with a new table\n",
|
"# Access the vector DB with a new table\n",
|
||||||
"db = HanaDB(\n",
|
"db = HanaDB(\n",
|
||||||
" connection=connection,\n",
|
" connection=connection,\n",
|
||||||
@ -837,7 +930,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 30,
|
"execution_count": 24,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@ -874,6 +967,8 @@
|
|||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from langchain.chains import ConversationalRetrievalChain\n",
|
"from langchain.chains import ConversationalRetrievalChain\n",
|
||||||
|
"from langchain.memory import ConversationBufferMemory\n",
|
||||||
|
"from langchain_openai import ChatOpenAI\n",
|
||||||
"\n",
|
"\n",
|
||||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo\")\n",
|
"llm = ChatOpenAI(model=\"gpt-3.5-turbo\")\n",
|
||||||
"memory = ConversationBufferMemory(\n",
|
"memory = ConversationBufferMemory(\n",
|
||||||
@ -898,7 +993,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 32,
|
"execution_count": 26,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -907,7 +1002,7 @@
|
|||||||
"text": [
|
"text": [
|
||||||
"Answer from LLM:\n",
|
"Answer from LLM:\n",
|
||||||
"================\n",
|
"================\n",
|
||||||
"The United States has set up joint patrols with Mexico and Guatemala to catch more human traffickers. This collaboration is part of the efforts to address immigration issues and secure the borders in the region.\n",
|
"The United States has set up joint patrols with Mexico and Guatemala to catch more human traffickers at the border. This collaborative effort aims to improve border security and combat illegal activities such as human trafficking.\n",
|
||||||
"================\n",
|
"================\n",
|
||||||
"Number of used source document chunks: 5\n"
|
"Number of used source document chunks: 5\n"
|
||||||
]
|
]
|
||||||
@ -954,7 +1049,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 34,
|
"execution_count": 28,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -963,12 +1058,12 @@
|
|||||||
"text": [
|
"text": [
|
||||||
"Answer from LLM:\n",
|
"Answer from LLM:\n",
|
||||||
"================\n",
|
"================\n",
|
||||||
"Mexico and Guatemala are involved in joint patrols to catch human traffickers.\n"
|
"Countries like Mexico and Guatemala are participating in joint patrols to catch human traffickers. The United States is also working with partners in South and Central America to host more refugees and secure their borders. Additionally, the U.S. is working with twenty-seven members of the European Union, as well as countries like France, Germany, Italy, the United Kingdom, Canada, Japan, Korea, Australia, New Zealand, and Switzerland.\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"question = \"What about other countries?\"\n",
|
"question = \"How many casualties were reported after that?\"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"result = qa_chain.invoke({\"question\": question})\n",
|
"result = qa_chain.invoke({\"question\": question})\n",
|
||||||
"print(\"Answer from LLM:\")\n",
|
"print(\"Answer from LLM:\")\n",
|
||||||
@ -996,7 +1091,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 35,
|
"execution_count": 29,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -1005,7 +1100,7 @@
|
|||||||
"[]"
|
"[]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 35,
|
"execution_count": 29,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@ -1038,7 +1133,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 36,
|
"execution_count": 30,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -1101,7 +1196,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 39,
|
"execution_count": 32,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -1111,7 +1206,7 @@
|
|||||||
"None\n",
|
"None\n",
|
||||||
"Some other text\n",
|
"Some other text\n",
|
||||||
"{\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\"}\n",
|
"{\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\"}\n",
|
||||||
"<memory at 0x7f5edcb18d00>\n"
|
"<memory at 0x110f856c0>\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -1168,7 +1263,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 40,
|
"execution_count": 33,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -1176,9 +1271,9 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"--------------------------------------------------------------------------------\n",
|
"--------------------------------------------------------------------------------\n",
|
||||||
"Some other text\n",
|
"Some more text\n",
|
||||||
"--------------------------------------------------------------------------------\n",
|
"--------------------------------------------------------------------------------\n",
|
||||||
"Some more text\n"
|
"Some other text\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -1214,7 +1309,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 41,
|
"execution_count": 34,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -1224,7 +1319,7 @@
|
|||||||
"Filters on this value are very performant\n",
|
"Filters on this value are very performant\n",
|
||||||
"Some other text\n",
|
"Some other text\n",
|
||||||
"{\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\", \"CUSTOMTEXT\": \"Filters on this value are very performant\"}\n",
|
"{\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\", \"CUSTOMTEXT\": \"Filters on this value are very performant\"}\n",
|
||||||
"<memory at 0x7f5edcb193c0>\n"
|
"<memory at 0x110f859c0>\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -1291,7 +1386,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 42,
|
"execution_count": 35,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@ -1299,9 +1394,9 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"--------------------------------------------------------------------------------\n",
|
"--------------------------------------------------------------------------------\n",
|
||||||
"Some other text\n",
|
"Some more text\n",
|
||||||
"--------------------------------------------------------------------------------\n",
|
"--------------------------------------------------------------------------------\n",
|
||||||
"Some more text\n"
|
"Some other text\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -1330,9 +1425,9 @@
|
|||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3 (ipykernel)",
|
"display_name": "lc3",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "your_env_name"
|
||||||
},
|
},
|
||||||
"language_info": {
|
"language_info": {
|
||||||
"codemirror_mode": {
|
"codemirror_mode": {
|
||||||
@ -1344,7 +1439,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.14"
|
"version": "3.10.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": null,
|
||||||
"id": "39f3ce3e",
|
"id": "39f3ce3e",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@ -118,15 +118,13 @@
|
|||||||
" language: str = Field(description=\"The language the text is written in\")\n",
|
" language: str = Field(description=\"The language the text is written in\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# LLM\n",
|
"# Structured LLM\n",
|
||||||
"llm = ChatOpenAI(temperature=0, model=\"gpt-4o-mini\").with_structured_output(\n",
|
"structured_llm = llm.with_structured_output(Classification)"
|
||||||
" Classification\n",
|
|
||||||
")"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": null,
|
||||||
"id": "5509b6a6",
|
"id": "5509b6a6",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@ -144,7 +142,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"inp = \"Estoy increiblemente contento de haberte conocido! Creo que seremos muy buenos amigos!\"\n",
|
"inp = \"Estoy increiblemente contento de haberte conocido! Creo que seremos muy buenos amigos!\"\n",
|
||||||
"prompt = tagging_prompt.invoke({\"input\": inp})\n",
|
"prompt = tagging_prompt.invoke({\"input\": inp})\n",
|
||||||
"response = llm.invoke(prompt)\n",
|
"response = structured_llm.invoke(prompt)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"response"
|
"response"
|
||||||
]
|
]
|
||||||
@ -159,7 +157,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 10,
|
"execution_count": null,
|
||||||
"id": "9154474c",
|
"id": "9154474c",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@ -177,7 +175,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"inp = \"Estoy muy enojado con vos! Te voy a dar tu merecido!\"\n",
|
"inp = \"Estoy muy enojado con vos! Te voy a dar tu merecido!\"\n",
|
||||||
"prompt = tagging_prompt.invoke({\"input\": inp})\n",
|
"prompt = tagging_prompt.invoke({\"input\": inp})\n",
|
||||||
"response = llm.invoke(prompt)\n",
|
"response = structured_llm.invoke(prompt)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"response.model_dump()"
|
"response.model_dump()"
|
||||||
]
|
]
|
||||||
|
@ -145,15 +145,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": null,
|
||||||
"id": "a5e490f6-35ad-455e-8ae4-2bae021583ff",
|
"id": "a5e490f6-35ad-455e-8ae4-2bae021583ff",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from typing import Optional\n",
|
|
||||||
"\n",
|
|
||||||
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
|
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
|
||||||
"from pydantic import BaseModel, Field\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"# Define a custom prompt to provide instructions and any additional context.\n",
|
"# Define a custom prompt to provide instructions and any additional context.\n",
|
||||||
"# 1) You can add examples into the prompt template to improve extraction quality\n",
|
"# 1) You can add examples into the prompt template to improve extraction quality\n",
|
||||||
|
@ -135,6 +135,13 @@ ${llmVarName} = AzureChatOpenAI(
|
|||||||
apiKeyName: "AZURE_OPENAI_API_KEY",
|
apiKeyName: "AZURE_OPENAI_API_KEY",
|
||||||
packageName: "langchain[openai]",
|
packageName: "langchain[openai]",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "google_genai",
|
||||||
|
label: "Google Gemini",
|
||||||
|
model: "gemini-2.0-flash",
|
||||||
|
apiKeyName: "GOOGLE_API_KEY",
|
||||||
|
packageName: "langchain[google-genai]",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: "google_vertexai",
|
value: "google_vertexai",
|
||||||
label: "Google Vertex",
|
label: "Google Vertex",
|
||||||
|
@ -366,6 +366,12 @@ const FEATURE_TABLES = {
|
|||||||
package: "langchain-openai",
|
package: "langchain-openai",
|
||||||
apiLink: "https://python.langchain.com/api_reference/openai/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html"
|
apiLink: "https://python.langchain.com/api_reference/openai/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Google Gemini",
|
||||||
|
link: "google-generative-ai",
|
||||||
|
package: "langchain-google-genai",
|
||||||
|
apiLink: "https://python.langchain.com/api_reference/google_genai/embeddings/langchain_google_genai.embeddings.GoogleGenerativeAIEmbeddings.html"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Together",
|
name: "Together",
|
||||||
link: "together",
|
link: "together",
|
||||||
|
1
docs/static/js/google_analytics.js
vendored
1
docs/static/js/google_analytics.js
vendored
@ -3,3 +3,4 @@ function gtag(){dataLayer.push(arguments);}
|
|||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-9B66JQQH2F');
|
gtag('config', 'G-9B66JQQH2F');
|
||||||
|
gtag('config', 'G-47WX3HKKY2');
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
httpx
|
httpx
|
||||||
grpcio
|
grpcio
|
||||||
|
aiohttp<3.11
|
||||||
|
protobuf<3.21
|
||||||
|
@ -5,7 +5,7 @@ build-backend = "pdm.backend"
|
|||||||
[project]
|
[project]
|
||||||
authors = [{ name = "Erick Friis", email = "erick@langchain.dev" }]
|
authors = [{ name = "Erick Friis", email = "erick@langchain.dev" }]
|
||||||
license = { text = "MIT" }
|
license = { text = "MIT" }
|
||||||
requires-python = "<4.0,>=3.9"
|
requires-python = ">=3.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typer[all]<1.0.0,>=0.9.0",
|
"typer[all]<1.0.0,>=0.9.0",
|
||||||
"gitpython<4,>=3",
|
"gitpython<4,>=3",
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
version = 1
|
version = 1
|
||||||
requires-python = ">=3.9, <4.0"
|
revision = 1
|
||||||
|
requires-python = ">=3.9"
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"python_full_version >= '3.12.4'",
|
"python_full_version >= '3.12.4'",
|
||||||
"python_full_version >= '3.12' and python_full_version < '3.12.4'",
|
"python_full_version < '3.12.4'",
|
||||||
"python_full_version < '3.12'",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -407,7 +407,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain"
|
name = "langchain"
|
||||||
version = "0.3.21"
|
version = "0.3.24"
|
||||||
source = { editable = "../langchain" }
|
source = { editable = "../langchain" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "async-timeout", marker = "python_full_version < '3.11'" },
|
{ name = "async-timeout", marker = "python_full_version < '3.11'" },
|
||||||
@ -438,6 +438,7 @@ requires-dist = [
|
|||||||
{ name = "langchain-mistralai", marker = "extra == 'mistralai'" },
|
{ name = "langchain-mistralai", marker = "extra == 'mistralai'" },
|
||||||
{ name = "langchain-ollama", marker = "extra == 'ollama'" },
|
{ name = "langchain-ollama", marker = "extra == 'ollama'" },
|
||||||
{ name = "langchain-openai", marker = "extra == 'openai'", editable = "../partners/openai" },
|
{ name = "langchain-openai", marker = "extra == 'openai'", editable = "../partners/openai" },
|
||||||
|
{ name = "langchain-perplexity", marker = "extra == 'perplexity'" },
|
||||||
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
||||||
{ name = "langchain-together", marker = "extra == 'together'" },
|
{ name = "langchain-together", marker = "extra == 'together'" },
|
||||||
{ name = "langchain-xai", marker = "extra == 'xai'" },
|
{ name = "langchain-xai", marker = "extra == 'xai'" },
|
||||||
@ -447,6 +448,7 @@ requires-dist = [
|
|||||||
{ name = "requests", specifier = ">=2,<3" },
|
{ name = "requests", specifier = ">=2,<3" },
|
||||||
{ name = "sqlalchemy", specifier = ">=1.4,<3" },
|
{ name = "sqlalchemy", specifier = ">=1.4,<3" },
|
||||||
]
|
]
|
||||||
|
provides-extras = ["community", "anthropic", "openai", "azure-ai", "cohere", "google-vertexai", "google-genai", "fireworks", "ollama", "together", "mistralai", "huggingface", "groq", "aws", "deepseek", "xai", "perplexity"]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
codespell = [{ name = "codespell", specifier = ">=2.2.0,<3.0.0" }]
|
codespell = [{ name = "codespell", specifier = ">=2.2.0,<3.0.0" }]
|
||||||
@ -473,7 +475,8 @@ test = [
|
|||||||
{ name = "langchain-tests", editable = "../standard-tests" },
|
{ name = "langchain-tests", editable = "../standard-tests" },
|
||||||
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
||||||
{ name = "lark", specifier = ">=1.1.5,<2.0.0" },
|
{ name = "lark", specifier = ">=1.1.5,<2.0.0" },
|
||||||
{ name = "numpy", specifier = ">=1.26.4,<3" },
|
{ name = "numpy", marker = "python_full_version < '3.13'", specifier = ">=1.26.4" },
|
||||||
|
{ name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1.0" },
|
||||||
{ name = "packaging", specifier = ">=24.2" },
|
{ name = "packaging", specifier = ">=24.2" },
|
||||||
{ name = "pandas", specifier = ">=2.0.0,<3.0.0" },
|
{ name = "pandas", specifier = ">=2.0.0,<3.0.0" },
|
||||||
{ name = "pytest", specifier = ">=8,<9" },
|
{ name = "pytest", specifier = ">=8,<9" },
|
||||||
@ -502,9 +505,10 @@ test-integration = [
|
|||||||
typing = [
|
typing = [
|
||||||
{ name = "langchain-core", editable = "../core" },
|
{ name = "langchain-core", editable = "../core" },
|
||||||
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
||||||
{ name = "mypy", specifier = ">=1.10,<2.0" },
|
{ name = "mypy", specifier = ">=1.15,<2.0" },
|
||||||
{ name = "mypy-protobuf", specifier = ">=3.0.0,<4.0.0" },
|
{ name = "mypy-protobuf", specifier = ">=3.0.0,<4.0.0" },
|
||||||
{ name = "numpy", specifier = ">=1.26.4,<3" },
|
{ name = "numpy", marker = "python_full_version < '3.13'", specifier = ">=1.26.4" },
|
||||||
|
{ name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1.0" },
|
||||||
{ name = "types-chardet", specifier = ">=5.0.4.6,<6.0.0.0" },
|
{ name = "types-chardet", specifier = ">=5.0.4.6,<6.0.0.0" },
|
||||||
{ name = "types-pytz", specifier = ">=2023.3.0.0,<2024.0.0.0" },
|
{ name = "types-pytz", specifier = ">=2023.3.0.0,<2024.0.0.0" },
|
||||||
{ name = "types-pyyaml", specifier = ">=6.0.12.2,<7.0.0.0" },
|
{ name = "types-pyyaml", specifier = ">=6.0.12.2,<7.0.0.0" },
|
||||||
@ -571,7 +575,7 @@ typing = [{ name = "langchain", editable = "../langchain" }]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-core"
|
name = "langchain-core"
|
||||||
version = "0.3.48"
|
version = "0.3.56"
|
||||||
source = { editable = "../core" }
|
source = { editable = "../core" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jsonpatch" },
|
{ name = "jsonpatch" },
|
||||||
@ -601,16 +605,18 @@ dev = [
|
|||||||
{ name = "jupyter", specifier = ">=1.0.0,<2.0.0" },
|
{ name = "jupyter", specifier = ">=1.0.0,<2.0.0" },
|
||||||
{ name = "setuptools", specifier = ">=67.6.1,<68.0.0" },
|
{ name = "setuptools", specifier = ">=67.6.1,<68.0.0" },
|
||||||
]
|
]
|
||||||
lint = [{ name = "ruff", specifier = ">=0.9.2,<1.0.0" }]
|
lint = [{ name = "ruff", specifier = ">=0.11.2,<0.12.0" }]
|
||||||
test = [
|
test = [
|
||||||
{ name = "blockbuster", specifier = "~=1.5.18" },
|
{ name = "blockbuster", specifier = "~=1.5.18" },
|
||||||
{ name = "freezegun", specifier = ">=1.2.2,<2.0.0" },
|
{ name = "freezegun", specifier = ">=1.2.2,<2.0.0" },
|
||||||
{ name = "grandalf", specifier = ">=0.8,<1.0" },
|
{ name = "grandalf", specifier = ">=0.8,<1.0" },
|
||||||
{ name = "langchain-tests", directory = "../standard-tests" },
|
{ name = "langchain-tests", directory = "../standard-tests" },
|
||||||
{ name = "numpy", marker = "python_full_version < '3.12'", specifier = ">=1.24.0,<2.0.0" },
|
{ name = "numpy", marker = "python_full_version < '3.13'", specifier = ">=1.26.4" },
|
||||||
{ name = "numpy", marker = "python_full_version >= '3.12'", specifier = ">=1.26.0,<3" },
|
{ name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1.0" },
|
||||||
{ name = "pytest", specifier = ">=8,<9" },
|
{ name = "pytest", specifier = ">=8,<9" },
|
||||||
{ name = "pytest-asyncio", specifier = ">=0.21.1,<1.0.0" },
|
{ name = "pytest-asyncio", specifier = ">=0.21.1,<1.0.0" },
|
||||||
|
{ name = "pytest-benchmark" },
|
||||||
|
{ name = "pytest-codspeed" },
|
||||||
{ name = "pytest-mock", specifier = ">=3.10.0,<4.0.0" },
|
{ name = "pytest-mock", specifier = ">=3.10.0,<4.0.0" },
|
||||||
{ name = "pytest-socket", specifier = ">=0.7.0,<1.0.0" },
|
{ name = "pytest-socket", specifier = ">=0.7.0,<1.0.0" },
|
||||||
{ name = "pytest-watcher", specifier = ">=0.3.4,<1.0.0" },
|
{ name = "pytest-watcher", specifier = ">=0.3.4,<1.0.0" },
|
||||||
@ -621,15 +627,14 @@ test = [
|
|||||||
test-integration = []
|
test-integration = []
|
||||||
typing = [
|
typing = [
|
||||||
{ name = "langchain-text-splitters", directory = "../text-splitters" },
|
{ name = "langchain-text-splitters", directory = "../text-splitters" },
|
||||||
{ name = "mypy", specifier = ">=1.10,<1.11" },
|
{ name = "mypy", specifier = ">=1.15,<1.16" },
|
||||||
{ name = "types-jinja2", specifier = ">=2.11.9,<3.0.0" },
|
|
||||||
{ name = "types-pyyaml", specifier = ">=6.0.12.2,<7.0.0.0" },
|
{ name = "types-pyyaml", specifier = ">=6.0.12.2,<7.0.0.0" },
|
||||||
{ name = "types-requests", specifier = ">=2.28.11.5,<3.0.0.0" },
|
{ name = "types-requests", specifier = ">=2.28.11.5,<3.0.0.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-text-splitters"
|
name = "langchain-text-splitters"
|
||||||
version = "0.3.7"
|
version = "0.3.8"
|
||||||
source = { editable = "../text-splitters" }
|
source = { editable = "../text-splitters" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "langchain-core" },
|
{ name = "langchain-core" },
|
||||||
@ -666,7 +671,7 @@ test-integration = [
|
|||||||
]
|
]
|
||||||
typing = [
|
typing = [
|
||||||
{ name = "lxml-stubs", specifier = ">=0.5.1,<1.0.0" },
|
{ name = "lxml-stubs", specifier = ">=0.5.1,<1.0.0" },
|
||||||
{ name = "mypy", specifier = ">=1.10,<2.0" },
|
{ name = "mypy", specifier = ">=1.15,<2.0" },
|
||||||
{ name = "tiktoken", specifier = ">=0.8.0,<1.0.0" },
|
{ name = "tiktoken", specifier = ">=0.8.0,<1.0.0" },
|
||||||
{ name = "types-requests", specifier = ">=2.31.0.20240218,<3.0.0.0" },
|
{ name = "types-requests", specifier = ">=2.31.0.20240218,<3.0.0.0" },
|
||||||
]
|
]
|
||||||
@ -694,19 +699,20 @@ all = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langsmith"
|
name = "langsmith"
|
||||||
version = "0.3.5"
|
version = "0.3.37"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
{ name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
|
{ name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
|
||||||
|
{ name = "packaging" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
{ name = "requests-toolbelt" },
|
{ name = "requests-toolbelt" },
|
||||||
{ name = "zstandard" },
|
{ name = "zstandard" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/2c/d8acbc61896f5fc210a3f3bc8ddf39db5213b23eaf83a755ba334be30212/langsmith-0.3.5.tar.gz", hash = "sha256:d891a205f70ab0b2c26311db6c52486ffc9fc1124238b999619445f6ae900725", size = 321847 }
|
sdist = { url = "https://files.pythonhosted.org/packages/7b/d0/98daffe57c57c2f44c5d363df5004d8e530b8c9b15751f451d273fd1d4c8/langsmith-0.3.37.tar.gz", hash = "sha256:d49d9a12d24d3984d5b3e2b5915b525b4a29a4706ea9cadde43c980fba43fab0", size = 344645 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e6/00/dbbb9df2c575217326021da731534f246dce4bb6e95b55432ff7191643ac/langsmith-0.3.5-py3-none-any.whl", hash = "sha256:29da924d2e3662dd56f96d179ebc06662b66dd0b2317362ccebe0de1b78750e7", size = 333276 },
|
{ url = "https://files.pythonhosted.org/packages/50/f2/5700dbeec7dca0aa57a6ed2f472fa3a323b46c85ab2bc446b2c7c8fb599e/langsmith-0.3.37-py3-none-any.whl", hash = "sha256:bdecca4eb48ba1799e821a33dbdca318ab202faa71a5bfa7d2358be6c3fd7eeb", size = 359308 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
.PHONY: all format lint test tests test_watch integration_tests docker_tests help extended_tests
|
|
||||||
|
|
||||||
# Default target executed when no arguments are given to make.
|
|
||||||
all: help
|
|
||||||
|
|
||||||
# Define a variable for the test file path.
|
|
||||||
TEST_FILE ?= tests/unit_tests/
|
|
||||||
integration_tests: TEST_FILE = tests/integration_tests/
|
|
||||||
|
|
||||||
.EXPORT_ALL_VARIABLES:
|
|
||||||
UV_FROZEN = true
|
|
||||||
|
|
||||||
# Run unit tests and generate a coverage report.
|
|
||||||
coverage:
|
|
||||||
uv run --group test pytest --cov \
|
|
||||||
--cov-config=.coveragerc \
|
|
||||||
--cov-report xml \
|
|
||||||
--cov-report term-missing:skip-covered \
|
|
||||||
$(TEST_FILE)
|
|
||||||
|
|
||||||
test tests:
|
|
||||||
uv run --group test pytest -n auto --disable-socket --allow-unix-socket $(TEST_FILE)
|
|
||||||
|
|
||||||
integration_tests:
|
|
||||||
uv run --group test --group test_integration pytest $(TEST_FILE)
|
|
||||||
|
|
||||||
test_watch:
|
|
||||||
uv run --group test ptw --disable-socket --allow-unix-socket --snapshot-update --now . -- -vv tests/unit_tests
|
|
||||||
|
|
||||||
check_imports: $(shell find langchain_community -name '*.py')
|
|
||||||
uv run --group test python ./scripts/check_imports.py $^
|
|
||||||
|
|
||||||
extended_tests:
|
|
||||||
uv run --no-sync --group test pytest --disable-socket --allow-unix-socket --only-extended tests/unit_tests
|
|
||||||
|
|
||||||
|
|
||||||
######################
|
|
||||||
# LINTING AND FORMATTING
|
|
||||||
######################
|
|
||||||
|
|
||||||
# Define a variable for Python and notebook files.
|
|
||||||
PYTHON_FILES=.
|
|
||||||
MYPY_CACHE=.mypy_cache
|
|
||||||
lint format: PYTHON_FILES=.
|
|
||||||
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/community --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')
|
|
||||||
lint_package: PYTHON_FILES=langchain_community
|
|
||||||
lint_tests: PYTHON_FILES=tests
|
|
||||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
|
||||||
|
|
||||||
lint lint_diff lint_package lint_tests:
|
|
||||||
./scripts/check_pydantic.sh .
|
|
||||||
./scripts/lint_imports.sh .
|
|
||||||
./scripts/check_pickle.sh .
|
|
||||||
[ "$(PYTHON_FILES)" = "" ] || uv run --group typing --group lint ruff check $(PYTHON_FILES)
|
|
||||||
[ "$(PYTHON_FILES)" = "" ] || uv run --group typing --group lint ruff format $(PYTHON_FILES) --diff
|
|
||||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && uv run --group typing --group lint mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
|
||||||
|
|
||||||
format format_diff:
|
|
||||||
[ "$(PYTHON_FILES)" = "" ] || uv run --group typing --group lint ruff format $(PYTHON_FILES)
|
|
||||||
[ "$(PYTHON_FILES)" = "" ] || uv run --group typing --group lint ruff check --select I --fix $(PYTHON_FILES)
|
|
||||||
|
|
||||||
spell_check:
|
|
||||||
uv run --group typing --group lint codespell --toml pyproject.toml
|
|
||||||
|
|
||||||
spell_fix:
|
|
||||||
uv run --group typing --group lint codespell --toml pyproject.toml -w
|
|
||||||
|
|
||||||
######################
|
|
||||||
# HELP
|
|
||||||
######################
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo '----'
|
|
||||||
@echo 'format - run code formatters'
|
|
||||||
@echo 'lint - run linters'
|
|
||||||
@echo 'test - run unit tests'
|
|
||||||
@echo 'tests - run unit tests'
|
|
||||||
@echo 'test TEST_FILE=<test_file> - run all tests in file'
|
|
||||||
@echo 'test_watch - run unit tests in watch mode'
|
|
@ -1,30 +1,3 @@
|
|||||||
# 🦜️🧑🤝🧑 LangChain Community
|
This package has moved!
|
||||||
|
|
||||||
[](https://pepy.tech/project/langchain_community)
|
https://github.com/langchain-ai/langchain-community/tree/main/libs/community
|
||||||
[](https://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
## Quick Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install langchain-community
|
|
||||||
```
|
|
||||||
|
|
||||||
## What is it?
|
|
||||||
|
|
||||||
LangChain Community contains third-party integrations that implement the base interfaces defined in LangChain Core, making them ready-to-use in any LangChain application.
|
|
||||||
|
|
||||||
For full documentation see the [API reference](https://python.langchain.com/api_reference/community/index.html).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 📕 Releases & Versioning
|
|
||||||
|
|
||||||
`langchain-community` is currently on version `0.0.x`
|
|
||||||
|
|
||||||
All changes will be accompanied by a patch version increase.
|
|
||||||
|
|
||||||
## 💁 Contributing
|
|
||||||
|
|
||||||
As an open-source project in a rapidly developing field, we are extremely open to contributions, whether it be in the form of a new feature, improved infrastructure, or better documentation.
|
|
||||||
|
|
||||||
For detailed information on how to contribute, see the [Contributing Guide](https://python.langchain.com/docs/contributing/).
|
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
aiosqlite>=0.19.0,<0.20
|
|
||||||
aleph-alpha-client>=2.15.0,<3
|
|
||||||
anthropic>=0.3.11,<0.4
|
|
||||||
arxiv>=1.4,<2
|
|
||||||
assemblyai>=0.17.0,<0.18
|
|
||||||
atlassian-python-api>=3.36.0,<4
|
|
||||||
azure-ai-documentintelligence>=1.0.0b1,<2
|
|
||||||
azure-identity>=1.15.0,<2
|
|
||||||
azure-search-documents==11.4.0
|
|
||||||
azure.ai.vision.imageanalysis>=1.0.0,<2
|
|
||||||
beautifulsoup4>=4,<5
|
|
||||||
bibtexparser>=1.4.0,<2
|
|
||||||
cassio>=0.1.6,<0.2
|
|
||||||
chardet>=5.1.0,<6
|
|
||||||
cloudpathlib>=0.18,<0.19
|
|
||||||
cloudpickle>=2.0.0
|
|
||||||
cohere>=4,<6
|
|
||||||
databricks-vectorsearch>=0.21,<0.22
|
|
||||||
datasets>=2.15.0,<3
|
|
||||||
dgml-utils>=0.3.0,<0.4
|
|
||||||
elasticsearch>=8.12.0,<9
|
|
||||||
esprima>=4.0.1,<5
|
|
||||||
faiss-cpu>=1,<2
|
|
||||||
feedparser>=6.0.10,<7
|
|
||||||
fireworks-ai>=0.9.0,<0.10
|
|
||||||
friendli-client>=1.2.4,<2
|
|
||||||
geopandas>=0.13.1
|
|
||||||
gitpython>=3.1.32,<4
|
|
||||||
gliner>=0.2.7
|
|
||||||
google-cloud-documentai>=2.20.1,<3
|
|
||||||
gql>=3.4.1,<4
|
|
||||||
gradientai>=1.4.0,<2
|
|
||||||
graphviz>=0.20.3,<0.21
|
|
||||||
hdbcli>=2.19.21,<3
|
|
||||||
hologres-vector==0.0.6
|
|
||||||
html2text>=2020.1.16
|
|
||||||
httpx>=0.24.1,<0.25
|
|
||||||
httpx-sse>=0.4.0,<0.5
|
|
||||||
jinja2>=3,<4
|
|
||||||
jq>=1.4.1,<2
|
|
||||||
jsonschema>1
|
|
||||||
keybert>=0.8.5
|
|
||||||
langchain_openai>=0.2.1
|
|
||||||
litellm>=1.30,<=1.39.5
|
|
||||||
lxml>=4.9.3,<6.0
|
|
||||||
markdownify>=0.11.6,<0.12
|
|
||||||
motor>=3.3.1,<4
|
|
||||||
msal>=1.25.0,<2
|
|
||||||
mwparserfromhell>=0.6.4,<0.7
|
|
||||||
mwxml>=0.3.3,<0.4
|
|
||||||
needle-python>=0.4
|
|
||||||
networkx>=3.2.1,<4
|
|
||||||
newspaper3k>=0.2.8,<0.3
|
|
||||||
numexpr>=2.8.6,<3
|
|
||||||
nvidia-riva-client>=2.14.0,<3
|
|
||||||
oci>=2.128.0,<3
|
|
||||||
openai<2
|
|
||||||
openapi-pydantic>=0.3.2,<0.4
|
|
||||||
oracle-ads>=2.9.1,<3
|
|
||||||
oracledb>=2.2.0,<3
|
|
||||||
pandas>=2.0.1,<3
|
|
||||||
pdfminer-six==20231228
|
|
||||||
pdfplumber>=0.11
|
|
||||||
pgvector>=0.1.6,<0.2
|
|
||||||
playwright>=1.48.0,<2
|
|
||||||
praw>=7.7.1,<8
|
|
||||||
premai>=0.3.25,<0.4,!=0.3.100
|
|
||||||
psychicapi>=0.8.0,<0.9
|
|
||||||
pydantic>=2.7.4,<3
|
|
||||||
pytesseract>=0.3.13
|
|
||||||
py-trello>=0.19.0,<0.20
|
|
||||||
pyjwt>=2.8.0,<3
|
|
||||||
pymupdf>=1.22.3,<2
|
|
||||||
pypdf>=3.4.0,<5
|
|
||||||
pypdfium2>=4.10.0,<5
|
|
||||||
rank-bm25>=0.2.2,<0.3
|
|
||||||
rapidfuzz>=3.1.1,<4
|
|
||||||
rapidocr-onnxruntime>=1.3.2,<2
|
|
||||||
rdflib==7.0.0
|
|
||||||
requests-toolbelt>=1.0.0,<2
|
|
||||||
rspace_client>=2.5.0,<3
|
|
||||||
scikit-learn>=1.2.2,<2
|
|
||||||
simsimd>=5.0.0,<6
|
|
||||||
sqlite-vss>=0.1.2,<0.2
|
|
||||||
sqlite-vec>=0.1.0,<0.2
|
|
||||||
sseclient-py>=1.8.0,<2
|
|
||||||
streamlit>=1.18.0,<2
|
|
||||||
sympy>=1.12,<2
|
|
||||||
telethon>=1.28.5,<2
|
|
||||||
tidb-vector>=0.0.3,<1.0.0
|
|
||||||
timescale-vector==0.0.1
|
|
||||||
tqdm>=4.48.0
|
|
||||||
tiktoken>=0.8.0
|
|
||||||
tree-sitter>=0.20.2,<0.21
|
|
||||||
tree-sitter-languages>=1.8.0,<2
|
|
||||||
upstash-redis>=1.1.0,<2
|
|
||||||
upstash-ratelimit>=1.1.0,<2
|
|
||||||
vdms>=0.0.20
|
|
||||||
xata>=1.0.0a7,<2
|
|
||||||
xmltodict>=0.13.0,<0.14
|
|
||||||
nanopq==0.2.1
|
|
||||||
mlflow[genai]>=2.14.0
|
|
||||||
databricks-sdk>=0.30.0
|
|
||||||
websocket>=0.2.1,<1
|
|
||||||
writer-sdk>=1.2.0
|
|
||||||
yandexcloud==0.144.0
|
|
||||||
unstructured[pdf]>=0.15
|
|
@ -1,10 +0,0 @@
|
|||||||
"""Main entrypoint into package."""
|
|
||||||
|
|
||||||
from importlib import metadata
|
|
||||||
|
|
||||||
try:
|
|
||||||
__version__ = metadata.version(__package__)
|
|
||||||
except metadata.PackageNotFoundError:
|
|
||||||
# Case where package metadata is not available.
|
|
||||||
__version__ = ""
|
|
||||||
del metadata # optional, avoids polluting the results of dir(__package__)
|
|
@ -1,8 +0,0 @@
|
|||||||
"""**Adapters** are used to adapt LangChain models to other APIs.
|
|
||||||
|
|
||||||
LangChain integrates with many model providers.
|
|
||||||
While LangChain has its own message and model APIs,
|
|
||||||
LangChain has also made it as easy as
|
|
||||||
possible to explore other models by exposing an **adapter** to adapt LangChain
|
|
||||||
models to the other APIs, as to the OpenAI API.
|
|
||||||
"""
|
|
@ -1,421 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import importlib
|
|
||||||
from typing import (
|
|
||||||
Any,
|
|
||||||
AsyncIterator,
|
|
||||||
Dict,
|
|
||||||
Iterable,
|
|
||||||
List,
|
|
||||||
Mapping,
|
|
||||||
Sequence,
|
|
||||||
Union,
|
|
||||||
overload,
|
|
||||||
)
|
|
||||||
|
|
||||||
from langchain_core.chat_sessions import ChatSession
|
|
||||||
from langchain_core.messages import (
|
|
||||||
AIMessage,
|
|
||||||
AIMessageChunk,
|
|
||||||
BaseMessage,
|
|
||||||
BaseMessageChunk,
|
|
||||||
ChatMessage,
|
|
||||||
FunctionMessage,
|
|
||||||
HumanMessage,
|
|
||||||
SystemMessage,
|
|
||||||
ToolMessage,
|
|
||||||
)
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing_extensions import Literal
|
|
||||||
|
|
||||||
|
|
||||||
async def aenumerate(
|
|
||||||
iterable: AsyncIterator[Any], start: int = 0
|
|
||||||
) -> AsyncIterator[tuple[int, Any]]:
|
|
||||||
"""Async version of enumerate function."""
|
|
||||||
i = start
|
|
||||||
async for x in iterable:
|
|
||||||
yield i, x
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
|
|
||||||
class IndexableBaseModel(BaseModel):
|
|
||||||
"""Allows a BaseModel to return its fields by string variable indexing."""
|
|
||||||
|
|
||||||
def __getitem__(self, item: str) -> Any:
|
|
||||||
return getattr(self, item)
|
|
||||||
|
|
||||||
|
|
||||||
class Choice(IndexableBaseModel):
|
|
||||||
"""Choice."""
|
|
||||||
|
|
||||||
message: dict
|
|
||||||
|
|
||||||
|
|
||||||
class ChatCompletions(IndexableBaseModel):
|
|
||||||
"""Chat completions."""
|
|
||||||
|
|
||||||
choices: List[Choice]
|
|
||||||
|
|
||||||
|
|
||||||
class ChoiceChunk(IndexableBaseModel):
|
|
||||||
"""Choice chunk."""
|
|
||||||
|
|
||||||
delta: dict
|
|
||||||
|
|
||||||
|
|
||||||
class ChatCompletionChunk(IndexableBaseModel):
|
|
||||||
"""Chat completion chunk."""
|
|
||||||
|
|
||||||
choices: List[ChoiceChunk]
|
|
||||||
|
|
||||||
|
|
||||||
def convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage:
|
|
||||||
"""Convert a dictionary to a LangChain message.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
_dict: The dictionary.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The LangChain message.
|
|
||||||
"""
|
|
||||||
role = _dict.get("role")
|
|
||||||
if role == "user":
|
|
||||||
return HumanMessage(content=_dict.get("content", ""))
|
|
||||||
elif role == "assistant":
|
|
||||||
# Fix for azure
|
|
||||||
# Also OpenAI returns None for tool invocations
|
|
||||||
content = _dict.get("content", "") or ""
|
|
||||||
additional_kwargs: Dict = {}
|
|
||||||
if function_call := _dict.get("function_call"):
|
|
||||||
additional_kwargs["function_call"] = dict(function_call)
|
|
||||||
if tool_calls := _dict.get("tool_calls"):
|
|
||||||
additional_kwargs["tool_calls"] = tool_calls
|
|
||||||
if context := _dict.get("context"):
|
|
||||||
additional_kwargs["context"] = context
|
|
||||||
return AIMessage(content=content, additional_kwargs=additional_kwargs)
|
|
||||||
elif role == "system":
|
|
||||||
return SystemMessage(content=_dict.get("content", ""))
|
|
||||||
elif role == "function":
|
|
||||||
return FunctionMessage(content=_dict.get("content", ""), name=_dict.get("name")) # type: ignore[arg-type]
|
|
||||||
elif role == "tool":
|
|
||||||
additional_kwargs = {}
|
|
||||||
if "name" in _dict:
|
|
||||||
additional_kwargs["name"] = _dict["name"]
|
|
||||||
return ToolMessage(
|
|
||||||
content=_dict.get("content", ""),
|
|
||||||
tool_call_id=_dict.get("tool_call_id"), # type: ignore[arg-type]
|
|
||||||
additional_kwargs=additional_kwargs,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return ChatMessage(content=_dict.get("content", ""), role=role) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
|
|
||||||
def convert_message_to_dict(message: BaseMessage) -> dict:
|
|
||||||
"""Convert a LangChain message to a dictionary.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message: The LangChain message.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The dictionary.
|
|
||||||
"""
|
|
||||||
message_dict: Dict[str, Any]
|
|
||||||
if isinstance(message, ChatMessage):
|
|
||||||
message_dict = {"role": message.role, "content": message.content}
|
|
||||||
elif isinstance(message, HumanMessage):
|
|
||||||
message_dict = {"role": "user", "content": message.content}
|
|
||||||
elif isinstance(message, AIMessage):
|
|
||||||
message_dict = {"role": "assistant", "content": message.content}
|
|
||||||
if "function_call" in message.additional_kwargs:
|
|
||||||
message_dict["function_call"] = message.additional_kwargs["function_call"]
|
|
||||||
# If function call only, content is None not empty string
|
|
||||||
if message_dict["content"] == "":
|
|
||||||
message_dict["content"] = None
|
|
||||||
if "tool_calls" in message.additional_kwargs:
|
|
||||||
message_dict["tool_calls"] = message.additional_kwargs["tool_calls"]
|
|
||||||
# If tool calls only, content is None not empty string
|
|
||||||
if message_dict["content"] == "":
|
|
||||||
message_dict["content"] = None
|
|
||||||
if "context" in message.additional_kwargs:
|
|
||||||
message_dict["context"] = message.additional_kwargs["context"]
|
|
||||||
# If context only, content is None not empty string
|
|
||||||
if message_dict["content"] == "":
|
|
||||||
message_dict["content"] = None
|
|
||||||
elif isinstance(message, SystemMessage):
|
|
||||||
message_dict = {"role": "system", "content": message.content}
|
|
||||||
elif isinstance(message, FunctionMessage):
|
|
||||||
message_dict = {
|
|
||||||
"role": "function",
|
|
||||||
"content": message.content,
|
|
||||||
"name": message.name,
|
|
||||||
}
|
|
||||||
elif isinstance(message, ToolMessage):
|
|
||||||
message_dict = {
|
|
||||||
"role": "tool",
|
|
||||||
"content": message.content,
|
|
||||||
"tool_call_id": message.tool_call_id,
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise TypeError(f"Got unknown type {message}")
|
|
||||||
if "name" in message.additional_kwargs:
|
|
||||||
message_dict["name"] = message.additional_kwargs["name"]
|
|
||||||
return message_dict
|
|
||||||
|
|
||||||
|
|
||||||
def convert_openai_messages(messages: Sequence[Dict[str, Any]]) -> List[BaseMessage]:
|
|
||||||
"""Convert dictionaries representing OpenAI messages to LangChain format.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
messages: List of dictionaries representing OpenAI messages
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of LangChain BaseMessage objects.
|
|
||||||
"""
|
|
||||||
return [convert_dict_to_message(m) for m in messages]
|
|
||||||
|
|
||||||
|
|
||||||
def _convert_message_chunk(chunk: BaseMessageChunk, i: int) -> dict:
|
|
||||||
_dict: Dict[str, Any] = {}
|
|
||||||
if isinstance(chunk, AIMessageChunk):
|
|
||||||
if i == 0:
|
|
||||||
# Only shows up in the first chunk
|
|
||||||
_dict["role"] = "assistant"
|
|
||||||
if "function_call" in chunk.additional_kwargs:
|
|
||||||
_dict["function_call"] = chunk.additional_kwargs["function_call"]
|
|
||||||
# If the first chunk is a function call, the content is not empty string,
|
|
||||||
# not missing, but None.
|
|
||||||
if i == 0:
|
|
||||||
_dict["content"] = None
|
|
||||||
if "tool_calls" in chunk.additional_kwargs:
|
|
||||||
_dict["tool_calls"] = chunk.additional_kwargs["tool_calls"]
|
|
||||||
# If the first chunk is tool calls, the content is not empty string,
|
|
||||||
# not missing, but None.
|
|
||||||
if i == 0:
|
|
||||||
_dict["content"] = None
|
|
||||||
else:
|
|
||||||
_dict["content"] = chunk.content
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Got unexpected streaming chunk type: {type(chunk)}")
|
|
||||||
# This only happens at the end of streams, and OpenAI returns as empty dict
|
|
||||||
if _dict == {"content": ""}:
|
|
||||||
_dict = {}
|
|
||||||
return _dict
|
|
||||||
|
|
||||||
|
|
||||||
def _convert_message_chunk_to_delta(chunk: BaseMessageChunk, i: int) -> Dict[str, Any]:
|
|
||||||
_dict = _convert_message_chunk(chunk, i)
|
|
||||||
return {"choices": [{"delta": _dict}]}
|
|
||||||
|
|
||||||
|
|
||||||
class ChatCompletion:
|
|
||||||
"""Chat completion."""
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
def create(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[False] = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> dict: ...
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
def create(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[True],
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Iterable: ...
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Union[dict, Iterable]:
|
|
||||||
models = importlib.import_module("langchain.chat_models")
|
|
||||||
model_cls = getattr(models, provider)
|
|
||||||
model_config = model_cls(**kwargs)
|
|
||||||
converted_messages = convert_openai_messages(messages)
|
|
||||||
if not stream:
|
|
||||||
result = model_config.invoke(converted_messages)
|
|
||||||
return {"choices": [{"message": convert_message_to_dict(result)}]}
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
_convert_message_chunk_to_delta(c, i)
|
|
||||||
for i, c in enumerate(model_config.stream(converted_messages))
|
|
||||||
)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
async def acreate(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[False] = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> dict: ...
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
async def acreate(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[True],
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AsyncIterator: ...
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def acreate(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Union[dict, AsyncIterator]:
|
|
||||||
models = importlib.import_module("langchain.chat_models")
|
|
||||||
model_cls = getattr(models, provider)
|
|
||||||
model_config = model_cls(**kwargs)
|
|
||||||
converted_messages = convert_openai_messages(messages)
|
|
||||||
if not stream:
|
|
||||||
result = await model_config.ainvoke(converted_messages)
|
|
||||||
return {"choices": [{"message": convert_message_to_dict(result)}]}
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
_convert_message_chunk_to_delta(c, i)
|
|
||||||
async for i, c in aenumerate(model_config.astream(converted_messages))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _has_assistant_message(session: ChatSession) -> bool:
|
|
||||||
"""Check if chat session has an assistant message."""
|
|
||||||
return any([isinstance(m, AIMessage) for m in session["messages"]])
|
|
||||||
|
|
||||||
|
|
||||||
def convert_messages_for_finetuning(
|
|
||||||
sessions: Iterable[ChatSession],
|
|
||||||
) -> List[List[dict]]:
|
|
||||||
"""Convert messages to a list of lists of dictionaries for fine-tuning.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sessions: The chat sessions.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The list of lists of dictionaries.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
[convert_message_to_dict(s) for s in session["messages"]]
|
|
||||||
for session in sessions
|
|
||||||
if _has_assistant_message(session)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Completions:
|
|
||||||
"""Completions."""
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
def create(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[False] = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> ChatCompletions: ...
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
def create(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[True],
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Iterable: ...
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Union[ChatCompletions, Iterable]:
|
|
||||||
models = importlib.import_module("langchain.chat_models")
|
|
||||||
model_cls = getattr(models, provider)
|
|
||||||
model_config = model_cls(**kwargs)
|
|
||||||
converted_messages = convert_openai_messages(messages)
|
|
||||||
if not stream:
|
|
||||||
result = model_config.invoke(converted_messages)
|
|
||||||
return ChatCompletions(
|
|
||||||
choices=[Choice(message=convert_message_to_dict(result))]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
ChatCompletionChunk(
|
|
||||||
choices=[ChoiceChunk(delta=_convert_message_chunk(c, i))]
|
|
||||||
)
|
|
||||||
for i, c in enumerate(model_config.stream(converted_messages))
|
|
||||||
)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
async def acreate(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[False] = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> ChatCompletions: ...
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@staticmethod
|
|
||||||
async def acreate(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: Literal[True],
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AsyncIterator: ...
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def acreate(
|
|
||||||
messages: Sequence[Dict[str, Any]],
|
|
||||||
*,
|
|
||||||
provider: str = "ChatOpenAI",
|
|
||||||
stream: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Union[ChatCompletions, AsyncIterator]:
|
|
||||||
models = importlib.import_module("langchain.chat_models")
|
|
||||||
model_cls = getattr(models, provider)
|
|
||||||
model_config = model_cls(**kwargs)
|
|
||||||
converted_messages = convert_openai_messages(messages)
|
|
||||||
if not stream:
|
|
||||||
result = await model_config.ainvoke(converted_messages)
|
|
||||||
return ChatCompletions(
|
|
||||||
choices=[Choice(message=convert_message_to_dict(result))]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
ChatCompletionChunk(
|
|
||||||
choices=[ChoiceChunk(delta=_convert_message_chunk(c, i))]
|
|
||||||
)
|
|
||||||
async for i, c in aenumerate(model_config.astream(converted_messages))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Chat:
|
|
||||||
"""Chat."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.completions = Completions()
|
|
||||||
|
|
||||||
|
|
||||||
chat = Chat()
|
|
@ -1,170 +0,0 @@
|
|||||||
"""**Toolkits** are sets of tools that can be used to interact with
|
|
||||||
various services and APIs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import importlib
|
|
||||||
from typing import TYPE_CHECKING, Any
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain_community.agent_toolkits.ainetwork.toolkit import (
|
|
||||||
AINetworkToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.amadeus.toolkit import (
|
|
||||||
AmadeusToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.azure_ai_services import (
|
|
||||||
AzureAiServicesToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.azure_cognitive_services import (
|
|
||||||
AzureCognitiveServicesToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.cassandra_database.toolkit import (
|
|
||||||
CassandraDatabaseToolkit, # noqa: F401
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.cogniswitch.toolkit import (
|
|
||||||
CogniswitchToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.connery import (
|
|
||||||
ConneryToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.file_management.toolkit import (
|
|
||||||
FileManagementToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.gmail.toolkit import (
|
|
||||||
GmailToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.jira.toolkit import (
|
|
||||||
JiraToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.json.base import (
|
|
||||||
create_json_agent,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.json.toolkit import (
|
|
||||||
JsonToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.multion.toolkit import (
|
|
||||||
MultionToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.nasa.toolkit import (
|
|
||||||
NasaToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.nla.toolkit import (
|
|
||||||
NLAToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.office365.toolkit import (
|
|
||||||
O365Toolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.openapi.base import (
|
|
||||||
create_openapi_agent,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.openapi.toolkit import (
|
|
||||||
OpenAPIToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.playwright.toolkit import (
|
|
||||||
PlayWrightBrowserToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.polygon.toolkit import (
|
|
||||||
PolygonToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.powerbi.base import (
|
|
||||||
create_pbi_agent,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.powerbi.chat_base import (
|
|
||||||
create_pbi_chat_agent,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.powerbi.toolkit import (
|
|
||||||
PowerBIToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.slack.toolkit import (
|
|
||||||
SlackToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.spark_sql.base import (
|
|
||||||
create_spark_sql_agent,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.spark_sql.toolkit import (
|
|
||||||
SparkSQLToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.sql.base import (
|
|
||||||
create_sql_agent,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.sql.toolkit import (
|
|
||||||
SQLDatabaseToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.steam.toolkit import (
|
|
||||||
SteamToolkit,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.zapier.toolkit import (
|
|
||||||
ZapierToolkit,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"AINetworkToolkit",
|
|
||||||
"AmadeusToolkit",
|
|
||||||
"AzureAiServicesToolkit",
|
|
||||||
"AzureCognitiveServicesToolkit",
|
|
||||||
"CogniswitchToolkit",
|
|
||||||
"ConneryToolkit",
|
|
||||||
"FileManagementToolkit",
|
|
||||||
"GmailToolkit",
|
|
||||||
"JiraToolkit",
|
|
||||||
"JsonToolkit",
|
|
||||||
"MultionToolkit",
|
|
||||||
"NLAToolkit",
|
|
||||||
"NasaToolkit",
|
|
||||||
"O365Toolkit",
|
|
||||||
"OpenAPIToolkit",
|
|
||||||
"PlayWrightBrowserToolkit",
|
|
||||||
"PolygonToolkit",
|
|
||||||
"PowerBIToolkit",
|
|
||||||
"SQLDatabaseToolkit",
|
|
||||||
"SlackToolkit",
|
|
||||||
"SparkSQLToolkit",
|
|
||||||
"SteamToolkit",
|
|
||||||
"ZapierToolkit",
|
|
||||||
"create_json_agent",
|
|
||||||
"create_openapi_agent",
|
|
||||||
"create_pbi_agent",
|
|
||||||
"create_pbi_chat_agent",
|
|
||||||
"create_spark_sql_agent",
|
|
||||||
"create_sql_agent",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
_module_lookup = {
|
|
||||||
"AINetworkToolkit": "langchain_community.agent_toolkits.ainetwork.toolkit",
|
|
||||||
"AmadeusToolkit": "langchain_community.agent_toolkits.amadeus.toolkit",
|
|
||||||
"AzureAiServicesToolkit": "langchain_community.agent_toolkits.azure_ai_services",
|
|
||||||
"AzureCognitiveServicesToolkit": "langchain_community.agent_toolkits.azure_cognitive_services", # noqa: E501
|
|
||||||
"CogniswitchToolkit": "langchain_community.agent_toolkits.cogniswitch.toolkit",
|
|
||||||
"ConneryToolkit": "langchain_community.agent_toolkits.connery",
|
|
||||||
"FileManagementToolkit": "langchain_community.agent_toolkits.file_management.toolkit", # noqa: E501
|
|
||||||
"GmailToolkit": "langchain_community.agent_toolkits.gmail.toolkit",
|
|
||||||
"JiraToolkit": "langchain_community.agent_toolkits.jira.toolkit",
|
|
||||||
"JsonToolkit": "langchain_community.agent_toolkits.json.toolkit",
|
|
||||||
"MultionToolkit": "langchain_community.agent_toolkits.multion.toolkit",
|
|
||||||
"NLAToolkit": "langchain_community.agent_toolkits.nla.toolkit",
|
|
||||||
"NasaToolkit": "langchain_community.agent_toolkits.nasa.toolkit",
|
|
||||||
"O365Toolkit": "langchain_community.agent_toolkits.office365.toolkit",
|
|
||||||
"OpenAPIToolkit": "langchain_community.agent_toolkits.openapi.toolkit",
|
|
||||||
"PlayWrightBrowserToolkit": "langchain_community.agent_toolkits.playwright.toolkit",
|
|
||||||
"PolygonToolkit": "langchain_community.agent_toolkits.polygon.toolkit",
|
|
||||||
"PowerBIToolkit": "langchain_community.agent_toolkits.powerbi.toolkit",
|
|
||||||
"SQLDatabaseToolkit": "langchain_community.agent_toolkits.sql.toolkit",
|
|
||||||
"SlackToolkit": "langchain_community.agent_toolkits.slack.toolkit",
|
|
||||||
"SparkSQLToolkit": "langchain_community.agent_toolkits.spark_sql.toolkit",
|
|
||||||
"SteamToolkit": "langchain_community.agent_toolkits.steam.toolkit",
|
|
||||||
"ZapierToolkit": "langchain_community.agent_toolkits.zapier.toolkit",
|
|
||||||
"create_json_agent": "langchain_community.agent_toolkits.json.base",
|
|
||||||
"create_openapi_agent": "langchain_community.agent_toolkits.openapi.base",
|
|
||||||
"create_pbi_agent": "langchain_community.agent_toolkits.powerbi.base",
|
|
||||||
"create_pbi_chat_agent": "langchain_community.agent_toolkits.powerbi.chat_base",
|
|
||||||
"create_spark_sql_agent": "langchain_community.agent_toolkits.spark_sql.base",
|
|
||||||
"create_sql_agent": "langchain_community.agent_toolkits.sql.base",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def __getattr__(name: str) -> Any:
|
|
||||||
if name in _module_lookup:
|
|
||||||
module = importlib.import_module(_module_lookup[name])
|
|
||||||
return getattr(module, name)
|
|
||||||
raise AttributeError(f"module {__name__} has no attribute {name}")
|
|
@ -1 +0,0 @@
|
|||||||
"""AINetwork toolkit."""
|
|
@ -1,70 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, List, Literal, Optional
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, model_validator
|
|
||||||
|
|
||||||
from langchain_community.tools.ainetwork.app import AINAppOps
|
|
||||||
from langchain_community.tools.ainetwork.owner import AINOwnerOps
|
|
||||||
from langchain_community.tools.ainetwork.rule import AINRuleOps
|
|
||||||
from langchain_community.tools.ainetwork.transfer import AINTransfer
|
|
||||||
from langchain_community.tools.ainetwork.utils import authenticate
|
|
||||||
from langchain_community.tools.ainetwork.value import AINValueOps
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ain.ain import Ain
|
|
||||||
|
|
||||||
|
|
||||||
class AINetworkToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with AINetwork Blockchain.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by reading, creating, updating, deleting
|
|
||||||
data associated with this service.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
network: Optional. The network to connect to. Default is "testnet".
|
|
||||||
Options are "mainnet" or "testnet".
|
|
||||||
interface: Optional. The interface to use. If not provided, will
|
|
||||||
attempt to authenticate with the network. Default is None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
network: Optional[Literal["mainnet", "testnet"]] = "testnet"
|
|
||||||
interface: Optional[Ain] = None
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
|
||||||
@classmethod
|
|
||||||
def set_interface(cls, values: dict) -> Any:
|
|
||||||
"""Set the interface if not provided.
|
|
||||||
|
|
||||||
If the interface is not provided, attempt to authenticate with the
|
|
||||||
network using the network value provided.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
values: The values to validate.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The validated values.
|
|
||||||
"""
|
|
||||||
if not values.get("interface"):
|
|
||||||
values["interface"] = authenticate(network=values.get("network", "testnet"))
|
|
||||||
return values
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
validate_default=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
AINAppOps(),
|
|
||||||
AINOwnerOps(),
|
|
||||||
AINRuleOps(),
|
|
||||||
AINTransfer(),
|
|
||||||
AINValueOps(),
|
|
||||||
]
|
|
@ -1,38 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.amadeus.closest_airport import AmadeusClosestAirport
|
|
||||||
from langchain_community.tools.amadeus.flight_search import AmadeusFlightSearch
|
|
||||||
from langchain_community.tools.amadeus.utils import authenticate
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from amadeus import Client
|
|
||||||
|
|
||||||
|
|
||||||
class AmadeusToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with Amadeus which offers APIs for travel.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
client: Optional. The Amadeus client. Default is None.
|
|
||||||
llm: Optional. The language model to use. Default is None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
client: Client = Field(default_factory=authenticate)
|
|
||||||
llm: Optional[BaseLanguageModel] = Field(default=None)
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
AmadeusClosestAirport(llm=self.llm),
|
|
||||||
AmadeusFlightSearch(),
|
|
||||||
]
|
|
@ -1,31 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.azure_ai_services import (
|
|
||||||
AzureAiServicesDocumentIntelligenceTool,
|
|
||||||
AzureAiServicesImageAnalysisTool,
|
|
||||||
AzureAiServicesSpeechToTextTool,
|
|
||||||
AzureAiServicesTextAnalyticsForHealthTool,
|
|
||||||
AzureAiServicesTextToSpeechTool,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AzureAiServicesToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for Azure AI Services."""
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = [
|
|
||||||
AzureAiServicesDocumentIntelligenceTool(), # type: ignore[call-arg]
|
|
||||||
AzureAiServicesImageAnalysisTool(), # type: ignore[call-arg]
|
|
||||||
AzureAiServicesSpeechToTextTool(), # type: ignore[call-arg]
|
|
||||||
AzureAiServicesTextToSpeechTool(), # type: ignore[call-arg]
|
|
||||||
AzureAiServicesTextAnalyticsForHealthTool(), # type: ignore[call-arg]
|
|
||||||
]
|
|
||||||
|
|
||||||
return tools
|
|
@ -1,34 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.azure_cognitive_services import (
|
|
||||||
AzureCogsFormRecognizerTool,
|
|
||||||
AzureCogsImageAnalysisTool,
|
|
||||||
AzureCogsSpeech2TextTool,
|
|
||||||
AzureCogsText2SpeechTool,
|
|
||||||
AzureCogsTextAnalyticsHealthTool,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AzureCognitiveServicesToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for Azure Cognitive Services."""
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = [
|
|
||||||
AzureCogsFormRecognizerTool(), # type: ignore[call-arg]
|
|
||||||
AzureCogsSpeech2TextTool(), # type: ignore[call-arg]
|
|
||||||
AzureCogsText2SpeechTool(), # type: ignore[call-arg]
|
|
||||||
AzureCogsTextAnalyticsHealthTool(), # type: ignore[call-arg]
|
|
||||||
]
|
|
||||||
|
|
||||||
# TODO: Remove check once azure-ai-vision supports MacOS.
|
|
||||||
if sys.platform.startswith("linux") or sys.platform.startswith("win"):
|
|
||||||
tools.append(AzureCogsImageAnalysisTool()) # type: ignore[call-arg]
|
|
||||||
return tools
|
|
@ -1,5 +0,0 @@
|
|||||||
"""Toolkits for agents."""
|
|
||||||
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
__all__ = ["BaseToolkit"]
|
|
@ -1 +0,0 @@
|
|||||||
"""Apache Cassandra Toolkit."""
|
|
@ -1,37 +0,0 @@
|
|||||||
"""Apache Cassandra Toolkit."""
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.cassandra_database.tool import (
|
|
||||||
GetSchemaCassandraDatabaseTool,
|
|
||||||
GetTableDataCassandraDatabaseTool,
|
|
||||||
QueryCassandraDatabaseTool,
|
|
||||||
)
|
|
||||||
from langchain_community.utilities.cassandra_database import CassandraDatabase
|
|
||||||
|
|
||||||
|
|
||||||
class CassandraDatabaseToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with an Apache Cassandra database.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
db: CassandraDatabase. The Cassandra database to interact
|
|
||||||
with.
|
|
||||||
"""
|
|
||||||
|
|
||||||
db: CassandraDatabase = Field(exclude=True)
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
GetSchemaCassandraDatabaseTool(db=self.db),
|
|
||||||
QueryCassandraDatabaseTool(db=self.db),
|
|
||||||
GetTableDataCassandraDatabaseTool(db=self.db),
|
|
||||||
]
|
|
@ -1,120 +0,0 @@
|
|||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.clickup.prompt import (
|
|
||||||
CLICKUP_FOLDER_CREATE_PROMPT,
|
|
||||||
CLICKUP_GET_ALL_TEAMS_PROMPT,
|
|
||||||
CLICKUP_GET_FOLDERS_PROMPT,
|
|
||||||
CLICKUP_GET_LIST_PROMPT,
|
|
||||||
CLICKUP_GET_SPACES_PROMPT,
|
|
||||||
CLICKUP_GET_TASK_ATTRIBUTE_PROMPT,
|
|
||||||
CLICKUP_GET_TASK_PROMPT,
|
|
||||||
CLICKUP_LIST_CREATE_PROMPT,
|
|
||||||
CLICKUP_TASK_CREATE_PROMPT,
|
|
||||||
CLICKUP_UPDATE_TASK_ASSIGNEE_PROMPT,
|
|
||||||
CLICKUP_UPDATE_TASK_PROMPT,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.clickup.tool import ClickupAction
|
|
||||||
from langchain_community.utilities.clickup import ClickupAPIWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class ClickupToolkit(BaseToolkit):
|
|
||||||
"""Clickup Toolkit.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by reading, creating, updating, deleting
|
|
||||||
data associated with this service.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools: List[BaseTool]. The tools in the toolkit. Default is an empty list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_clickup_api_wrapper(
|
|
||||||
cls, clickup_api_wrapper: ClickupAPIWrapper
|
|
||||||
) -> "ClickupToolkit":
|
|
||||||
"""Create a ClickupToolkit from a ClickupAPIWrapper.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
clickup_api_wrapper: ClickupAPIWrapper. The Clickup API wrapper.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ClickupToolkit. The Clickup toolkit.
|
|
||||||
"""
|
|
||||||
operations: List[Dict] = [
|
|
||||||
{
|
|
||||||
"mode": "get_task",
|
|
||||||
"name": "Get task",
|
|
||||||
"description": CLICKUP_GET_TASK_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_task_attribute",
|
|
||||||
"name": "Get task attribute",
|
|
||||||
"description": CLICKUP_GET_TASK_ATTRIBUTE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_teams",
|
|
||||||
"name": "Get Teams",
|
|
||||||
"description": CLICKUP_GET_ALL_TEAMS_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_task",
|
|
||||||
"name": "Create Task",
|
|
||||||
"description": CLICKUP_TASK_CREATE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_list",
|
|
||||||
"name": "Create List",
|
|
||||||
"description": CLICKUP_LIST_CREATE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_folder",
|
|
||||||
"name": "Create Folder",
|
|
||||||
"description": CLICKUP_FOLDER_CREATE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_list",
|
|
||||||
"name": "Get all lists in the space",
|
|
||||||
"description": CLICKUP_GET_LIST_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_folders",
|
|
||||||
"name": "Get all folders in the workspace",
|
|
||||||
"description": CLICKUP_GET_FOLDERS_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_spaces",
|
|
||||||
"name": "Get all spaces in the workspace",
|
|
||||||
"description": CLICKUP_GET_SPACES_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "update_task",
|
|
||||||
"name": "Update task",
|
|
||||||
"description": CLICKUP_UPDATE_TASK_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "update_task_assignees",
|
|
||||||
"name": "Update task assignees",
|
|
||||||
"description": CLICKUP_UPDATE_TASK_ASSIGNEE_PROMPT,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
tools = [
|
|
||||||
ClickupAction(
|
|
||||||
name=action["name"],
|
|
||||||
description=action["description"],
|
|
||||||
mode=action["mode"],
|
|
||||||
api_wrapper=clickup_api_wrapper,
|
|
||||||
)
|
|
||||||
for action in operations
|
|
||||||
]
|
|
||||||
return cls(tools=tools) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return self.tools
|
|
@ -1 +0,0 @@
|
|||||||
"""CogniSwitch Toolkit"""
|
|
@ -1,45 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.cogniswitch.tool import (
|
|
||||||
CogniswitchKnowledgeRequest,
|
|
||||||
CogniswitchKnowledgeSourceFile,
|
|
||||||
CogniswitchKnowledgeSourceURL,
|
|
||||||
CogniswitchKnowledgeStatus,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CogniswitchToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for CogniSwitch.
|
|
||||||
|
|
||||||
Use the toolkit to get all the tools present in the Cogniswitch and
|
|
||||||
use them to interact with your knowledge.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
cs_token: str. The Cogniswitch token.
|
|
||||||
OAI_token: str. The OpenAI API token.
|
|
||||||
apiKey: str. The Cogniswitch OAuth token.
|
|
||||||
"""
|
|
||||||
|
|
||||||
cs_token: str
|
|
||||||
OAI_token: str
|
|
||||||
apiKey: str
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
CogniswitchKnowledgeStatus(
|
|
||||||
cs_token=self.cs_token, OAI_token=self.OAI_token, apiKey=self.apiKey
|
|
||||||
),
|
|
||||||
CogniswitchKnowledgeRequest(
|
|
||||||
cs_token=self.cs_token, OAI_token=self.OAI_token, apiKey=self.apiKey
|
|
||||||
),
|
|
||||||
CogniswitchKnowledgeSourceFile(
|
|
||||||
cs_token=self.cs_token, OAI_token=self.OAI_token, apiKey=self.apiKey
|
|
||||||
),
|
|
||||||
CogniswitchKnowledgeSourceURL(
|
|
||||||
cs_token=self.cs_token, OAI_token=self.OAI_token, apiKey=self.apiKey
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
This module contains the ConneryToolkit.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .toolkit import ConneryToolkit
|
|
||||||
|
|
||||||
__all__ = ["ConneryToolkit"]
|
|
@ -1,60 +0,0 @@
|
|||||||
from typing import Any, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import model_validator
|
|
||||||
|
|
||||||
from langchain_community.tools.connery import ConneryService
|
|
||||||
|
|
||||||
|
|
||||||
class ConneryToolkit(BaseToolkit):
|
|
||||||
"""
|
|
||||||
Toolkit with a list of Connery Actions as tools.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools (List[BaseTool]): The list of Connery Actions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools: List[BaseTool]
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""
|
|
||||||
Returns the list of Connery Actions.
|
|
||||||
"""
|
|
||||||
return self.tools
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
|
||||||
@classmethod
|
|
||||||
def validate_attributes(cls, values: dict) -> Any:
|
|
||||||
"""
|
|
||||||
Validate the attributes of the ConneryToolkit class.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
values (dict): The arguments to validate.
|
|
||||||
Returns:
|
|
||||||
dict: The validated arguments.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the 'tools' attribute is not set
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not values.get("tools"):
|
|
||||||
raise ValueError("The attribute 'tools' must be set.")
|
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_instance(cls, connery_service: ConneryService) -> "ConneryToolkit":
|
|
||||||
"""
|
|
||||||
Creates a Connery Toolkit using a Connery Service.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
connery_service (ConneryService): The Connery Service
|
|
||||||
to get the list of Connery Actions.
|
|
||||||
Returns:
|
|
||||||
ConneryToolkit: The Connery Toolkit.
|
|
||||||
"""
|
|
||||||
|
|
||||||
instance = cls(tools=connery_service.list_actions()) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
return instance
|
|
@ -1,26 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from langchain_core._api.path import as_import_path
|
|
||||||
|
|
||||||
|
|
||||||
def __getattr__(name: str) -> Any:
|
|
||||||
"""Get attr name."""
|
|
||||||
|
|
||||||
if name == "create_csv_agent":
|
|
||||||
# Get directory of langchain package
|
|
||||||
HERE = Path(__file__).parents[3]
|
|
||||||
here = as_import_path(Path(__file__).parent, relative_to=HERE)
|
|
||||||
|
|
||||||
old_path = "langchain." + here + "." + name
|
|
||||||
new_path = "langchain_experimental." + here + "." + name
|
|
||||||
raise ImportError(
|
|
||||||
"This agent has been moved to langchain experiment. "
|
|
||||||
"This agent relies on python REPL tool under the hood, so to use it "
|
|
||||||
"safely please sandbox the python REPL. "
|
|
||||||
"Read https://github.com/langchain-ai/langchain/blob/master/SECURITY.md "
|
|
||||||
"and https://github.com/langchain-ai/langchain/discussions/11680"
|
|
||||||
"To keep using this code as is, install langchain experimental and "
|
|
||||||
f"update your import statement from:\n `{old_path}` to `{new_path}`."
|
|
||||||
)
|
|
||||||
raise AttributeError(f"{name} does not exist")
|
|
@ -1,7 +0,0 @@
|
|||||||
"""Local file management toolkit."""
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.file_management.toolkit import (
|
|
||||||
FileManagementToolkit,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = ["FileManagementToolkit"]
|
|
@ -1,88 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional, Type
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool, BaseToolkit
|
|
||||||
from langchain_core.utils.pydantic import get_fields
|
|
||||||
from pydantic import model_validator
|
|
||||||
|
|
||||||
from langchain_community.tools.file_management.copy import CopyFileTool
|
|
||||||
from langchain_community.tools.file_management.delete import DeleteFileTool
|
|
||||||
from langchain_community.tools.file_management.file_search import FileSearchTool
|
|
||||||
from langchain_community.tools.file_management.list_dir import ListDirectoryTool
|
|
||||||
from langchain_community.tools.file_management.move import MoveFileTool
|
|
||||||
from langchain_community.tools.file_management.read import ReadFileTool
|
|
||||||
from langchain_community.tools.file_management.write import WriteFileTool
|
|
||||||
|
|
||||||
_FILE_TOOLS: List[Type[BaseTool]] = [
|
|
||||||
CopyFileTool,
|
|
||||||
DeleteFileTool,
|
|
||||||
FileSearchTool,
|
|
||||||
MoveFileTool,
|
|
||||||
ReadFileTool,
|
|
||||||
WriteFileTool,
|
|
||||||
ListDirectoryTool,
|
|
||||||
]
|
|
||||||
_FILE_TOOLS_MAP: Dict[str, Type[BaseTool]] = {
|
|
||||||
get_fields(tool_cls)["name"].default: tool_cls for tool_cls in _FILE_TOOLS
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class FileManagementToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with local files.
|
|
||||||
|
|
||||||
*Security Notice*: This toolkit provides methods to interact with local files.
|
|
||||||
If providing this toolkit to an agent on an LLM, ensure you scope
|
|
||||||
the agent's permissions to only include the necessary permissions
|
|
||||||
to perform the desired operations.
|
|
||||||
|
|
||||||
By **default** the agent will have access to all files within
|
|
||||||
the root dir and will be able to Copy, Delete, Move, Read, Write
|
|
||||||
and List files in that directory.
|
|
||||||
|
|
||||||
Consider the following:
|
|
||||||
- Limit access to particular directories using `root_dir`.
|
|
||||||
- Use filesystem permissions to restrict access and permissions to only
|
|
||||||
the files and directories required by the agent.
|
|
||||||
- Limit the tools available to the agent to only the file operations
|
|
||||||
necessary for the agent's intended use.
|
|
||||||
- Sandbox the agent by running it in a container.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
root_dir: Optional. The root directory to perform file operations.
|
|
||||||
If not provided, file operations are performed relative to the current
|
|
||||||
working directory.
|
|
||||||
selected_tools: Optional. The tools to include in the toolkit. If not
|
|
||||||
provided, all tools are included.
|
|
||||||
"""
|
|
||||||
|
|
||||||
root_dir: Optional[str] = None
|
|
||||||
"""If specified, all file operations are made relative to root_dir."""
|
|
||||||
selected_tools: Optional[List[str]] = None
|
|
||||||
"""If provided, only provide the selected tools. Defaults to all."""
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
|
||||||
@classmethod
|
|
||||||
def validate_tools(cls, values: dict) -> Any:
|
|
||||||
selected_tools = values.get("selected_tools") or []
|
|
||||||
for tool_name in selected_tools:
|
|
||||||
if tool_name not in _FILE_TOOLS_MAP:
|
|
||||||
raise ValueError(
|
|
||||||
f"File Tool of name {tool_name} not supported."
|
|
||||||
f" Permitted tools: {list(_FILE_TOOLS_MAP)}"
|
|
||||||
)
|
|
||||||
return values
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
allowed_tools = self.selected_tools or _FILE_TOOLS_MAP
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
for tool in allowed_tools:
|
|
||||||
tool_cls = _FILE_TOOLS_MAP[tool]
|
|
||||||
tools.append(tool_cls(root_dir=self.root_dir)) # type: ignore[call-arg]
|
|
||||||
return tools
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["FileManagementToolkit"]
|
|
@ -1 +0,0 @@
|
|||||||
"""financial datasets toolkit."""
|
|
@ -1,44 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.financial_datasets.balance_sheets import BalanceSheets
|
|
||||||
from langchain_community.tools.financial_datasets.cash_flow_statements import (
|
|
||||||
CashFlowStatements,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.financial_datasets.income_statements import (
|
|
||||||
IncomeStatements,
|
|
||||||
)
|
|
||||||
from langchain_community.utilities.financial_datasets import FinancialDatasetsAPIWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class FinancialDatasetsToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with financialdatasets.ai.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
api_wrapper: The FinancialDatasets API Wrapper.
|
|
||||||
"""
|
|
||||||
|
|
||||||
api_wrapper: FinancialDatasetsAPIWrapper = Field(
|
|
||||||
default_factory=FinancialDatasetsAPIWrapper
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, api_wrapper: FinancialDatasetsAPIWrapper):
|
|
||||||
super().__init__()
|
|
||||||
self.api_wrapper = api_wrapper
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
BalanceSheets(api_wrapper=self.api_wrapper),
|
|
||||||
CashFlowStatements(api_wrapper=self.api_wrapper),
|
|
||||||
IncomeStatements(api_wrapper=self.api_wrapper),
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
"""GitHub Toolkit."""
|
|
@ -1,479 +0,0 @@
|
|||||||
"""GitHub Toolkit."""
|
|
||||||
|
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.github.prompt import (
|
|
||||||
COMMENT_ON_ISSUE_PROMPT,
|
|
||||||
CREATE_BRANCH_PROMPT,
|
|
||||||
CREATE_FILE_PROMPT,
|
|
||||||
CREATE_PULL_REQUEST_PROMPT,
|
|
||||||
CREATE_REVIEW_REQUEST_PROMPT,
|
|
||||||
DELETE_FILE_PROMPT,
|
|
||||||
GET_FILES_FROM_DIRECTORY_PROMPT,
|
|
||||||
GET_ISSUE_PROMPT,
|
|
||||||
GET_ISSUES_PROMPT,
|
|
||||||
GET_LATEST_RELEASE_PROMPT,
|
|
||||||
GET_PR_PROMPT,
|
|
||||||
GET_RELEASE_PROMPT,
|
|
||||||
GET_RELEASES_PROMPT,
|
|
||||||
LIST_BRANCHES_IN_REPO_PROMPT,
|
|
||||||
LIST_PRS_PROMPT,
|
|
||||||
LIST_PULL_REQUEST_FILES,
|
|
||||||
OVERVIEW_EXISTING_FILES_BOT_BRANCH,
|
|
||||||
OVERVIEW_EXISTING_FILES_IN_MAIN,
|
|
||||||
READ_FILE_PROMPT,
|
|
||||||
SEARCH_CODE_PROMPT,
|
|
||||||
SEARCH_ISSUES_AND_PRS_PROMPT,
|
|
||||||
SET_ACTIVE_BRANCH_PROMPT,
|
|
||||||
UPDATE_FILE_PROMPT,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.github.tool import GitHubAction
|
|
||||||
from langchain_community.utilities.github import GitHubAPIWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class NoInput(BaseModel):
|
|
||||||
"""Schema for operations that do not require any input."""
|
|
||||||
|
|
||||||
no_input: str = Field("", description="No input required, e.g. `` (empty string).")
|
|
||||||
|
|
||||||
|
|
||||||
class GetIssue(BaseModel):
|
|
||||||
"""Schema for operations that require an issue number as input."""
|
|
||||||
|
|
||||||
issue_number: int = Field(0, description="Issue number as an integer, e.g. `42`")
|
|
||||||
|
|
||||||
|
|
||||||
class CommentOnIssue(BaseModel):
|
|
||||||
"""Schema for operations that require a comment as input."""
|
|
||||||
|
|
||||||
input: str = Field(..., description="Follow the required formatting.")
|
|
||||||
|
|
||||||
|
|
||||||
class GetPR(BaseModel):
|
|
||||||
"""Schema for operations that require a PR number as input."""
|
|
||||||
|
|
||||||
pr_number: int = Field(0, description="The PR number as an integer, e.g. `12`")
|
|
||||||
|
|
||||||
|
|
||||||
class CreatePR(BaseModel):
|
|
||||||
"""Schema for operations that require a PR title and body as input."""
|
|
||||||
|
|
||||||
formatted_pr: str = Field(..., description="Follow the required formatting.")
|
|
||||||
|
|
||||||
|
|
||||||
class CreateFile(BaseModel):
|
|
||||||
"""Schema for operations that require a file path and content as input."""
|
|
||||||
|
|
||||||
formatted_file: str = Field(..., description="Follow the required formatting.")
|
|
||||||
|
|
||||||
|
|
||||||
class ReadFile(BaseModel):
|
|
||||||
"""Schema for operations that require a file path as input."""
|
|
||||||
|
|
||||||
formatted_filepath: str = Field(
|
|
||||||
...,
|
|
||||||
description=(
|
|
||||||
"The full file path of the file you would like to read where the "
|
|
||||||
"path must NOT start with a slash, e.g. `some_dir/my_file.py`."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateFile(BaseModel):
|
|
||||||
"""Schema for operations that require a file path and content as input."""
|
|
||||||
|
|
||||||
formatted_file_update: str = Field(
|
|
||||||
..., description="Strictly follow the provided rules."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteFile(BaseModel):
|
|
||||||
"""Schema for operations that require a file path as input."""
|
|
||||||
|
|
||||||
formatted_filepath: str = Field(
|
|
||||||
...,
|
|
||||||
description=(
|
|
||||||
"The full file path of the file you would like to delete"
|
|
||||||
" where the path must NOT start with a slash, e.g."
|
|
||||||
" `some_dir/my_file.py`. Only input a string,"
|
|
||||||
" not the param name."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DirectoryPath(BaseModel):
|
|
||||||
"""Schema for operations that require a directory path as input."""
|
|
||||||
|
|
||||||
input: str = Field(
|
|
||||||
"",
|
|
||||||
description=(
|
|
||||||
"The path of the directory, e.g. `some_dir/inner_dir`."
|
|
||||||
" Only input a string, do not include the parameter name."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BranchName(BaseModel):
|
|
||||||
"""Schema for operations that require a branch name as input."""
|
|
||||||
|
|
||||||
branch_name: str = Field(
|
|
||||||
..., description="The name of the branch, e.g. `my_branch`."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SearchCode(BaseModel):
|
|
||||||
"""Schema for operations that require a search query as input."""
|
|
||||||
|
|
||||||
search_query: str = Field(
|
|
||||||
...,
|
|
||||||
description=(
|
|
||||||
"A keyword-focused natural language search"
|
|
||||||
"query for code, e.g. `MyFunctionName()`."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateReviewRequest(BaseModel):
|
|
||||||
"""Schema for operations that require a username as input."""
|
|
||||||
|
|
||||||
username: str = Field(
|
|
||||||
...,
|
|
||||||
description="GitHub username of the user being requested, e.g. `my_username`.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SearchIssuesAndPRs(BaseModel):
|
|
||||||
"""Schema for operations that require a search query as input."""
|
|
||||||
|
|
||||||
search_query: str = Field(
|
|
||||||
...,
|
|
||||||
description="Natural language search query, e.g. `My issue title or topic`.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TagName(BaseModel):
|
|
||||||
"""Schema for operations that require a tag name as input."""
|
|
||||||
|
|
||||||
tag_name: str = Field(
|
|
||||||
...,
|
|
||||||
description="The tag name of the release, e.g. `v1.0.0`.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GitHubToolkit(BaseToolkit):
|
|
||||||
"""GitHub Toolkit.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by creating, deleting, or updating,
|
|
||||||
reading underlying data.
|
|
||||||
|
|
||||||
For example, this toolkit can be used to create issues, pull requests,
|
|
||||||
and comments on GitHub.
|
|
||||||
|
|
||||||
See [Security](https://python.langchain.com/docs/security) for more information.
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
See detailed installation instructions here:
|
|
||||||
https://python.langchain.com/docs/integrations/tools/github/#installation
|
|
||||||
|
|
||||||
You will need to install ``pygithub`` and set the following environment
|
|
||||||
variables:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pip install -U pygithub
|
|
||||||
export GITHUB_APP_ID="your-app-id"
|
|
||||||
export GITHUB_APP_PRIVATE_KEY="path-to-private-key"
|
|
||||||
export GITHUB_REPOSITORY="your-github-repository"
|
|
||||||
|
|
||||||
Instantiate:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.github.toolkit import GitHubToolkit
|
|
||||||
from langchain_community.utilities.github import GitHubAPIWrapper
|
|
||||||
|
|
||||||
github = GitHubAPIWrapper()
|
|
||||||
toolkit = GitHubToolkit.from_github_api_wrapper(github)
|
|
||||||
|
|
||||||
Tools:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
for tool in tools:
|
|
||||||
print(tool.name)
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
Get Issues
|
|
||||||
Get Issue
|
|
||||||
Comment on Issue
|
|
||||||
List open pull requests (PRs)
|
|
||||||
Get Pull Request
|
|
||||||
Overview of files included in PR
|
|
||||||
Create Pull Request
|
|
||||||
List Pull Requests' Files
|
|
||||||
Create File
|
|
||||||
Read File
|
|
||||||
Update File
|
|
||||||
Delete File
|
|
||||||
Overview of existing files in Main branch
|
|
||||||
Overview of files in current working branch
|
|
||||||
List branches in this repository
|
|
||||||
Set active branch
|
|
||||||
Create a new branch
|
|
||||||
Get files from a directory
|
|
||||||
Search issues and pull requests
|
|
||||||
Search code
|
|
||||||
Create review request
|
|
||||||
|
|
||||||
Include release tools:
|
|
||||||
By default, the toolkit does not include release-related tools.
|
|
||||||
You can include them by setting ``include_release_tools=True`` when
|
|
||||||
initializing the toolkit:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
toolkit = GitHubToolkit.from_github_api_wrapper(
|
|
||||||
github, include_release_tools=True
|
|
||||||
)
|
|
||||||
|
|
||||||
Setting ``include_release_tools=True`` will include the following tools:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
Get latest release
|
|
||||||
Get releases
|
|
||||||
Get release
|
|
||||||
|
|
||||||
Use within an agent:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langgraph.prebuilt import create_react_agent
|
|
||||||
|
|
||||||
# Select example tool
|
|
||||||
tools = [tool for tool in toolkit.get_tools() if tool.name == "Get Issue"]
|
|
||||||
assert len(tools) == 1
|
|
||||||
tools[0].name = "get_issue"
|
|
||||||
|
|
||||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
|
||||||
agent_executor = create_react_agent(llm, tools)
|
|
||||||
|
|
||||||
example_query = "What is the title of issue 24888?"
|
|
||||||
|
|
||||||
events = agent_executor.stream(
|
|
||||||
{"messages": [("user", example_query)]},
|
|
||||||
stream_mode="values",
|
|
||||||
)
|
|
||||||
for event in events:
|
|
||||||
event["messages"][-1].pretty_print()
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
================================[1m Human Message [0m=================================
|
|
||||||
|
|
||||||
What is the title of issue 24888?
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
Tool Calls:
|
|
||||||
get_issue (call_iSYJVaM7uchfNHOMJoVPQsOi)
|
|
||||||
Call ID: call_iSYJVaM7uchfNHOMJoVPQsOi
|
|
||||||
Args:
|
|
||||||
issue_number: 24888
|
|
||||||
=================================[1m Tool Message [0m=================================
|
|
||||||
Name: get_issue
|
|
||||||
|
|
||||||
{"number": 24888, "title": "Standardize KV-Store Docs", "body": "..."
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
|
|
||||||
The title of issue 24888 is "Standardize KV-Store Docs".
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools: List[BaseTool]. The tools in the toolkit. Default is an empty list.
|
|
||||||
""" # noqa: E501
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_github_api_wrapper(
|
|
||||||
cls, github_api_wrapper: GitHubAPIWrapper, include_release_tools: bool = False
|
|
||||||
) -> "GitHubToolkit":
|
|
||||||
"""Create a GitHubToolkit from a GitHubAPIWrapper.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
github_api_wrapper: GitHubAPIWrapper. The GitHub API wrapper.
|
|
||||||
include_release_tools: bool. Whether to include release-related tools.
|
|
||||||
Defaults to False.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
GitHubToolkit. The GitHub toolkit.
|
|
||||||
"""
|
|
||||||
operations: List[Dict] = [
|
|
||||||
{
|
|
||||||
"mode": "get_issues",
|
|
||||||
"name": "Get Issues",
|
|
||||||
"description": GET_ISSUES_PROMPT,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_issue",
|
|
||||||
"name": "Get Issue",
|
|
||||||
"description": GET_ISSUE_PROMPT,
|
|
||||||
"args_schema": GetIssue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "comment_on_issue",
|
|
||||||
"name": "Comment on Issue",
|
|
||||||
"description": COMMENT_ON_ISSUE_PROMPT,
|
|
||||||
"args_schema": CommentOnIssue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_open_pull_requests",
|
|
||||||
"name": "List open pull requests (PRs)",
|
|
||||||
"description": LIST_PRS_PROMPT,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_pull_request",
|
|
||||||
"name": "Get Pull Request",
|
|
||||||
"description": GET_PR_PROMPT,
|
|
||||||
"args_schema": GetPR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_pull_request_files",
|
|
||||||
"name": "Overview of files included in PR",
|
|
||||||
"description": LIST_PULL_REQUEST_FILES,
|
|
||||||
"args_schema": GetPR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_pull_request",
|
|
||||||
"name": "Create Pull Request",
|
|
||||||
"description": CREATE_PULL_REQUEST_PROMPT,
|
|
||||||
"args_schema": CreatePR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_pull_request_files",
|
|
||||||
"name": "List Pull Requests' Files",
|
|
||||||
"description": LIST_PULL_REQUEST_FILES,
|
|
||||||
"args_schema": GetPR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_file",
|
|
||||||
"name": "Create File",
|
|
||||||
"description": CREATE_FILE_PROMPT,
|
|
||||||
"args_schema": CreateFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "read_file",
|
|
||||||
"name": "Read File",
|
|
||||||
"description": READ_FILE_PROMPT,
|
|
||||||
"args_schema": ReadFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "update_file",
|
|
||||||
"name": "Update File",
|
|
||||||
"description": UPDATE_FILE_PROMPT,
|
|
||||||
"args_schema": UpdateFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "delete_file",
|
|
||||||
"name": "Delete File",
|
|
||||||
"description": DELETE_FILE_PROMPT,
|
|
||||||
"args_schema": DeleteFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_files_in_main_branch",
|
|
||||||
"name": "Overview of existing files in Main branch",
|
|
||||||
"description": OVERVIEW_EXISTING_FILES_IN_MAIN,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_files_in_bot_branch",
|
|
||||||
"name": "Overview of files in current working branch",
|
|
||||||
"description": OVERVIEW_EXISTING_FILES_BOT_BRANCH,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_branches_in_repo",
|
|
||||||
"name": "List branches in this repository",
|
|
||||||
"description": LIST_BRANCHES_IN_REPO_PROMPT,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "set_active_branch",
|
|
||||||
"name": "Set active branch",
|
|
||||||
"description": SET_ACTIVE_BRANCH_PROMPT,
|
|
||||||
"args_schema": BranchName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_branch",
|
|
||||||
"name": "Create a new branch",
|
|
||||||
"description": CREATE_BRANCH_PROMPT,
|
|
||||||
"args_schema": BranchName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_files_from_directory",
|
|
||||||
"name": "Get files from a directory",
|
|
||||||
"description": GET_FILES_FROM_DIRECTORY_PROMPT,
|
|
||||||
"args_schema": DirectoryPath,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "search_issues_and_prs",
|
|
||||||
"name": "Search issues and pull requests",
|
|
||||||
"description": SEARCH_ISSUES_AND_PRS_PROMPT,
|
|
||||||
"args_schema": SearchIssuesAndPRs,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "search_code",
|
|
||||||
"name": "Search code",
|
|
||||||
"description": SEARCH_CODE_PROMPT,
|
|
||||||
"args_schema": SearchCode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_review_request",
|
|
||||||
"name": "Create review request",
|
|
||||||
"description": CREATE_REVIEW_REQUEST_PROMPT,
|
|
||||||
"args_schema": CreateReviewRequest,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
release_operations: List[Dict] = [
|
|
||||||
{
|
|
||||||
"mode": "get_latest_release",
|
|
||||||
"name": "Get latest release",
|
|
||||||
"description": GET_LATEST_RELEASE_PROMPT,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_releases",
|
|
||||||
"name": "Get releases",
|
|
||||||
"description": GET_RELEASES_PROMPT,
|
|
||||||
"args_schema": NoInput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_release",
|
|
||||||
"name": "Get release",
|
|
||||||
"description": GET_RELEASE_PROMPT,
|
|
||||||
"args_schema": TagName,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = operations + (release_operations if include_release_tools else [])
|
|
||||||
tools = [
|
|
||||||
GitHubAction(
|
|
||||||
name=action["name"],
|
|
||||||
description=action["description"],
|
|
||||||
mode=action["mode"],
|
|
||||||
api_wrapper=github_api_wrapper,
|
|
||||||
args_schema=action.get("args_schema", None),
|
|
||||||
)
|
|
||||||
for action in operations
|
|
||||||
]
|
|
||||||
return cls(tools=tools) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return self.tools
|
|
@ -1 +0,0 @@
|
|||||||
"""GitLab Toolkit."""
|
|
@ -1,170 +0,0 @@
|
|||||||
"""GitLab Toolkit."""
|
|
||||||
|
|
||||||
from typing import Dict, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.gitlab.prompt import (
|
|
||||||
COMMENT_ON_ISSUE_PROMPT,
|
|
||||||
CREATE_FILE_PROMPT,
|
|
||||||
CREATE_PULL_REQUEST_PROMPT,
|
|
||||||
CREATE_REPO_BRANCH,
|
|
||||||
DELETE_FILE_PROMPT,
|
|
||||||
GET_ISSUE_PROMPT,
|
|
||||||
GET_ISSUES_PROMPT,
|
|
||||||
GET_REPO_FILES_FROM_DIRECTORY,
|
|
||||||
GET_REPO_FILES_IN_BOT_BRANCH,
|
|
||||||
GET_REPO_FILES_IN_MAIN,
|
|
||||||
LIST_REPO_BRANCES,
|
|
||||||
READ_FILE_PROMPT,
|
|
||||||
SET_ACTIVE_BRANCH,
|
|
||||||
UPDATE_FILE_PROMPT,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.gitlab.tool import GitLabAction
|
|
||||||
from langchain_community.utilities.gitlab import GitLabAPIWrapper
|
|
||||||
|
|
||||||
# only include a subset of tools by default to avoid a breaking change, where
|
|
||||||
# new tools are added to the toolkit and the user's code breaks because of
|
|
||||||
# the new tools
|
|
||||||
DEFAULT_INCLUDED_TOOLS = [
|
|
||||||
"get_issues",
|
|
||||||
"get_issue",
|
|
||||||
"comment_on_issue",
|
|
||||||
"create_pull_request",
|
|
||||||
"create_file",
|
|
||||||
"read_file",
|
|
||||||
"update_file",
|
|
||||||
"delete_file",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class GitLabToolkit(BaseToolkit):
|
|
||||||
"""GitLab Toolkit.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by creating, deleting, or updating,
|
|
||||||
reading underlying data.
|
|
||||||
|
|
||||||
For example, this toolkit can be used to create issues, pull requests,
|
|
||||||
and comments on GitLab.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools: List[BaseTool]. The tools in the toolkit. Default is an empty list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_gitlab_api_wrapper(
|
|
||||||
cls,
|
|
||||||
gitlab_api_wrapper: GitLabAPIWrapper,
|
|
||||||
*,
|
|
||||||
included_tools: Optional[List[str]] = None,
|
|
||||||
) -> "GitLabToolkit":
|
|
||||||
"""Create a GitLabToolkit from a GitLabAPIWrapper.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
gitlab_api_wrapper: GitLabAPIWrapper. The GitLab API wrapper.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
GitLabToolkit. The GitLab toolkit.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools_to_include = (
|
|
||||||
included_tools if included_tools is not None else DEFAULT_INCLUDED_TOOLS
|
|
||||||
)
|
|
||||||
|
|
||||||
operations: List[Dict] = [
|
|
||||||
{
|
|
||||||
"mode": "get_issues",
|
|
||||||
"name": "Get Issues",
|
|
||||||
"description": GET_ISSUES_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_issue",
|
|
||||||
"name": "Get Issue",
|
|
||||||
"description": GET_ISSUE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "comment_on_issue",
|
|
||||||
"name": "Comment on Issue",
|
|
||||||
"description": COMMENT_ON_ISSUE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_pull_request",
|
|
||||||
"name": "Create Pull Request",
|
|
||||||
"description": CREATE_PULL_REQUEST_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_file",
|
|
||||||
"name": "Create File",
|
|
||||||
"description": CREATE_FILE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "read_file",
|
|
||||||
"name": "Read File",
|
|
||||||
"description": READ_FILE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "update_file",
|
|
||||||
"name": "Update File",
|
|
||||||
"description": UPDATE_FILE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "delete_file",
|
|
||||||
"name": "Delete File",
|
|
||||||
"description": DELETE_FILE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_branch",
|
|
||||||
"name": "Create a new branch",
|
|
||||||
"description": CREATE_REPO_BRANCH,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_branches_in_repo",
|
|
||||||
"name": "Get the list of branches",
|
|
||||||
"description": LIST_REPO_BRANCES,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "set_active_branch",
|
|
||||||
"name": "Change the active branch",
|
|
||||||
"description": SET_ACTIVE_BRANCH,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_files_in_main_branch",
|
|
||||||
"name": "Overview of existing files in Main branch",
|
|
||||||
"description": GET_REPO_FILES_IN_MAIN,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_files_in_bot_branch",
|
|
||||||
"name": "Overview of files in current working branch",
|
|
||||||
"description": GET_REPO_FILES_IN_BOT_BRANCH,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "list_files_from_directory",
|
|
||||||
"name": "Overview of files in current working branch from a specific path", # noqa: E501
|
|
||||||
"description": GET_REPO_FILES_FROM_DIRECTORY,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
operations_filtered = [
|
|
||||||
operation
|
|
||||||
for operation in operations
|
|
||||||
if operation["mode"] in tools_to_include
|
|
||||||
]
|
|
||||||
tools = [
|
|
||||||
GitLabAction(
|
|
||||||
name=action["name"],
|
|
||||||
description=action["description"],
|
|
||||||
mode=action["mode"],
|
|
||||||
api_wrapper=gitlab_api_wrapper,
|
|
||||||
)
|
|
||||||
for action in operations_filtered
|
|
||||||
]
|
|
||||||
return cls(tools=tools) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return self.tools
|
|
@ -1 +0,0 @@
|
|||||||
"""Gmail toolkit."""
|
|
@ -1,132 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.gmail.create_draft import GmailCreateDraft
|
|
||||||
from langchain_community.tools.gmail.get_message import GmailGetMessage
|
|
||||||
from langchain_community.tools.gmail.get_thread import GmailGetThread
|
|
||||||
from langchain_community.tools.gmail.search import GmailSearch
|
|
||||||
from langchain_community.tools.gmail.send_message import GmailSendMessage
|
|
||||||
from langchain_community.tools.gmail.utils import build_resource_service
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
# This is for linting and IDE typehints
|
|
||||||
from googleapiclient.discovery import Resource
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
# We do this so pydantic can resolve the types when instantiating
|
|
||||||
from googleapiclient.discovery import Resource
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
SCOPES = ["https://mail.google.com/"]
|
|
||||||
|
|
||||||
|
|
||||||
class GmailToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with Gmail.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by reading, creating, updating, deleting
|
|
||||||
data associated with this service.
|
|
||||||
|
|
||||||
For example, this toolkit can be used to send emails on behalf of the
|
|
||||||
associated account.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
You will need a Google credentials.json file to use this toolkit.
|
|
||||||
See instructions here: https://python.langchain.com/docs/integrations/tools/gmail/#setup
|
|
||||||
|
|
||||||
Key init args:
|
|
||||||
api_resource: Optional. The Google API resource. Default is None.
|
|
||||||
|
|
||||||
Instantiate:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_google_community import GmailToolkit
|
|
||||||
|
|
||||||
toolkit = GmailToolkit()
|
|
||||||
|
|
||||||
Tools:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
toolkit.get_tools()
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
[GmailCreateDraft(api_resource=<googleapiclient.discovery.Resource object at 0x1094509d0>),
|
|
||||||
GmailSendMessage(api_resource=<googleapiclient.discovery.Resource object at 0x1094509d0>),
|
|
||||||
GmailSearch(api_resource=<googleapiclient.discovery.Resource object at 0x1094509d0>),
|
|
||||||
GmailGetMessage(api_resource=<googleapiclient.discovery.Resource object at 0x1094509d0>),
|
|
||||||
GmailGetThread(api_resource=<googleapiclient.discovery.Resource object at 0x1094509d0>)]
|
|
||||||
|
|
||||||
Use within an agent:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langgraph.prebuilt import create_react_agent
|
|
||||||
|
|
||||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
|
||||||
|
|
||||||
agent_executor = create_react_agent(llm, tools)
|
|
||||||
|
|
||||||
example_query = "Draft an email to fake@fake.com thanking them for coffee."
|
|
||||||
|
|
||||||
events = agent_executor.stream(
|
|
||||||
{"messages": [("user", example_query)]},
|
|
||||||
stream_mode="values",
|
|
||||||
)
|
|
||||||
for event in events:
|
|
||||||
event["messages"][-1].pretty_print()
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
================================[1m Human Message [0m=================================
|
|
||||||
|
|
||||||
Draft an email to fake@fake.com thanking them for coffee.
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
Tool Calls:
|
|
||||||
create_gmail_draft (call_slGkYKZKA6h3Mf1CraUBzs6M)
|
|
||||||
Call ID: call_slGkYKZKA6h3Mf1CraUBzs6M
|
|
||||||
Args:
|
|
||||||
message: Dear Fake,
|
|
||||||
|
|
||||||
I wanted to take a moment to thank you for the coffee yesterday. It was a pleasure catching up with you. Let's do it again soon!
|
|
||||||
|
|
||||||
Best regards,
|
|
||||||
[Your Name]
|
|
||||||
to: ['fake@fake.com']
|
|
||||||
subject: Thank You for the Coffee
|
|
||||||
=================================[1m Tool Message [0m=================================
|
|
||||||
Name: create_gmail_draft
|
|
||||||
|
|
||||||
Draft created. Draft Id: r-7233782721440261513
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
|
|
||||||
I have drafted an email to fake@fake.com thanking them for the coffee. You can review and send it from your email draft with the subject "Thank You for the Coffee".
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
api_resource: Optional. The Google API resource. Default is None.
|
|
||||||
""" # noqa: E501
|
|
||||||
|
|
||||||
api_resource: Resource = Field(default_factory=build_resource_service)
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
GmailCreateDraft(api_resource=self.api_resource),
|
|
||||||
GmailSendMessage(api_resource=self.api_resource),
|
|
||||||
GmailSearch(api_resource=self.api_resource),
|
|
||||||
GmailGetMessage(api_resource=self.api_resource),
|
|
||||||
GmailGetThread(api_resource=self.api_resource),
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
"""Jira Toolkit."""
|
|
@ -1,83 +0,0 @@
|
|||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.jira.prompt import (
|
|
||||||
JIRA_CATCH_ALL_PROMPT,
|
|
||||||
JIRA_CONFLUENCE_PAGE_CREATE_PROMPT,
|
|
||||||
JIRA_GET_ALL_PROJECTS_PROMPT,
|
|
||||||
JIRA_ISSUE_CREATE_PROMPT,
|
|
||||||
JIRA_JQL_PROMPT,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.jira.tool import JiraAction
|
|
||||||
from langchain_community.utilities.jira import JiraAPIWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class JiraToolkit(BaseToolkit):
|
|
||||||
"""Jira Toolkit.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by creating, deleting, or updating,
|
|
||||||
reading underlying data.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools: List[BaseTool]. The tools in the toolkit. Default is an empty list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_jira_api_wrapper(cls, jira_api_wrapper: JiraAPIWrapper) -> "JiraToolkit":
|
|
||||||
"""Create a JiraToolkit from a JiraAPIWrapper.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
jira_api_wrapper: JiraAPIWrapper. The Jira API wrapper.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
JiraToolkit. The Jira toolkit.
|
|
||||||
"""
|
|
||||||
|
|
||||||
operations: List[Dict] = [
|
|
||||||
{
|
|
||||||
"mode": "jql",
|
|
||||||
"name": "jql_query",
|
|
||||||
"description": JIRA_JQL_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_projects",
|
|
||||||
"name": "get_projects",
|
|
||||||
"description": JIRA_GET_ALL_PROJECTS_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_issue",
|
|
||||||
"name": "create_issue",
|
|
||||||
"description": JIRA_ISSUE_CREATE_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "other",
|
|
||||||
"name": "catch_all_jira_api",
|
|
||||||
"description": JIRA_CATCH_ALL_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "create_page",
|
|
||||||
"name": "create_confluence_page",
|
|
||||||
"description": JIRA_CONFLUENCE_PAGE_CREATE_PROMPT,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
tools = [
|
|
||||||
JiraAction(
|
|
||||||
name=action["name"],
|
|
||||||
description=action["description"],
|
|
||||||
mode=action["mode"],
|
|
||||||
api_wrapper=jira_api_wrapper,
|
|
||||||
)
|
|
||||||
for action in operations
|
|
||||||
]
|
|
||||||
return cls(tools=tools) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return self.tools
|
|
@ -1 +0,0 @@
|
|||||||
"""Json agent."""
|
|
@ -1,76 +0,0 @@
|
|||||||
"""Json agent."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
|
|
||||||
from langchain_community.agent_toolkits.json.toolkit import JsonToolkit
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
|
|
||||||
|
|
||||||
def create_json_agent(
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
toolkit: JsonToolkit,
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None,
|
|
||||||
prefix: str = JSON_PREFIX,
|
|
||||||
suffix: str = JSON_SUFFIX,
|
|
||||||
format_instructions: Optional[str] = None,
|
|
||||||
input_variables: Optional[List[str]] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
agent_executor_kwargs: Optional[Dict[str, Any]] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AgentExecutor:
|
|
||||||
"""Construct a json agent from an LLM and tools.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
toolkit: The toolkit to use.
|
|
||||||
callback_manager: The callback manager to use. Default is None.
|
|
||||||
prefix: The prefix to use. Default is JSON_PREFIX.
|
|
||||||
suffix: The suffix to use. Default is JSON_SUFFIX.
|
|
||||||
format_instructions: The format instructions to use. Default is None.
|
|
||||||
input_variables: The input variables to use. Default is None.
|
|
||||||
verbose: Whether to print verbose output. Default is False.
|
|
||||||
agent_executor_kwargs: Optional additional arguments for the agent executor.
|
|
||||||
kwargs: Additional arguments for the agent.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The agent executor.
|
|
||||||
"""
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
from langchain.agents.mrkl.base import ZeroShotAgent
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
prompt_params = (
|
|
||||||
{"format_instructions": format_instructions}
|
|
||||||
if format_instructions is not None
|
|
||||||
else {}
|
|
||||||
)
|
|
||||||
prompt = ZeroShotAgent.create_prompt(
|
|
||||||
tools,
|
|
||||||
prefix=prefix,
|
|
||||||
suffix=suffix,
|
|
||||||
input_variables=input_variables,
|
|
||||||
**prompt_params,
|
|
||||||
)
|
|
||||||
llm_chain = LLMChain(
|
|
||||||
llm=llm,
|
|
||||||
prompt=prompt,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
)
|
|
||||||
tool_names = [tool.name for tool in tools]
|
|
||||||
agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names, **kwargs)
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
|
||||||
agent=agent,
|
|
||||||
tools=tools,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
verbose=verbose,
|
|
||||||
**(agent_executor_kwargs or {}),
|
|
||||||
)
|
|
@ -1,25 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
JSON_PREFIX = """You are an agent designed to interact with JSON.
|
|
||||||
Your goal is to return a final answer by interacting with the JSON.
|
|
||||||
You have access to the following tools which help you learn more about the JSON you are interacting with.
|
|
||||||
Only use the below tools. Only use the information returned by the below tools to construct your final answer.
|
|
||||||
Do not make up any information that is not contained in the JSON.
|
|
||||||
Your input to the tools should be in the form of `data["key"][0]` where `data` is the JSON blob you are interacting with, and the syntax used is Python.
|
|
||||||
You should only use keys that you know for a fact exist. You must validate that a key exists by seeing it previously when calling `json_spec_list_keys`.
|
|
||||||
If you have not seen a key in one of those responses, you cannot use it.
|
|
||||||
You should only add one key at a time to the path. You cannot add multiple keys at once.
|
|
||||||
If you encounter a "KeyError", go back to the previous key, look at the available keys, and try again.
|
|
||||||
|
|
||||||
If the question does not seem to be related to the JSON, just return "I don't know" as the answer.
|
|
||||||
Always begin your interaction with the `json_spec_list_keys` tool with input "data" to see what keys exist in the JSON.
|
|
||||||
|
|
||||||
Note that sometimes the value at a given path is large. In this case, you will get an error "Value is a large dictionary, should explore its keys directly".
|
|
||||||
In this case, you should ALWAYS follow up by using the `json_spec_list_keys` tool to see what keys exist at that path.
|
|
||||||
Do not simply refer the user to the JSON or a section of the JSON, as this is not a valid answer. Keep digging until you find the answer and explicitly return it.
|
|
||||||
"""
|
|
||||||
JSON_SUFFIX = """Begin!"
|
|
||||||
|
|
||||||
Question: {input}
|
|
||||||
Thought: I should look at the keys that exist in data to see what I have access to
|
|
||||||
{agent_scratchpad}"""
|
|
@ -1,29 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.json.tool import (
|
|
||||||
JsonGetValueTool,
|
|
||||||
JsonListKeysTool,
|
|
||||||
JsonSpec,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class JsonToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with a JSON spec.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
spec: The JSON spec.
|
|
||||||
"""
|
|
||||||
|
|
||||||
spec: JsonSpec
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
JsonListKeysTool(spec=self.spec),
|
|
||||||
JsonGetValueTool(spec=self.spec),
|
|
||||||
]
|
|
@ -1,771 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
"""Tools provide access to various resources and services.
|
|
||||||
|
|
||||||
LangChain has a large ecosystem of integrations with various external resources
|
|
||||||
like local and remote file systems, APIs and databases.
|
|
||||||
|
|
||||||
These integrations allow developers to create versatile applications that combine the
|
|
||||||
power of LLMs with the ability to access, interact with and manipulate external
|
|
||||||
resources.
|
|
||||||
|
|
||||||
When developing an application, developers should inspect the capabilities and
|
|
||||||
permissions of the tools that underlie the given agent toolkit, and determine
|
|
||||||
whether permissions of the given toolkit are appropriate for the application.
|
|
||||||
|
|
||||||
See [Security](https://python.langchain.com/docs/security) for more information.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import warnings
|
|
||||||
from typing import Any, Dict, List, Optional, Callable, Tuple
|
|
||||||
|
|
||||||
from mypy_extensions import Arg, KwArg
|
|
||||||
|
|
||||||
from langchain_community.tools.arxiv.tool import ArxivQueryRun
|
|
||||||
from langchain_community.tools.bing_search.tool import BingSearchRun
|
|
||||||
from langchain_community.tools.dataforseo_api_search import DataForSeoAPISearchResults
|
|
||||||
from langchain_community.tools.dataforseo_api_search import DataForSeoAPISearchRun
|
|
||||||
from langchain_community.tools.ddg_search.tool import DuckDuckGoSearchRun
|
|
||||||
from langchain_community.tools.eleven_labs.text2speech import ElevenLabsText2SpeechTool
|
|
||||||
from langchain_community.tools.file_management import ReadFileTool
|
|
||||||
from langchain_community.tools.golden_query.tool import GoldenQueryRun
|
|
||||||
from langchain_community.tools.google_cloud.texttospeech import (
|
|
||||||
GoogleCloudTextToSpeechTool,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.google_finance.tool import GoogleFinanceQueryRun
|
|
||||||
from langchain_community.tools.google_jobs.tool import GoogleJobsQueryRun
|
|
||||||
from langchain_community.tools.google_lens.tool import GoogleLensQueryRun
|
|
||||||
from langchain_community.tools.google_scholar.tool import GoogleScholarQueryRun
|
|
||||||
from langchain_community.tools.google_search.tool import (
|
|
||||||
GoogleSearchResults,
|
|
||||||
GoogleSearchRun,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.google_serper.tool import (
|
|
||||||
GoogleSerperResults,
|
|
||||||
GoogleSerperRun,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.google_trends.tool import GoogleTrendsQueryRun
|
|
||||||
from langchain_community.tools.graphql.tool import BaseGraphQLTool
|
|
||||||
from langchain_community.tools.human.tool import HumanInputRun
|
|
||||||
from langchain_community.tools.memorize.tool import Memorize
|
|
||||||
from langchain_community.tools.merriam_webster.tool import MerriamWebsterQueryRun
|
|
||||||
from langchain_community.tools.metaphor_search.tool import MetaphorSearchResults
|
|
||||||
from langchain_community.tools.openweathermap.tool import OpenWeatherMapQueryRun
|
|
||||||
from langchain_community.tools.pubmed.tool import PubmedQueryRun
|
|
||||||
from langchain_community.tools.reddit_search.tool import RedditSearchRun
|
|
||||||
from langchain_community.tools.requests.tool import (
|
|
||||||
RequestsDeleteTool,
|
|
||||||
RequestsGetTool,
|
|
||||||
RequestsPatchTool,
|
|
||||||
RequestsPostTool,
|
|
||||||
RequestsPutTool,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.scenexplain.tool import SceneXplainTool
|
|
||||||
from langchain_community.tools.searchapi.tool import SearchAPIResults, SearchAPIRun
|
|
||||||
from langchain_community.tools.searx_search.tool import (
|
|
||||||
SearxSearchResults,
|
|
||||||
SearxSearchRun,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.shell.tool import ShellTool
|
|
||||||
from langchain_community.tools.sleep.tool import SleepTool
|
|
||||||
from langchain_community.tools.stackexchange.tool import StackExchangeTool
|
|
||||||
from langchain_community.tools.wikipedia.tool import WikipediaQueryRun
|
|
||||||
from langchain_community.tools.wolfram_alpha.tool import WolframAlphaQueryRun
|
|
||||||
from langchain_community.utilities.arxiv import ArxivAPIWrapper
|
|
||||||
from langchain_community.utilities.awslambda import LambdaWrapper
|
|
||||||
from langchain_community.utilities.bing_search import BingSearchAPIWrapper
|
|
||||||
from langchain_community.utilities.dalle_image_generator import DallEAPIWrapper
|
|
||||||
from langchain_community.utilities.dataforseo_api_search import DataForSeoAPIWrapper
|
|
||||||
from langchain_community.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper
|
|
||||||
from langchain_community.utilities.golden_query import GoldenQueryAPIWrapper
|
|
||||||
from langchain_community.utilities.google_books import GoogleBooksAPIWrapper
|
|
||||||
from langchain_community.utilities.google_finance import GoogleFinanceAPIWrapper
|
|
||||||
from langchain_community.utilities.google_jobs import GoogleJobsAPIWrapper
|
|
||||||
from langchain_community.utilities.google_lens import GoogleLensAPIWrapper
|
|
||||||
from langchain_community.utilities.google_scholar import GoogleScholarAPIWrapper
|
|
||||||
from langchain_community.utilities.google_search import GoogleSearchAPIWrapper
|
|
||||||
from langchain_community.utilities.google_serper import GoogleSerperAPIWrapper
|
|
||||||
from langchain_community.utilities.google_trends import GoogleTrendsAPIWrapper
|
|
||||||
from langchain_community.utilities.graphql import GraphQLAPIWrapper
|
|
||||||
from langchain_community.utilities.merriam_webster import MerriamWebsterAPIWrapper
|
|
||||||
from langchain_community.utilities.metaphor_search import MetaphorSearchAPIWrapper
|
|
||||||
from langchain_community.utilities.openweathermap import OpenWeatherMapAPIWrapper
|
|
||||||
from langchain_community.utilities.pubmed import PubMedAPIWrapper
|
|
||||||
from langchain_community.utilities.reddit_search import RedditSearchAPIWrapper
|
|
||||||
from langchain_community.utilities.requests import TextRequestsWrapper
|
|
||||||
from langchain_community.utilities.searchapi import SearchApiAPIWrapper
|
|
||||||
from langchain_community.utilities.searx_search import SearxSearchWrapper
|
|
||||||
from langchain_community.utilities.serpapi import SerpAPIWrapper
|
|
||||||
from langchain_community.utilities.stackexchange import StackExchangeAPIWrapper
|
|
||||||
from langchain_community.utilities.twilio import TwilioAPIWrapper
|
|
||||||
from langchain_community.utilities.wikipedia import WikipediaAPIWrapper
|
|
||||||
from langchain_community.utilities.wolfram_alpha import WolframAlphaAPIWrapper
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.callbacks import Callbacks
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.tools import BaseTool, Tool
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tools_requests_get() -> BaseTool:
|
|
||||||
# Dangerous requests are allowed here, because there's another flag that the user
|
|
||||||
# has to provide in order to actually opt in.
|
|
||||||
# This is a private function and should not be used directly.
|
|
||||||
return RequestsGetTool(
|
|
||||||
requests_wrapper=TextRequestsWrapper(), allow_dangerous_requests=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tools_requests_post() -> BaseTool:
|
|
||||||
# Dangerous requests are allowed here, because there's another flag that the user
|
|
||||||
# has to provide in order to actually opt in.
|
|
||||||
# This is a private function and should not be used directly.
|
|
||||||
return RequestsPostTool(
|
|
||||||
requests_wrapper=TextRequestsWrapper(), allow_dangerous_requests=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tools_requests_patch() -> BaseTool:
|
|
||||||
# Dangerous requests are allowed here, because there's another flag that the user
|
|
||||||
# has to provide in order to actually opt in.
|
|
||||||
# This is a private function and should not be used directly.
|
|
||||||
return RequestsPatchTool(
|
|
||||||
requests_wrapper=TextRequestsWrapper(), allow_dangerous_requests=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tools_requests_put() -> BaseTool:
|
|
||||||
# Dangerous requests are allowed here, because there's another flag that the user
|
|
||||||
# has to provide in order to actually opt in.
|
|
||||||
# This is a private function and should not be used directly.
|
|
||||||
return RequestsPutTool(
|
|
||||||
requests_wrapper=TextRequestsWrapper(), allow_dangerous_requests=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tools_requests_delete() -> BaseTool:
|
|
||||||
# Dangerous requests are allowed here, because there's another flag that the user
|
|
||||||
# has to provide in order to actually opt in.
|
|
||||||
# This is a private function and should not be used directly.
|
|
||||||
return RequestsDeleteTool(
|
|
||||||
requests_wrapper=TextRequestsWrapper(), allow_dangerous_requests=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_terminal() -> BaseTool:
|
|
||||||
return ShellTool()
|
|
||||||
|
|
||||||
|
|
||||||
def _get_sleep() -> BaseTool:
|
|
||||||
return SleepTool()
|
|
||||||
|
|
||||||
|
|
||||||
_BASE_TOOLS: Dict[str, Callable[[], BaseTool]] = {
|
|
||||||
"sleep": _get_sleep,
|
|
||||||
}
|
|
||||||
|
|
||||||
DANGEROUS_TOOLS = {
|
|
||||||
# Tools that contain some level of risk.
|
|
||||||
# Please use with caution and read the documentation of these tools
|
|
||||||
# to understand the risks and how to mitigate them.
|
|
||||||
# Refer to https://python.langchain.com/docs/security
|
|
||||||
# for more information.
|
|
||||||
"requests": _get_tools_requests_get, # preserved for backwards compatibility
|
|
||||||
"requests_get": _get_tools_requests_get,
|
|
||||||
"requests_post": _get_tools_requests_post,
|
|
||||||
"requests_patch": _get_tools_requests_patch,
|
|
||||||
"requests_put": _get_tools_requests_put,
|
|
||||||
"requests_delete": _get_tools_requests_delete,
|
|
||||||
"terminal": _get_terminal,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _get_llm_math(llm: BaseLanguageModel) -> BaseTool:
|
|
||||||
try:
|
|
||||||
from langchain.chains.llm_math.base import LLMMathChain
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"LLM Math tools require the library `langchain` to be installed."
|
|
||||||
" Please install it with `pip install langchain`."
|
|
||||||
)
|
|
||||||
return Tool(
|
|
||||||
name="Calculator",
|
|
||||||
description="Useful for when you need to answer questions about math.",
|
|
||||||
func=LLMMathChain.from_llm(llm=llm).run,
|
|
||||||
coroutine=LLMMathChain.from_llm(llm=llm).arun,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_open_meteo_api(llm: BaseLanguageModel) -> BaseTool:
|
|
||||||
try:
|
|
||||||
from langchain.chains.api.base import APIChain
|
|
||||||
from langchain.chains.api import (
|
|
||||||
open_meteo_docs,
|
|
||||||
)
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"API tools require the library `langchain` to be installed."
|
|
||||||
" Please install it with `pip install langchain`."
|
|
||||||
)
|
|
||||||
chain = APIChain.from_llm_and_api_docs(
|
|
||||||
llm,
|
|
||||||
open_meteo_docs.OPEN_METEO_DOCS,
|
|
||||||
limit_to_domains=["https://api.open-meteo.com/"],
|
|
||||||
)
|
|
||||||
return Tool(
|
|
||||||
name="Open-Meteo-API",
|
|
||||||
description="Useful for when you want to get weather information from the OpenMeteo API. The input should be a question in natural language that this API can answer.",
|
|
||||||
func=chain.run,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_LLM_TOOLS: Dict[str, Callable[[BaseLanguageModel], BaseTool]] = {
|
|
||||||
"llm-math": _get_llm_math,
|
|
||||||
"open-meteo-api": _get_open_meteo_api,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _get_news_api(llm: BaseLanguageModel, **kwargs: Any) -> BaseTool:
|
|
||||||
news_api_key = kwargs["news_api_key"]
|
|
||||||
try:
|
|
||||||
from langchain.chains.api.base import APIChain
|
|
||||||
from langchain.chains.api import (
|
|
||||||
news_docs,
|
|
||||||
)
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"API tools require the library `langchain` to be installed."
|
|
||||||
" Please install it with `pip install langchain`."
|
|
||||||
)
|
|
||||||
chain = APIChain.from_llm_and_api_docs(
|
|
||||||
llm,
|
|
||||||
news_docs.NEWS_DOCS,
|
|
||||||
headers={"X-Api-Key": news_api_key},
|
|
||||||
limit_to_domains=["https://newsapi.org/"],
|
|
||||||
)
|
|
||||||
return Tool(
|
|
||||||
name="News-API",
|
|
||||||
description="Use this when you want to get information about the top headlines of current news stories. The input should be a question in natural language that this API can answer.",
|
|
||||||
func=chain.run,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tmdb_api(llm: BaseLanguageModel, **kwargs: Any) -> BaseTool:
|
|
||||||
tmdb_bearer_token = kwargs["tmdb_bearer_token"]
|
|
||||||
try:
|
|
||||||
from langchain.chains.api.base import APIChain
|
|
||||||
from langchain.chains.api import (
|
|
||||||
tmdb_docs,
|
|
||||||
)
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"API tools require the library `langchain` to be installed."
|
|
||||||
" Please install it with `pip install langchain`."
|
|
||||||
)
|
|
||||||
chain = APIChain.from_llm_and_api_docs(
|
|
||||||
llm,
|
|
||||||
tmdb_docs.TMDB_DOCS,
|
|
||||||
headers={"Authorization": f"Bearer {tmdb_bearer_token}"},
|
|
||||||
limit_to_domains=["https://api.themoviedb.org/"],
|
|
||||||
)
|
|
||||||
return Tool(
|
|
||||||
name="TMDB-API",
|
|
||||||
description="Useful for when you want to get information from The Movie Database. The input should be a question in natural language that this API can answer.",
|
|
||||||
func=chain.run,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_podcast_api(llm: BaseLanguageModel, **kwargs: Any) -> BaseTool:
|
|
||||||
listen_api_key = kwargs["listen_api_key"]
|
|
||||||
try:
|
|
||||||
from langchain.chains.api.base import APIChain
|
|
||||||
from langchain.chains.api import (
|
|
||||||
podcast_docs,
|
|
||||||
)
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"API tools require the library `langchain` to be installed."
|
|
||||||
" Please install it with `pip install langchain`."
|
|
||||||
)
|
|
||||||
chain = APIChain.from_llm_and_api_docs(
|
|
||||||
llm,
|
|
||||||
podcast_docs.PODCAST_DOCS,
|
|
||||||
headers={"X-ListenAPI-Key": listen_api_key},
|
|
||||||
limit_to_domains=["https://listen-api.listennotes.com/"],
|
|
||||||
)
|
|
||||||
return Tool(
|
|
||||||
name="Podcast-API",
|
|
||||||
description="Use the Listen Notes Podcast API to search all podcasts or episodes. The input should be a question in natural language that this API can answer.",
|
|
||||||
func=chain.run,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_lambda_api(**kwargs: Any) -> BaseTool:
|
|
||||||
return Tool(
|
|
||||||
name=kwargs["awslambda_tool_name"],
|
|
||||||
description=kwargs["awslambda_tool_description"],
|
|
||||||
func=LambdaWrapper(**kwargs).run,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_wolfram_alpha(**kwargs: Any) -> BaseTool:
|
|
||||||
return WolframAlphaQueryRun(api_wrapper=WolframAlphaAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleSearchRun(api_wrapper=GoogleSearchAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_merriam_webster(**kwargs: Any) -> BaseTool:
|
|
||||||
return MerriamWebsterQueryRun(api_wrapper=MerriamWebsterAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_wikipedia(**kwargs: Any) -> BaseTool:
|
|
||||||
return WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_arxiv(**kwargs: Any) -> BaseTool:
|
|
||||||
return ArxivQueryRun(api_wrapper=ArxivAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_golden_query(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoldenQueryRun(api_wrapper=GoldenQueryAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_pubmed(**kwargs: Any) -> BaseTool:
|
|
||||||
return PubmedQueryRun(api_wrapper=PubMedAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_books(**kwargs: Any) -> BaseTool:
|
|
||||||
from langchain_community.tools.google_books import GoogleBooksQueryRun
|
|
||||||
|
|
||||||
return GoogleBooksQueryRun(api_wrapper=GoogleBooksAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_jobs(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleJobsQueryRun(api_wrapper=GoogleJobsAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_lens(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleLensQueryRun(api_wrapper=GoogleLensAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_serper(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleSerperRun(api_wrapper=GoogleSerperAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_scholar(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleScholarQueryRun(api_wrapper=GoogleScholarAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_finance(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleFinanceQueryRun(api_wrapper=GoogleFinanceAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_trends(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleTrendsQueryRun(api_wrapper=GoogleTrendsAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_serper_results_json(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleSerperResults(api_wrapper=GoogleSerperAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_search_results_json(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleSearchResults(api_wrapper=GoogleSearchAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_searchapi(**kwargs: Any) -> BaseTool:
|
|
||||||
return SearchAPIRun(api_wrapper=SearchApiAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_searchapi_results_json(**kwargs: Any) -> BaseTool:
|
|
||||||
return SearchAPIResults(api_wrapper=SearchApiAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_serpapi(**kwargs: Any) -> BaseTool:
|
|
||||||
return Tool(
|
|
||||||
name="Search",
|
|
||||||
description="A search engine. Useful for when you need to answer questions about current events. Input should be a search query.",
|
|
||||||
func=SerpAPIWrapper(**kwargs).run,
|
|
||||||
coroutine=SerpAPIWrapper(**kwargs).arun,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_stackexchange(**kwargs: Any) -> BaseTool:
|
|
||||||
return StackExchangeTool(api_wrapper=StackExchangeAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_dalle_image_generator(**kwargs: Any) -> Tool:
|
|
||||||
return Tool(
|
|
||||||
"Dall-E-Image-Generator",
|
|
||||||
DallEAPIWrapper(**kwargs).run,
|
|
||||||
"A wrapper around OpenAI DALL-E API. Useful for when you need to generate images from a text description. Input should be an image description.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_twilio(**kwargs: Any) -> BaseTool:
|
|
||||||
return Tool(
|
|
||||||
name="Text-Message",
|
|
||||||
description="Useful for when you need to send a text message to a provided phone number.",
|
|
||||||
func=TwilioAPIWrapper(**kwargs).run,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_searx_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return SearxSearchRun(wrapper=SearxSearchWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_searx_search_results_json(**kwargs: Any) -> BaseTool:
|
|
||||||
wrapper_kwargs = {k: v for k, v in kwargs.items() if k != "num_results"}
|
|
||||||
return SearxSearchResults(wrapper=SearxSearchWrapper(**wrapper_kwargs), **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_bing_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return BingSearchRun(api_wrapper=BingSearchAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_metaphor_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return MetaphorSearchResults(api_wrapper=MetaphorSearchAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_ddg_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return DuckDuckGoSearchRun(api_wrapper=DuckDuckGoSearchAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_human_tool(**kwargs: Any) -> BaseTool:
|
|
||||||
return HumanInputRun(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_scenexplain(**kwargs: Any) -> BaseTool:
|
|
||||||
return SceneXplainTool(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_graphql_tool(**kwargs: Any) -> BaseTool:
|
|
||||||
return BaseGraphQLTool(graphql_wrapper=GraphQLAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_openweathermap(**kwargs: Any) -> BaseTool:
|
|
||||||
return OpenWeatherMapQueryRun(api_wrapper=OpenWeatherMapAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_dataforseo_api_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return DataForSeoAPISearchRun(api_wrapper=DataForSeoAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_dataforseo_api_search_json(**kwargs: Any) -> BaseTool:
|
|
||||||
return DataForSeoAPISearchResults(api_wrapper=DataForSeoAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_eleven_labs_text2speech(**kwargs: Any) -> BaseTool:
|
|
||||||
return ElevenLabsText2SpeechTool(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_memorize(llm: BaseLanguageModel, **kwargs: Any) -> BaseTool:
|
|
||||||
return Memorize(llm=llm) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
|
|
||||||
def _get_google_cloud_texttospeech(**kwargs: Any) -> BaseTool:
|
|
||||||
return GoogleCloudTextToSpeechTool(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_file_management_tool(**kwargs: Any) -> BaseTool:
|
|
||||||
return ReadFileTool(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_reddit_search(**kwargs: Any) -> BaseTool:
|
|
||||||
return RedditSearchRun(api_wrapper=RedditSearchAPIWrapper(**kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
_EXTRA_LLM_TOOLS: Dict[
|
|
||||||
str,
|
|
||||||
Tuple[Callable[[Arg(BaseLanguageModel, "llm"), KwArg(Any)], BaseTool], List[str]],
|
|
||||||
] = {
|
|
||||||
"news-api": (_get_news_api, ["news_api_key"]),
|
|
||||||
"tmdb-api": (_get_tmdb_api, ["tmdb_bearer_token"]),
|
|
||||||
"podcast-api": (_get_podcast_api, ["listen_api_key"]),
|
|
||||||
"memorize": (_get_memorize, []),
|
|
||||||
}
|
|
||||||
_EXTRA_OPTIONAL_TOOLS: Dict[str, Tuple[Callable[[KwArg(Any)], BaseTool], List[str]]] = {
|
|
||||||
"wolfram-alpha": (_get_wolfram_alpha, ["wolfram_alpha_appid"]),
|
|
||||||
"google-search": (_get_google_search, ["google_api_key", "google_cse_id"]),
|
|
||||||
"google-search-results-json": (
|
|
||||||
_get_google_search_results_json,
|
|
||||||
["google_api_key", "google_cse_id", "num_results"],
|
|
||||||
),
|
|
||||||
"searx-search-results-json": (
|
|
||||||
_get_searx_search_results_json,
|
|
||||||
["searx_host", "engines", "num_results", "aiosession"],
|
|
||||||
),
|
|
||||||
"bing-search": (_get_bing_search, ["bing_subscription_key", "bing_search_url"]),
|
|
||||||
"metaphor-search": (_get_metaphor_search, ["metaphor_api_key"]),
|
|
||||||
"ddg-search": (_get_ddg_search, []),
|
|
||||||
"google-books": (_get_google_books, ["google_books_api_key"]),
|
|
||||||
"google-lens": (_get_google_lens, ["serp_api_key"]),
|
|
||||||
"google-serper": (_get_google_serper, ["serper_api_key", "aiosession"]),
|
|
||||||
"google-scholar": (
|
|
||||||
_get_google_scholar,
|
|
||||||
["top_k_results", "hl", "lr", "serp_api_key"],
|
|
||||||
),
|
|
||||||
"google-finance": (
|
|
||||||
_get_google_finance,
|
|
||||||
["serp_api_key"],
|
|
||||||
),
|
|
||||||
"google-trends": (
|
|
||||||
_get_google_trends,
|
|
||||||
["serp_api_key"],
|
|
||||||
),
|
|
||||||
"google-jobs": (
|
|
||||||
_get_google_jobs,
|
|
||||||
["serp_api_key"],
|
|
||||||
),
|
|
||||||
"google-serper-results-json": (
|
|
||||||
_get_google_serper_results_json,
|
|
||||||
["serper_api_key", "aiosession"],
|
|
||||||
),
|
|
||||||
"searchapi": (_get_searchapi, ["searchapi_api_key", "aiosession"]),
|
|
||||||
"searchapi-results-json": (
|
|
||||||
_get_searchapi_results_json,
|
|
||||||
["searchapi_api_key", "aiosession"],
|
|
||||||
),
|
|
||||||
"serpapi": (_get_serpapi, ["serpapi_api_key", "aiosession"]),
|
|
||||||
"dalle-image-generator": (_get_dalle_image_generator, ["openai_api_key"]),
|
|
||||||
"twilio": (_get_twilio, ["account_sid", "auth_token", "from_number"]),
|
|
||||||
"searx-search": (_get_searx_search, ["searx_host", "engines", "aiosession"]),
|
|
||||||
"merriam-webster": (_get_merriam_webster, ["merriam_webster_api_key"]),
|
|
||||||
"wikipedia": (_get_wikipedia, ["top_k_results", "lang"]),
|
|
||||||
"arxiv": (
|
|
||||||
_get_arxiv,
|
|
||||||
["top_k_results", "load_max_docs", "load_all_available_meta"],
|
|
||||||
),
|
|
||||||
"golden-query": (_get_golden_query, ["golden_api_key"]),
|
|
||||||
"pubmed": (_get_pubmed, ["top_k_results"]),
|
|
||||||
"human": (_get_human_tool, ["prompt_func", "input_func"]),
|
|
||||||
"awslambda": (
|
|
||||||
_get_lambda_api,
|
|
||||||
["awslambda_tool_name", "awslambda_tool_description", "function_name"],
|
|
||||||
),
|
|
||||||
"stackexchange": (_get_stackexchange, []),
|
|
||||||
"sceneXplain": (_get_scenexplain, []),
|
|
||||||
"graphql": (
|
|
||||||
_get_graphql_tool,
|
|
||||||
["graphql_endpoint", "custom_headers", "fetch_schema_from_transport"],
|
|
||||||
),
|
|
||||||
"openweathermap-api": (_get_openweathermap, ["openweathermap_api_key"]),
|
|
||||||
"dataforseo-api-search": (
|
|
||||||
_get_dataforseo_api_search,
|
|
||||||
["api_login", "api_password", "aiosession"],
|
|
||||||
),
|
|
||||||
"dataforseo-api-search-json": (
|
|
||||||
_get_dataforseo_api_search_json,
|
|
||||||
["api_login", "api_password", "aiosession"],
|
|
||||||
),
|
|
||||||
"eleven_labs_text2speech": (_get_eleven_labs_text2speech, ["elevenlabs_api_key"]),
|
|
||||||
"google_cloud_texttospeech": (_get_google_cloud_texttospeech, []),
|
|
||||||
"read_file": (_get_file_management_tool, []),
|
|
||||||
"reddit_search": (
|
|
||||||
_get_reddit_search,
|
|
||||||
["reddit_client_id", "reddit_client_secret", "reddit_user_agent"],
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _handle_callbacks(
|
|
||||||
callback_manager: Optional[BaseCallbackManager], callbacks: Callbacks
|
|
||||||
) -> Callbacks:
|
|
||||||
if callback_manager is not None:
|
|
||||||
warnings.warn(
|
|
||||||
"callback_manager is deprecated. Please use callbacks instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
if callbacks is not None:
|
|
||||||
raise ValueError(
|
|
||||||
"Cannot specify both callback_manager and callbacks arguments."
|
|
||||||
)
|
|
||||||
return callback_manager
|
|
||||||
return callbacks
|
|
||||||
|
|
||||||
|
|
||||||
def load_huggingface_tool(
|
|
||||||
task_or_repo_id: str,
|
|
||||||
model_repo_id: Optional[str] = None,
|
|
||||||
token: Optional[str] = None,
|
|
||||||
remote: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> BaseTool:
|
|
||||||
"""Loads a tool from the HuggingFace Hub.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
task_or_repo_id: Task or model repo id.
|
|
||||||
model_repo_id: Optional model repo id. Defaults to None.
|
|
||||||
token: Optional token. Defaults to None.
|
|
||||||
remote: Optional remote. Defaults to False.
|
|
||||||
kwargs: Additional keyword arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A tool.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ImportError: If the required libraries are not installed.
|
|
||||||
NotImplementedError: If multimodal outputs or inputs are not supported.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from transformers import load_tool
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"HuggingFace tools require the libraries `transformers>=4.29.0`"
|
|
||||||
" and `huggingface_hub>=0.14.1` to be installed."
|
|
||||||
" Please install it with"
|
|
||||||
" `pip install --upgrade transformers huggingface_hub`."
|
|
||||||
)
|
|
||||||
hf_tool = load_tool(
|
|
||||||
task_or_repo_id,
|
|
||||||
model_repo_id=model_repo_id,
|
|
||||||
token=token,
|
|
||||||
remote=remote,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
outputs = hf_tool.outputs
|
|
||||||
if set(outputs) != {"text"}:
|
|
||||||
raise NotImplementedError("Multimodal outputs not supported yet.")
|
|
||||||
inputs = hf_tool.inputs
|
|
||||||
if set(inputs) != {"text"}:
|
|
||||||
raise NotImplementedError("Multimodal inputs not supported yet.")
|
|
||||||
return Tool.from_function(
|
|
||||||
hf_tool.__call__, name=hf_tool.name, description=hf_tool.description
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def raise_dangerous_tools_exception(name: str) -> None:
|
|
||||||
raise ValueError(
|
|
||||||
f"{name} is a dangerous tool. You cannot use it without opting in "
|
|
||||||
"by setting allow_dangerous_tools to True. "
|
|
||||||
"Most tools have some inherit risk to them merely because they are "
|
|
||||||
'allowed to interact with the "real world".'
|
|
||||||
"Please refer to LangChain security guidelines "
|
|
||||||
"to https://python.langchain.com/docs/security."
|
|
||||||
"Some tools have been designated as dangerous because they pose "
|
|
||||||
"risk that is not intuitively obvious. For example, a tool that "
|
|
||||||
"allows an agent to make requests to the web, can also be used "
|
|
||||||
"to make requests to a server that is only accessible from the "
|
|
||||||
"server hosting the code."
|
|
||||||
"Again, all tools carry some risk, and it's your responsibility to "
|
|
||||||
"understand which tools you're using and the risks associated with "
|
|
||||||
"them."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def load_tools(
|
|
||||||
tool_names: List[str],
|
|
||||||
llm: Optional[BaseLanguageModel] = None,
|
|
||||||
callbacks: Callbacks = None,
|
|
||||||
allow_dangerous_tools: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> List[BaseTool]:
|
|
||||||
"""Load tools based on their name.
|
|
||||||
|
|
||||||
Tools allow agents to interact with various resources and services like
|
|
||||||
APIs, databases, file systems, etc.
|
|
||||||
|
|
||||||
Please scope the permissions of each tools to the minimum required for the
|
|
||||||
application.
|
|
||||||
|
|
||||||
For example, if an application only needs to read from a database,
|
|
||||||
the database tool should not be given write permissions. Moreover
|
|
||||||
consider scoping the permissions to only allow accessing specific
|
|
||||||
tables and impose user-level quota for limiting resource usage.
|
|
||||||
|
|
||||||
Please read the APIs of the individual tools to determine which configuration
|
|
||||||
they support.
|
|
||||||
|
|
||||||
See [Security](https://python.langchain.com/docs/security) for more information.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tool_names: name of tools to load.
|
|
||||||
llm: An optional language model may be needed to initialize certain tools.
|
|
||||||
Defaults to None.
|
|
||||||
callbacks: Optional callback manager or list of callback handlers.
|
|
||||||
If not provided, default global callback manager will be used.
|
|
||||||
allow_dangerous_tools: Optional flag to allow dangerous tools.
|
|
||||||
Tools that contain some level of risk.
|
|
||||||
Please use with caution and read the documentation of these tools
|
|
||||||
to understand the risks and how to mitigate them.
|
|
||||||
Refer to https://python.langchain.com/docs/security
|
|
||||||
for more information.
|
|
||||||
Please note that this list may not be fully exhaustive.
|
|
||||||
It is your responsibility to understand which tools
|
|
||||||
you're using and the risks associated with them.
|
|
||||||
Defaults to False.
|
|
||||||
kwargs: Additional keyword arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of tools.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the tool name is unknown.
|
|
||||||
ValueError: If the tool requires an LLM to be provided.
|
|
||||||
ValueError: If the tool requires some parameters that were not provided.
|
|
||||||
ValueError: If the tool is a dangerous tool and allow_dangerous_tools is False.
|
|
||||||
"""
|
|
||||||
tools = []
|
|
||||||
callbacks = _handle_callbacks(
|
|
||||||
callback_manager=kwargs.get("callback_manager"), callbacks=callbacks
|
|
||||||
)
|
|
||||||
for name in tool_names:
|
|
||||||
if name in DANGEROUS_TOOLS and not allow_dangerous_tools:
|
|
||||||
raise_dangerous_tools_exception(name)
|
|
||||||
|
|
||||||
if name in {"requests"}:
|
|
||||||
warnings.warn(
|
|
||||||
"tool name `requests` is deprecated - "
|
|
||||||
"please use `requests_all` or specify the requests method"
|
|
||||||
)
|
|
||||||
if name == "requests_all":
|
|
||||||
# expand requests into various methods
|
|
||||||
if not allow_dangerous_tools:
|
|
||||||
raise_dangerous_tools_exception(name)
|
|
||||||
requests_method_tools = [
|
|
||||||
_tool for _tool in DANGEROUS_TOOLS if _tool.startswith("requests_")
|
|
||||||
]
|
|
||||||
tool_names.extend(requests_method_tools)
|
|
||||||
elif name in _BASE_TOOLS:
|
|
||||||
tools.append(_BASE_TOOLS[name]())
|
|
||||||
elif name in DANGEROUS_TOOLS:
|
|
||||||
tools.append(DANGEROUS_TOOLS[name]())
|
|
||||||
elif name in _LLM_TOOLS:
|
|
||||||
if llm is None:
|
|
||||||
raise ValueError(f"Tool {name} requires an LLM to be provided")
|
|
||||||
tool = _LLM_TOOLS[name](llm)
|
|
||||||
tools.append(tool)
|
|
||||||
elif name in _EXTRA_LLM_TOOLS:
|
|
||||||
if llm is None:
|
|
||||||
raise ValueError(f"Tool {name} requires an LLM to be provided")
|
|
||||||
_get_llm_tool_func, extra_keys = _EXTRA_LLM_TOOLS[name]
|
|
||||||
missing_keys = set(extra_keys).difference(kwargs)
|
|
||||||
if missing_keys:
|
|
||||||
raise ValueError(
|
|
||||||
f"Tool {name} requires some parameters that were not "
|
|
||||||
f"provided: {missing_keys}"
|
|
||||||
)
|
|
||||||
sub_kwargs = {k: kwargs[k] for k in extra_keys}
|
|
||||||
tool = _get_llm_tool_func(llm=llm, **sub_kwargs)
|
|
||||||
tools.append(tool)
|
|
||||||
elif name in _EXTRA_OPTIONAL_TOOLS:
|
|
||||||
_get_tool_func, extra_keys = _EXTRA_OPTIONAL_TOOLS[name]
|
|
||||||
sub_kwargs = {k: kwargs[k] for k in extra_keys if k in kwargs}
|
|
||||||
tool = _get_tool_func(**sub_kwargs)
|
|
||||||
tools.append(tool)
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Got unknown tool {name}")
|
|
||||||
if callbacks is not None:
|
|
||||||
for tool in tools:
|
|
||||||
tool.callbacks = callbacks
|
|
||||||
return tools
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_tool_names() -> List[str]:
|
|
||||||
"""Get a list of all possible tool names."""
|
|
||||||
return (
|
|
||||||
list(_BASE_TOOLS)
|
|
||||||
+ list(_EXTRA_OPTIONAL_TOOLS)
|
|
||||||
+ list(_EXTRA_LLM_TOOLS)
|
|
||||||
+ list(_LLM_TOOLS)
|
|
||||||
+ list(DANGEROUS_TOOLS)
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
"""MultiOn Toolkit."""
|
|
@ -1,35 +0,0 @@
|
|||||||
"""MultiOn agent."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict
|
|
||||||
|
|
||||||
from langchain_community.tools.multion.close_session import MultionCloseSession
|
|
||||||
from langchain_community.tools.multion.create_session import MultionCreateSession
|
|
||||||
from langchain_community.tools.multion.update_session import MultionUpdateSession
|
|
||||||
|
|
||||||
|
|
||||||
class MultionToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with the Browser Agent.
|
|
||||||
|
|
||||||
**Security Note**: This toolkit contains tools that interact with the
|
|
||||||
user's browser via the multion API which grants an agent
|
|
||||||
access to the user's browser.
|
|
||||||
|
|
||||||
Please review the documentation for the multion API to understand
|
|
||||||
the security implications of using this toolkit.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
"""
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [MultionCreateSession(), MultionUpdateSession(), MultionCloseSession()]
|
|
@ -1 +0,0 @@
|
|||||||
"""NASA Toolkit"""
|
|
@ -1,62 +0,0 @@
|
|||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.nasa.prompt import (
|
|
||||||
NASA_CAPTIONS_PROMPT,
|
|
||||||
NASA_MANIFEST_PROMPT,
|
|
||||||
NASA_METADATA_PROMPT,
|
|
||||||
NASA_SEARCH_PROMPT,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.nasa.tool import NasaAction
|
|
||||||
from langchain_community.utilities.nasa import NasaAPIWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class NasaToolkit(BaseToolkit):
|
|
||||||
"""Nasa Toolkit.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools: List[BaseTool]. The tools in the toolkit. Default is an empty list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_nasa_api_wrapper(cls, nasa_api_wrapper: NasaAPIWrapper) -> "NasaToolkit":
|
|
||||||
operations: List[Dict] = [
|
|
||||||
{
|
|
||||||
"mode": "search_media",
|
|
||||||
"name": "Search NASA Image and Video Library media",
|
|
||||||
"description": NASA_SEARCH_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_media_metadata_manifest",
|
|
||||||
"name": "Get NASA Image and Video Library media metadata manifest",
|
|
||||||
"description": NASA_MANIFEST_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_media_metadata_location",
|
|
||||||
"name": "Get NASA Image and Video Library media metadata location",
|
|
||||||
"description": NASA_METADATA_PROMPT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": "get_video_captions_location",
|
|
||||||
"name": "Get NASA Image and Video Library video captions location",
|
|
||||||
"description": NASA_CAPTIONS_PROMPT,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
tools = [
|
|
||||||
NasaAction(
|
|
||||||
name=action["name"],
|
|
||||||
description=action["description"],
|
|
||||||
mode=action["mode"],
|
|
||||||
api_wrapper=nasa_api_wrapper,
|
|
||||||
)
|
|
||||||
for action in operations
|
|
||||||
]
|
|
||||||
return cls(tools=tools) # type: ignore[arg-type]
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return self.tools
|
|
@ -1,79 +0,0 @@
|
|||||||
"""Tool for interacting with a single API with natural language definition."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any, Optional
|
|
||||||
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.tools import Tool
|
|
||||||
|
|
||||||
from langchain_community.chains.openapi.chain import OpenAPIEndpointChain
|
|
||||||
from langchain_community.tools.openapi.utils.api_models import APIOperation
|
|
||||||
from langchain_community.tools.openapi.utils.openapi_utils import OpenAPISpec
|
|
||||||
from langchain_community.utilities.requests import Requests
|
|
||||||
|
|
||||||
|
|
||||||
class NLATool(Tool): # type: ignore[override]
|
|
||||||
"""Natural Language API Tool."""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_open_api_endpoint_chain(
|
|
||||||
cls, chain: OpenAPIEndpointChain, api_title: str
|
|
||||||
) -> "NLATool":
|
|
||||||
"""Convert an endpoint chain to an API endpoint tool.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chain: The endpoint chain.
|
|
||||||
api_title: The title of the API.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The API endpoint tool.
|
|
||||||
"""
|
|
||||||
expanded_name = (
|
|
||||||
f"{api_title.replace(' ', '_')}.{chain.api_operation.operation_id}"
|
|
||||||
)
|
|
||||||
description = (
|
|
||||||
f"I'm an AI from {api_title}. Instruct what you want,"
|
|
||||||
" and I'll assist via an API with description:"
|
|
||||||
f" {chain.api_operation.description}"
|
|
||||||
)
|
|
||||||
return cls(name=expanded_name, func=chain.run, description=description)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_llm_and_method(
|
|
||||||
cls,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
path: str,
|
|
||||||
method: str,
|
|
||||||
spec: OpenAPISpec,
|
|
||||||
requests: Optional[Requests] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
return_intermediate_steps: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> "NLATool":
|
|
||||||
"""Instantiate the tool from the specified path and method.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
path: The path of the API.
|
|
||||||
method: The method of the API.
|
|
||||||
spec: The OpenAPI spec.
|
|
||||||
requests: Optional requests object. Default is None.
|
|
||||||
verbose: Whether to print verbose output. Default is False.
|
|
||||||
return_intermediate_steps: Whether to return intermediate steps.
|
|
||||||
Default is False.
|
|
||||||
kwargs: Additional arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The tool.
|
|
||||||
"""
|
|
||||||
api_operation = APIOperation.from_openapi_spec(spec, path, method)
|
|
||||||
chain = OpenAPIEndpointChain.from_api_operation(
|
|
||||||
api_operation,
|
|
||||||
llm,
|
|
||||||
requests=requests,
|
|
||||||
verbose=verbose,
|
|
||||||
return_intermediate_steps=return_intermediate_steps,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
return cls.from_open_api_endpoint_chain(chain, spec.info.title)
|
|
@ -1,150 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any, List, Optional, Sequence
|
|
||||||
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.nla.tool import NLATool
|
|
||||||
from langchain_community.tools.openapi.utils.openapi_utils import OpenAPISpec
|
|
||||||
from langchain_community.tools.plugin import AIPlugin
|
|
||||||
from langchain_community.utilities.requests import Requests
|
|
||||||
|
|
||||||
|
|
||||||
class NLAToolkit(BaseToolkit):
|
|
||||||
"""Natural Language API Toolkit.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit creates tools that enable making calls
|
|
||||||
to an Open API compliant API.
|
|
||||||
|
|
||||||
The tools created by this toolkit may be able to make GET, POST,
|
|
||||||
PATCH, PUT, DELETE requests to any of the exposed endpoints on
|
|
||||||
the API.
|
|
||||||
|
|
||||||
Control access to who can use this toolkit.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
"""
|
|
||||||
|
|
||||||
nla_tools: Sequence[NLATool] = Field(...)
|
|
||||||
"""List of API Endpoint Tools."""
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools for all the API operations."""
|
|
||||||
return list(self.nla_tools)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_http_operation_tools(
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
spec: OpenAPISpec,
|
|
||||||
requests: Optional[Requests] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> List[NLATool]:
|
|
||||||
"""Get the tools for all the API operations."""
|
|
||||||
if not spec.paths:
|
|
||||||
return []
|
|
||||||
http_operation_tools = []
|
|
||||||
for path in spec.paths:
|
|
||||||
for method in spec.get_methods_for_path(path):
|
|
||||||
endpoint_tool = NLATool.from_llm_and_method(
|
|
||||||
llm=llm,
|
|
||||||
path=path,
|
|
||||||
method=method,
|
|
||||||
spec=spec,
|
|
||||||
requests=requests,
|
|
||||||
verbose=verbose,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
http_operation_tools.append(endpoint_tool)
|
|
||||||
return http_operation_tools
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_llm_and_spec(
|
|
||||||
cls,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
spec: OpenAPISpec,
|
|
||||||
requests: Optional[Requests] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> NLAToolkit:
|
|
||||||
"""Instantiate the toolkit by creating tools for each operation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
spec: The OpenAPI spec.
|
|
||||||
requests: Optional requests object. Default is None.
|
|
||||||
verbose: Whether to print verbose output. Default is False.
|
|
||||||
kwargs: Additional arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The toolkit.
|
|
||||||
"""
|
|
||||||
http_operation_tools = cls._get_http_operation_tools(
|
|
||||||
llm=llm, spec=spec, requests=requests, verbose=verbose, **kwargs
|
|
||||||
)
|
|
||||||
return cls(nla_tools=http_operation_tools)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_llm_and_url(
|
|
||||||
cls,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
open_api_url: str,
|
|
||||||
requests: Optional[Requests] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> NLAToolkit:
|
|
||||||
"""Instantiate the toolkit from an OpenAPI Spec URL.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
open_api_url: The URL of the OpenAPI spec.
|
|
||||||
requests: Optional requests object. Default is None.
|
|
||||||
verbose: Whether to print verbose output. Default is False.
|
|
||||||
kwargs: Additional arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The toolkit.
|
|
||||||
"""
|
|
||||||
|
|
||||||
spec = OpenAPISpec.from_url(open_api_url)
|
|
||||||
return cls.from_llm_and_spec(
|
|
||||||
llm=llm, spec=spec, requests=requests, verbose=verbose, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_llm_and_ai_plugin(
|
|
||||||
cls,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
ai_plugin: AIPlugin,
|
|
||||||
requests: Optional[Requests] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> NLAToolkit:
|
|
||||||
"""Instantiate the toolkit from an OpenAPI Spec URL"""
|
|
||||||
spec = OpenAPISpec.from_url(ai_plugin.api.url)
|
|
||||||
# TODO: Merge optional Auth information with the `requests` argument
|
|
||||||
return cls.from_llm_and_spec(
|
|
||||||
llm=llm,
|
|
||||||
spec=spec,
|
|
||||||
requests=requests,
|
|
||||||
verbose=verbose,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_llm_and_ai_plugin_url(
|
|
||||||
cls,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
ai_plugin_url: str,
|
|
||||||
requests: Optional[Requests] = None,
|
|
||||||
verbose: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> NLAToolkit:
|
|
||||||
"""Instantiate the toolkit from an OpenAPI Spec URL"""
|
|
||||||
plugin = AIPlugin.from_url(ai_plugin_url)
|
|
||||||
return cls.from_llm_and_ai_plugin(
|
|
||||||
llm=llm, ai_plugin=plugin, requests=requests, verbose=verbose, **kwargs
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
"""Office365 toolkit."""
|
|
@ -1,55 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.office365.create_draft_message import (
|
|
||||||
O365CreateDraftMessage,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.office365.events_search import O365SearchEvents
|
|
||||||
from langchain_community.tools.office365.messages_search import O365SearchEmails
|
|
||||||
from langchain_community.tools.office365.send_event import O365SendEvent
|
|
||||||
from langchain_community.tools.office365.send_message import O365SendMessage
|
|
||||||
from langchain_community.tools.office365.utils import authenticate
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from O365 import Account
|
|
||||||
|
|
||||||
|
|
||||||
class O365Toolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with Office 365.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by reading, creating, updating, deleting
|
|
||||||
data associated with this service.
|
|
||||||
|
|
||||||
For example, this toolkit can be used search through emails and events,
|
|
||||||
send messages and event invites, and create draft messages.
|
|
||||||
|
|
||||||
Please make sure that the permissions given by this toolkit
|
|
||||||
are appropriate for your use case.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
account: Optional. The Office 365 account. Default is None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
account: Account = Field(default_factory=authenticate)
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
O365SearchEvents(),
|
|
||||||
O365CreateDraftMessage(),
|
|
||||||
O365SearchEmails(),
|
|
||||||
O365SendEvent(),
|
|
||||||
O365SendMessage(),
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
"""OpenAPI spec agent."""
|
|
@ -1,106 +0,0 @@
|
|||||||
"""OpenAPI spec agent."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.openapi.prompt import (
|
|
||||||
OPENAPI_PREFIX,
|
|
||||||
OPENAPI_SUFFIX,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.openapi.toolkit import OpenAPIToolkit
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
|
|
||||||
|
|
||||||
def create_openapi_agent(
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
toolkit: OpenAPIToolkit,
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None,
|
|
||||||
prefix: str = OPENAPI_PREFIX,
|
|
||||||
suffix: str = OPENAPI_SUFFIX,
|
|
||||||
format_instructions: Optional[str] = None,
|
|
||||||
input_variables: Optional[List[str]] = None,
|
|
||||||
max_iterations: Optional[int] = 15,
|
|
||||||
max_execution_time: Optional[float] = None,
|
|
||||||
early_stopping_method: str = "force",
|
|
||||||
verbose: bool = False,
|
|
||||||
return_intermediate_steps: bool = False,
|
|
||||||
agent_executor_kwargs: Optional[Dict[str, Any]] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AgentExecutor:
|
|
||||||
"""Construct an OpenAPI agent from an LLM and tools.
|
|
||||||
|
|
||||||
*Security Note*: When creating an OpenAPI agent, check the permissions
|
|
||||||
and capabilities of the underlying toolkit.
|
|
||||||
|
|
||||||
For example, if the default implementation of OpenAPIToolkit
|
|
||||||
uses the RequestsToolkit which contains tools to make arbitrary
|
|
||||||
network requests against any URL (e.g., GET, POST, PATCH, PUT, DELETE),
|
|
||||||
|
|
||||||
Control access to who can submit issue requests using this toolkit and
|
|
||||||
what network access it has.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
toolkit: The OpenAPI toolkit.
|
|
||||||
callback_manager: Optional. The callback manager. Default is None.
|
|
||||||
prefix: Optional. The prefix for the prompt. Default is OPENAPI_PREFIX.
|
|
||||||
suffix: Optional. The suffix for the prompt. Default is OPENAPI_SUFFIX.
|
|
||||||
format_instructions: Optional. The format instructions for the prompt.
|
|
||||||
Default is None.
|
|
||||||
input_variables: Optional. The input variables for the prompt. Default is None.
|
|
||||||
max_iterations: Optional. The maximum number of iterations. Default is 15.
|
|
||||||
max_execution_time: Optional. The maximum execution time. Default is None.
|
|
||||||
early_stopping_method: Optional. The early stopping method. Default is "force".
|
|
||||||
verbose: Optional. Whether to print verbose output. Default is False.
|
|
||||||
return_intermediate_steps: Optional. Whether to return intermediate steps.
|
|
||||||
Default is False.
|
|
||||||
agent_executor_kwargs: Optional. Additional keyword arguments
|
|
||||||
for the agent executor.
|
|
||||||
kwargs: Additional arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The agent executor.
|
|
||||||
"""
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
from langchain.agents.mrkl.base import ZeroShotAgent
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
prompt_params = (
|
|
||||||
{"format_instructions": format_instructions}
|
|
||||||
if format_instructions is not None
|
|
||||||
else {}
|
|
||||||
)
|
|
||||||
prompt = ZeroShotAgent.create_prompt(
|
|
||||||
tools,
|
|
||||||
prefix=prefix,
|
|
||||||
suffix=suffix,
|
|
||||||
input_variables=input_variables,
|
|
||||||
**prompt_params,
|
|
||||||
)
|
|
||||||
llm_chain = LLMChain(
|
|
||||||
llm=llm,
|
|
||||||
prompt=prompt,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
)
|
|
||||||
tool_names = [tool.name for tool in tools]
|
|
||||||
agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names, **kwargs)
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
|
||||||
agent=agent,
|
|
||||||
tools=tools,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
verbose=verbose,
|
|
||||||
return_intermediate_steps=return_intermediate_steps,
|
|
||||||
max_iterations=max_iterations,
|
|
||||||
max_execution_time=max_execution_time,
|
|
||||||
early_stopping_method=early_stopping_method,
|
|
||||||
**(agent_executor_kwargs or {}),
|
|
||||||
)
|
|
@ -1,464 +0,0 @@
|
|||||||
"""Agent that interacts with OpenAPI APIs via a hierarchical planning approach."""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
from functools import partial
|
|
||||||
from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, cast
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.prompts import BasePromptTemplate, PromptTemplate
|
|
||||||
from langchain_core.tools import BaseTool, Tool
|
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.openapi.planner_prompt import (
|
|
||||||
API_CONTROLLER_PROMPT,
|
|
||||||
API_CONTROLLER_TOOL_DESCRIPTION,
|
|
||||||
API_CONTROLLER_TOOL_NAME,
|
|
||||||
API_ORCHESTRATOR_PROMPT,
|
|
||||||
API_PLANNER_PROMPT,
|
|
||||||
API_PLANNER_TOOL_DESCRIPTION,
|
|
||||||
API_PLANNER_TOOL_NAME,
|
|
||||||
PARSING_DELETE_PROMPT,
|
|
||||||
PARSING_GET_PROMPT,
|
|
||||||
PARSING_PATCH_PROMPT,
|
|
||||||
PARSING_POST_PROMPT,
|
|
||||||
PARSING_PUT_PROMPT,
|
|
||||||
REQUESTS_DELETE_TOOL_DESCRIPTION,
|
|
||||||
REQUESTS_GET_TOOL_DESCRIPTION,
|
|
||||||
REQUESTS_PATCH_TOOL_DESCRIPTION,
|
|
||||||
REQUESTS_POST_TOOL_DESCRIPTION,
|
|
||||||
REQUESTS_PUT_TOOL_DESCRIPTION,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.openapi.spec import ReducedOpenAPISpec
|
|
||||||
from langchain_community.llms import OpenAI
|
|
||||||
from langchain_community.tools.requests.tool import BaseRequestsTool
|
|
||||||
from langchain_community.utilities.requests import RequestsWrapper
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requests tools with LLM-instructed extraction of truncated responses.
|
|
||||||
#
|
|
||||||
# Of course, truncating so bluntly may lose a lot of valuable
|
|
||||||
# information in the response.
|
|
||||||
# However, the goal for now is to have only a single inference step.
|
|
||||||
MAX_RESPONSE_LENGTH = 5000
|
|
||||||
"""Maximum length of the response to be returned."""
|
|
||||||
|
|
||||||
Operation = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
|
|
||||||
|
|
||||||
|
|
||||||
def _get_default_llm_chain(prompt: BasePromptTemplate) -> Any:
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
return LLMChain(
|
|
||||||
llm=OpenAI(),
|
|
||||||
prompt=prompt,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_default_llm_chain_factory(
|
|
||||||
prompt: BasePromptTemplate,
|
|
||||||
) -> Callable[[], Any]:
|
|
||||||
"""Returns a default LLMChain factory."""
|
|
||||||
return partial(_get_default_llm_chain, prompt)
|
|
||||||
|
|
||||||
|
|
||||||
class RequestsGetToolWithParsing(BaseRequestsTool, BaseTool): # type: ignore[override]
|
|
||||||
"""Requests GET tool with LLM-instructed extraction of truncated responses."""
|
|
||||||
|
|
||||||
name: str = "requests_get"
|
|
||||||
"""Tool name."""
|
|
||||||
description: str = REQUESTS_GET_TOOL_DESCRIPTION
|
|
||||||
"""Tool description."""
|
|
||||||
response_length: int = MAX_RESPONSE_LENGTH
|
|
||||||
"""Maximum length of the response to be returned."""
|
|
||||||
llm_chain: Any = Field(
|
|
||||||
default_factory=_get_default_llm_chain_factory(PARSING_GET_PROMPT)
|
|
||||||
)
|
|
||||||
"""LLMChain used to extract the response."""
|
|
||||||
|
|
||||||
def _run(self, text: str) -> str:
|
|
||||||
from langchain.output_parsers.json import parse_json_markdown
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = parse_json_markdown(text)
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
raise e
|
|
||||||
data_params = data.get("params")
|
|
||||||
response: str = cast(
|
|
||||||
str, self.requests_wrapper.get(data["url"], params=data_params)
|
|
||||||
)
|
|
||||||
response = response[: self.response_length]
|
|
||||||
return self.llm_chain.predict(
|
|
||||||
response=response, instructions=data["output_instructions"]
|
|
||||||
).strip()
|
|
||||||
|
|
||||||
async def _arun(self, text: str) -> str:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class RequestsPostToolWithParsing(BaseRequestsTool, BaseTool): # type: ignore[override]
|
|
||||||
"""Requests POST tool with LLM-instructed extraction of truncated responses."""
|
|
||||||
|
|
||||||
name: str = "requests_post"
|
|
||||||
"""Tool name."""
|
|
||||||
description: str = REQUESTS_POST_TOOL_DESCRIPTION
|
|
||||||
"""Tool description."""
|
|
||||||
response_length: int = MAX_RESPONSE_LENGTH
|
|
||||||
"""Maximum length of the response to be returned."""
|
|
||||||
llm_chain: Any = Field(
|
|
||||||
default_factory=_get_default_llm_chain_factory(PARSING_POST_PROMPT)
|
|
||||||
)
|
|
||||||
"""LLMChain used to extract the response."""
|
|
||||||
|
|
||||||
def _run(self, text: str) -> str:
|
|
||||||
from langchain.output_parsers.json import parse_json_markdown
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = parse_json_markdown(text)
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
raise e
|
|
||||||
response: str = cast(str, self.requests_wrapper.post(data["url"], data["data"]))
|
|
||||||
response = response[: self.response_length]
|
|
||||||
return self.llm_chain.predict(
|
|
||||||
response=response, instructions=data["output_instructions"]
|
|
||||||
).strip()
|
|
||||||
|
|
||||||
async def _arun(self, text: str) -> str:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class RequestsPatchToolWithParsing(BaseRequestsTool, BaseTool): # type: ignore[override]
|
|
||||||
"""Requests PATCH tool with LLM-instructed extraction of truncated responses."""
|
|
||||||
|
|
||||||
name: str = "requests_patch"
|
|
||||||
"""Tool name."""
|
|
||||||
description: str = REQUESTS_PATCH_TOOL_DESCRIPTION
|
|
||||||
"""Tool description."""
|
|
||||||
response_length: int = MAX_RESPONSE_LENGTH
|
|
||||||
"""Maximum length of the response to be returned."""
|
|
||||||
llm_chain: Any = Field(
|
|
||||||
default_factory=_get_default_llm_chain_factory(PARSING_PATCH_PROMPT)
|
|
||||||
)
|
|
||||||
"""LLMChain used to extract the response."""
|
|
||||||
|
|
||||||
def _run(self, text: str) -> str:
|
|
||||||
from langchain.output_parsers.json import parse_json_markdown
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = parse_json_markdown(text)
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
raise e
|
|
||||||
response: str = cast(
|
|
||||||
str, self.requests_wrapper.patch(data["url"], data["data"])
|
|
||||||
)
|
|
||||||
response = response[: self.response_length]
|
|
||||||
return self.llm_chain.predict(
|
|
||||||
response=response, instructions=data["output_instructions"]
|
|
||||||
).strip()
|
|
||||||
|
|
||||||
async def _arun(self, text: str) -> str:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class RequestsPutToolWithParsing(BaseRequestsTool, BaseTool): # type: ignore[override]
|
|
||||||
"""Requests PUT tool with LLM-instructed extraction of truncated responses."""
|
|
||||||
|
|
||||||
name: str = "requests_put"
|
|
||||||
"""Tool name."""
|
|
||||||
description: str = REQUESTS_PUT_TOOL_DESCRIPTION
|
|
||||||
"""Tool description."""
|
|
||||||
response_length: int = MAX_RESPONSE_LENGTH
|
|
||||||
"""Maximum length of the response to be returned."""
|
|
||||||
llm_chain: Any = Field(
|
|
||||||
default_factory=_get_default_llm_chain_factory(PARSING_PUT_PROMPT)
|
|
||||||
)
|
|
||||||
"""LLMChain used to extract the response."""
|
|
||||||
|
|
||||||
def _run(self, text: str) -> str:
|
|
||||||
from langchain.output_parsers.json import parse_json_markdown
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = parse_json_markdown(text)
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
raise e
|
|
||||||
response: str = cast(str, self.requests_wrapper.put(data["url"], data["data"]))
|
|
||||||
response = response[: self.response_length]
|
|
||||||
return self.llm_chain.predict(
|
|
||||||
response=response, instructions=data["output_instructions"]
|
|
||||||
).strip()
|
|
||||||
|
|
||||||
async def _arun(self, text: str) -> str:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class RequestsDeleteToolWithParsing(BaseRequestsTool, BaseTool): # type: ignore[override]
|
|
||||||
"""Tool that sends a DELETE request and parses the response."""
|
|
||||||
|
|
||||||
name: str = "requests_delete"
|
|
||||||
"""The name of the tool."""
|
|
||||||
description: str = REQUESTS_DELETE_TOOL_DESCRIPTION
|
|
||||||
"""The description of the tool."""
|
|
||||||
|
|
||||||
response_length: Optional[int] = MAX_RESPONSE_LENGTH
|
|
||||||
"""The maximum length of the response."""
|
|
||||||
llm_chain: Any = Field(
|
|
||||||
default_factory=_get_default_llm_chain_factory(PARSING_DELETE_PROMPT)
|
|
||||||
)
|
|
||||||
"""The LLM chain used to parse the response."""
|
|
||||||
|
|
||||||
def _run(self, text: str) -> str:
|
|
||||||
from langchain.output_parsers.json import parse_json_markdown
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = parse_json_markdown(text)
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
raise e
|
|
||||||
response: str = cast(str, self.requests_wrapper.delete(data["url"]))
|
|
||||||
response = response[: self.response_length]
|
|
||||||
return self.llm_chain.predict(
|
|
||||||
response=response, instructions=data["output_instructions"]
|
|
||||||
).strip()
|
|
||||||
|
|
||||||
async def _arun(self, text: str) -> str:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Orchestrator, planner, controller.
|
|
||||||
#
|
|
||||||
def _create_api_planner_tool(
|
|
||||||
api_spec: ReducedOpenAPISpec, llm: BaseLanguageModel
|
|
||||||
) -> Tool:
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
endpoint_descriptions = [
|
|
||||||
f"{name} {description}" for name, description, _ in api_spec.endpoints
|
|
||||||
]
|
|
||||||
prompt = PromptTemplate(
|
|
||||||
template=API_PLANNER_PROMPT,
|
|
||||||
input_variables=["query"],
|
|
||||||
partial_variables={"endpoints": "- " + "- ".join(endpoint_descriptions)},
|
|
||||||
)
|
|
||||||
chain = LLMChain(llm=llm, prompt=prompt)
|
|
||||||
tool = Tool(
|
|
||||||
name=API_PLANNER_TOOL_NAME,
|
|
||||||
description=API_PLANNER_TOOL_DESCRIPTION,
|
|
||||||
func=chain.run,
|
|
||||||
)
|
|
||||||
return tool
|
|
||||||
|
|
||||||
|
|
||||||
def _create_api_controller_agent(
|
|
||||||
api_url: str,
|
|
||||||
api_docs: str,
|
|
||||||
requests_wrapper: RequestsWrapper,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
allow_dangerous_requests: bool,
|
|
||||||
allowed_operations: Sequence[Operation],
|
|
||||||
) -> Any:
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
from langchain.agents.mrkl.base import ZeroShotAgent
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
if "GET" in allowed_operations:
|
|
||||||
get_llm_chain = LLMChain(llm=llm, prompt=PARSING_GET_PROMPT)
|
|
||||||
tools.append(
|
|
||||||
RequestsGetToolWithParsing( # type: ignore[call-arg]
|
|
||||||
requests_wrapper=requests_wrapper,
|
|
||||||
llm_chain=get_llm_chain,
|
|
||||||
allow_dangerous_requests=allow_dangerous_requests,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "POST" in allowed_operations:
|
|
||||||
post_llm_chain = LLMChain(llm=llm, prompt=PARSING_POST_PROMPT)
|
|
||||||
tools.append(
|
|
||||||
RequestsPostToolWithParsing( # type: ignore[call-arg]
|
|
||||||
requests_wrapper=requests_wrapper,
|
|
||||||
llm_chain=post_llm_chain,
|
|
||||||
allow_dangerous_requests=allow_dangerous_requests,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "PUT" in allowed_operations:
|
|
||||||
put_llm_chain = LLMChain(llm=llm, prompt=PARSING_PUT_PROMPT)
|
|
||||||
tools.append(
|
|
||||||
RequestsPutToolWithParsing( # type: ignore[call-arg]
|
|
||||||
requests_wrapper=requests_wrapper,
|
|
||||||
llm_chain=put_llm_chain,
|
|
||||||
allow_dangerous_requests=allow_dangerous_requests,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "DELETE" in allowed_operations:
|
|
||||||
delete_llm_chain = LLMChain(llm=llm, prompt=PARSING_DELETE_PROMPT)
|
|
||||||
tools.append(
|
|
||||||
RequestsDeleteToolWithParsing( # type: ignore[call-arg]
|
|
||||||
requests_wrapper=requests_wrapper,
|
|
||||||
llm_chain=delete_llm_chain,
|
|
||||||
allow_dangerous_requests=allow_dangerous_requests,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "PATCH" in allowed_operations:
|
|
||||||
patch_llm_chain = LLMChain(llm=llm, prompt=PARSING_PATCH_PROMPT)
|
|
||||||
tools.append(
|
|
||||||
RequestsPatchToolWithParsing( # type: ignore[call-arg]
|
|
||||||
requests_wrapper=requests_wrapper,
|
|
||||||
llm_chain=patch_llm_chain,
|
|
||||||
allow_dangerous_requests=allow_dangerous_requests,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if not tools:
|
|
||||||
raise ValueError("Tools not found")
|
|
||||||
prompt = PromptTemplate(
|
|
||||||
template=API_CONTROLLER_PROMPT,
|
|
||||||
input_variables=["input", "agent_scratchpad"],
|
|
||||||
partial_variables={
|
|
||||||
"api_url": api_url,
|
|
||||||
"api_docs": api_docs,
|
|
||||||
"tool_names": ", ".join([tool.name for tool in tools]),
|
|
||||||
"tool_descriptions": "\n".join(
|
|
||||||
[f"{tool.name}: {tool.description}" for tool in tools]
|
|
||||||
),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
agent = ZeroShotAgent(
|
|
||||||
llm_chain=LLMChain(llm=llm, prompt=prompt),
|
|
||||||
allowed_tools=[tool.name for tool in tools],
|
|
||||||
)
|
|
||||||
return AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _create_api_controller_tool(
|
|
||||||
api_spec: ReducedOpenAPISpec,
|
|
||||||
requests_wrapper: RequestsWrapper,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
allow_dangerous_requests: bool,
|
|
||||||
allowed_operations: Sequence[Operation],
|
|
||||||
) -> Tool:
|
|
||||||
"""Expose controller as a tool.
|
|
||||||
|
|
||||||
The tool is invoked with a plan from the planner, and dynamically
|
|
||||||
creates a controller agent with relevant documentation only to
|
|
||||||
constrain the context.
|
|
||||||
"""
|
|
||||||
|
|
||||||
base_url = api_spec.servers[0]["url"] # TODO: do better.
|
|
||||||
|
|
||||||
def _create_and_run_api_controller_agent(plan_str: str) -> str:
|
|
||||||
pattern = r"\b(GET|POST|PATCH|DELETE|PUT)\s+(/\S+)*"
|
|
||||||
matches = re.findall(pattern, plan_str)
|
|
||||||
endpoint_names = [
|
|
||||||
"{method} {route}".format(method=method, route=route.split("?")[0])
|
|
||||||
for method, route in matches
|
|
||||||
]
|
|
||||||
docs_str = ""
|
|
||||||
for endpoint_name in endpoint_names:
|
|
||||||
found_match = False
|
|
||||||
for name, _, docs in api_spec.endpoints:
|
|
||||||
regex_name = re.compile(re.sub("\\{.*?\\}", ".*", name))
|
|
||||||
if regex_name.match(endpoint_name):
|
|
||||||
found_match = True
|
|
||||||
docs_str += f"== Docs for {endpoint_name} == \n{yaml.dump(docs)}\n"
|
|
||||||
if not found_match:
|
|
||||||
raise ValueError(f"{endpoint_name} endpoint does not exist.")
|
|
||||||
|
|
||||||
agent = _create_api_controller_agent(
|
|
||||||
base_url,
|
|
||||||
docs_str,
|
|
||||||
requests_wrapper,
|
|
||||||
llm,
|
|
||||||
allow_dangerous_requests,
|
|
||||||
allowed_operations,
|
|
||||||
)
|
|
||||||
return agent.run(plan_str)
|
|
||||||
|
|
||||||
return Tool(
|
|
||||||
name=API_CONTROLLER_TOOL_NAME,
|
|
||||||
func=_create_and_run_api_controller_agent,
|
|
||||||
description=API_CONTROLLER_TOOL_DESCRIPTION,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_openapi_agent(
|
|
||||||
api_spec: ReducedOpenAPISpec,
|
|
||||||
requests_wrapper: RequestsWrapper,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
shared_memory: Optional[Any] = None,
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None,
|
|
||||||
verbose: bool = True,
|
|
||||||
agent_executor_kwargs: Optional[Dict[str, Any]] = None,
|
|
||||||
allow_dangerous_requests: bool = False,
|
|
||||||
allowed_operations: Sequence[Operation] = ("GET", "POST"),
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> Any:
|
|
||||||
"""Construct an OpenAI API planner and controller for a given spec.
|
|
||||||
|
|
||||||
Inject credentials via requests_wrapper.
|
|
||||||
|
|
||||||
We use a top-level "orchestrator" agent to invoke the planner and controller,
|
|
||||||
rather than a top-level planner
|
|
||||||
that invokes a controller with its plan. This is to keep the planner simple.
|
|
||||||
|
|
||||||
You need to set allow_dangerous_requests to True to use Agent with BaseRequestsTool.
|
|
||||||
Requests can be dangerous and can lead to security vulnerabilities.
|
|
||||||
For example, users can ask a server to make a request to an internal
|
|
||||||
server. It's recommended to use requests through a proxy server
|
|
||||||
and avoid accepting inputs from untrusted sources without proper sandboxing.
|
|
||||||
Please see: https://python.langchain.com/docs/security
|
|
||||||
for further security information.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
api_spec: The OpenAPI spec.
|
|
||||||
requests_wrapper: The requests wrapper.
|
|
||||||
llm: The language model.
|
|
||||||
shared_memory: Optional. The shared memory. Default is None.
|
|
||||||
callback_manager: Optional. The callback manager. Default is None.
|
|
||||||
verbose: Optional. Whether to print verbose output. Default is True.
|
|
||||||
agent_executor_kwargs: Optional. Additional keyword arguments
|
|
||||||
for the agent executor.
|
|
||||||
allow_dangerous_requests: Optional. Whether to allow dangerous requests.
|
|
||||||
Default is False.
|
|
||||||
allowed_operations: Optional. The allowed operations.
|
|
||||||
Default is ("GET", "POST").
|
|
||||||
kwargs: Additional arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The agent executor.
|
|
||||||
"""
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
from langchain.agents.mrkl.base import ZeroShotAgent
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
tools = [
|
|
||||||
_create_api_planner_tool(api_spec, llm),
|
|
||||||
_create_api_controller_tool(
|
|
||||||
api_spec,
|
|
||||||
requests_wrapper,
|
|
||||||
llm,
|
|
||||||
allow_dangerous_requests,
|
|
||||||
allowed_operations,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
prompt = PromptTemplate(
|
|
||||||
template=API_ORCHESTRATOR_PROMPT,
|
|
||||||
input_variables=["input", "agent_scratchpad"],
|
|
||||||
partial_variables={
|
|
||||||
"tool_names": ", ".join([tool.name for tool in tools]),
|
|
||||||
"tool_descriptions": "\n".join(
|
|
||||||
[f"{tool.name}: {tool.description}" for tool in tools]
|
|
||||||
),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
agent = ZeroShotAgent(
|
|
||||||
llm_chain=LLMChain(llm=llm, prompt=prompt, memory=shared_memory),
|
|
||||||
allowed_tools=[tool.name for tool in tools],
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
|
||||||
agent=agent,
|
|
||||||
tools=tools,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
verbose=verbose,
|
|
||||||
**(agent_executor_kwargs or {}),
|
|
||||||
)
|
|
@ -1,235 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
from langchain_core.prompts.prompt import PromptTemplate
|
|
||||||
|
|
||||||
|
|
||||||
API_PLANNER_PROMPT = """You are a planner that plans a sequence of API calls to assist with user queries against an API.
|
|
||||||
|
|
||||||
You should:
|
|
||||||
1) evaluate whether the user query can be solved by the API documented below. If no, say why.
|
|
||||||
2) if yes, generate a plan of API calls and say what they are doing step by step.
|
|
||||||
3) If the plan includes a DELETE call, you should always return an ask from the User for authorization first unless the User has specifically asked to delete something.
|
|
||||||
|
|
||||||
You should only use API endpoints documented below ("Endpoints you can use:").
|
|
||||||
You can only use the DELETE tool if the User has specifically asked to delete something. Otherwise, you should return a request authorization from the User first.
|
|
||||||
Some user queries can be resolved in a single API call, but some will require several API calls.
|
|
||||||
The plan will be passed to an API controller that can format it into web requests and return the responses.
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Here are some examples:
|
|
||||||
|
|
||||||
Fake endpoints for examples:
|
|
||||||
GET /user to get information about the current user
|
|
||||||
GET /products/search search across products
|
|
||||||
POST /users/{{id}}/cart to add products to a user's cart
|
|
||||||
PATCH /users/{{id}}/cart to update a user's cart
|
|
||||||
PUT /users/{{id}}/coupon to apply idempotent coupon to a user's cart
|
|
||||||
DELETE /users/{{id}}/cart to delete a user's cart
|
|
||||||
|
|
||||||
User query: tell me a joke
|
|
||||||
Plan: Sorry, this API's domain is shopping, not comedy.
|
|
||||||
|
|
||||||
User query: I want to buy a couch
|
|
||||||
Plan: 1. GET /products with a query param to search for couches
|
|
||||||
2. GET /user to find the user's id
|
|
||||||
3. POST /users/{{id}}/cart to add a couch to the user's cart
|
|
||||||
|
|
||||||
User query: I want to add a lamp to my cart
|
|
||||||
Plan: 1. GET /products with a query param to search for lamps
|
|
||||||
2. GET /user to find the user's id
|
|
||||||
3. PATCH /users/{{id}}/cart to add a lamp to the user's cart
|
|
||||||
|
|
||||||
User query: I want to add a coupon to my cart
|
|
||||||
Plan: 1. GET /user to find the user's id
|
|
||||||
2. PUT /users/{{id}}/coupon to apply the coupon
|
|
||||||
|
|
||||||
User query: I want to delete my cart
|
|
||||||
Plan: 1. GET /user to find the user's id
|
|
||||||
2. DELETE required. Did user specify DELETE or previously authorize? Yes, proceed.
|
|
||||||
3. DELETE /users/{{id}}/cart to delete the user's cart
|
|
||||||
|
|
||||||
User query: I want to start a new cart
|
|
||||||
Plan: 1. GET /user to find the user's id
|
|
||||||
2. DELETE required. Did user specify DELETE or previously authorize? No, ask for authorization.
|
|
||||||
3. Are you sure you want to delete your cart?
|
|
||||||
----
|
|
||||||
|
|
||||||
Here are endpoints you can use. Do not reference any of the endpoints above.
|
|
||||||
|
|
||||||
{endpoints}
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
User query: {query}
|
|
||||||
Plan:"""
|
|
||||||
API_PLANNER_TOOL_NAME = "api_planner"
|
|
||||||
API_PLANNER_TOOL_DESCRIPTION = f"Can be used to generate the right API calls to assist with a user query, like {API_PLANNER_TOOL_NAME}(query). Should always be called before trying to call the API controller."
|
|
||||||
|
|
||||||
# Execution.
|
|
||||||
API_CONTROLLER_PROMPT = """You are an agent that gets a sequence of API calls and given their documentation, should execute them and return the final response.
|
|
||||||
If you cannot complete them and run into issues, you should explain the issue. If you're unable to resolve an API call, you can retry the API call. When interacting with API objects, you should extract ids for inputs to other API calls but ids and names for outputs returned to the User.
|
|
||||||
|
|
||||||
|
|
||||||
Here is documentation on the API:
|
|
||||||
Base url: {api_url}
|
|
||||||
Endpoints:
|
|
||||||
{api_docs}
|
|
||||||
|
|
||||||
|
|
||||||
Here are tools to execute requests against the API: {tool_descriptions}
|
|
||||||
|
|
||||||
|
|
||||||
Starting below, you should follow this format:
|
|
||||||
|
|
||||||
Plan: the plan of API calls to execute
|
|
||||||
Thought: you should always think about what to do
|
|
||||||
Action: the action to take, should be one of the tools [{tool_names}]
|
|
||||||
Action Input: the input to the action
|
|
||||||
Observation: the output of the action
|
|
||||||
... (this Thought/Action/Action Input/Observation can repeat N times)
|
|
||||||
Thought: I am finished executing the plan (or, I cannot finish executing the plan without knowing some other information.)
|
|
||||||
Final Answer: the final output from executing the plan or missing information I'd need to re-plan correctly.
|
|
||||||
|
|
||||||
|
|
||||||
Begin!
|
|
||||||
|
|
||||||
Plan: {input}
|
|
||||||
Thought:
|
|
||||||
{agent_scratchpad}
|
|
||||||
"""
|
|
||||||
API_CONTROLLER_TOOL_NAME = "api_controller"
|
|
||||||
API_CONTROLLER_TOOL_DESCRIPTION = f"Can be used to execute a plan of API calls, like {API_CONTROLLER_TOOL_NAME}(plan)."
|
|
||||||
|
|
||||||
# Orchestrate planning + execution.
|
|
||||||
# The goal is to have an agent at the top-level (e.g. so it can recover from errors and re-plan) while
|
|
||||||
# keeping planning (and specifically the planning prompt) simple.
|
|
||||||
API_ORCHESTRATOR_PROMPT = """You are an agent that assists with user queries against API, things like querying information or creating resources.
|
|
||||||
Some user queries can be resolved in a single API call, particularly if you can find appropriate params from the OpenAPI spec; though some require several API calls.
|
|
||||||
You should always plan your API calls first, and then execute the plan second.
|
|
||||||
If the plan includes a DELETE call, be sure to ask the User for authorization first unless the User has specifically asked to delete something.
|
|
||||||
You should never return information without executing the api_controller tool.
|
|
||||||
|
|
||||||
|
|
||||||
Here are the tools to plan and execute API requests: {tool_descriptions}
|
|
||||||
|
|
||||||
|
|
||||||
Starting below, you should follow this format:
|
|
||||||
|
|
||||||
User query: the query a User wants help with related to the API
|
|
||||||
Thought: you should always think about what to do
|
|
||||||
Action: the action to take, should be one of the tools [{tool_names}]
|
|
||||||
Action Input: the input to the action
|
|
||||||
Observation: the result of the action
|
|
||||||
... (this Thought/Action/Action Input/Observation can repeat N times)
|
|
||||||
Thought: I am finished executing a plan and have the information the user asked for or the data the user asked to create
|
|
||||||
Final Answer: the final output from executing the plan
|
|
||||||
|
|
||||||
|
|
||||||
Example:
|
|
||||||
User query: can you add some trendy stuff to my shopping cart.
|
|
||||||
Thought: I should plan API calls first.
|
|
||||||
Action: api_planner
|
|
||||||
Action Input: I need to find the right API calls to add trendy items to the users shopping cart
|
|
||||||
Observation: 1) GET /items with params 'trending' is 'True' to get trending item ids
|
|
||||||
2) GET /user to get user
|
|
||||||
3) POST /cart to post the trending items to the user's cart
|
|
||||||
Thought: I'm ready to execute the API calls.
|
|
||||||
Action: api_controller
|
|
||||||
Action Input: 1) GET /items params 'trending' is 'True' to get trending item ids
|
|
||||||
2) GET /user to get user
|
|
||||||
3) POST /cart to post the trending items to the user's cart
|
|
||||||
...
|
|
||||||
|
|
||||||
Begin!
|
|
||||||
|
|
||||||
User query: {input}
|
|
||||||
Thought: I should generate a plan to help with this query and then copy that plan exactly to the controller.
|
|
||||||
{agent_scratchpad}"""
|
|
||||||
|
|
||||||
REQUESTS_GET_TOOL_DESCRIPTION = """Use this to GET content from a website.
|
|
||||||
Input to the tool should be a json string with 3 keys: "url", "params" and "output_instructions".
|
|
||||||
The value of "url" should be a string.
|
|
||||||
The value of "params" should be a dict of the needed and available parameters from the OpenAPI spec related to the endpoint.
|
|
||||||
If parameters are not needed, or not available, leave it empty.
|
|
||||||
The value of "output_instructions" should be instructions on what information to extract from the response,
|
|
||||||
for example the id(s) for a resource(s) that the GET request fetches.
|
|
||||||
"""
|
|
||||||
|
|
||||||
PARSING_GET_PROMPT = PromptTemplate(
|
|
||||||
template="""Here is an API response:\n\n{response}\n\n====
|
|
||||||
Your task is to extract some information according to these instructions: {instructions}
|
|
||||||
When working with API objects, you should usually use ids over names.
|
|
||||||
If the response indicates an error, you should instead output a summary of the error.
|
|
||||||
|
|
||||||
Output:""",
|
|
||||||
input_variables=["response", "instructions"],
|
|
||||||
)
|
|
||||||
|
|
||||||
REQUESTS_POST_TOOL_DESCRIPTION = """Use this when you want to POST to a website.
|
|
||||||
Input to the tool should be a json string with 3 keys: "url", "data", and "output_instructions".
|
|
||||||
The value of "url" should be a string.
|
|
||||||
The value of "data" should be a dictionary of key-value pairs you want to POST to the url.
|
|
||||||
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the POST request creates.
|
|
||||||
Always use double quotes for strings in the json string."""
|
|
||||||
|
|
||||||
PARSING_POST_PROMPT = PromptTemplate(
|
|
||||||
template="""Here is an API response:\n\n{response}\n\n====
|
|
||||||
Your task is to extract some information according to these instructions: {instructions}
|
|
||||||
When working with API objects, you should usually use ids over names. Do not return any ids or names that are not in the response.
|
|
||||||
If the response indicates an error, you should instead output a summary of the error.
|
|
||||||
|
|
||||||
Output:""",
|
|
||||||
input_variables=["response", "instructions"],
|
|
||||||
)
|
|
||||||
|
|
||||||
REQUESTS_PATCH_TOOL_DESCRIPTION = """Use this when you want to PATCH content on a website.
|
|
||||||
Input to the tool should be a json string with 3 keys: "url", "data", and "output_instructions".
|
|
||||||
The value of "url" should be a string.
|
|
||||||
The value of "data" should be a dictionary of key-value pairs of the body params available in the OpenAPI spec you want to PATCH the content with at the url.
|
|
||||||
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the PATCH request creates.
|
|
||||||
Always use double quotes for strings in the json string."""
|
|
||||||
|
|
||||||
PARSING_PATCH_PROMPT = PromptTemplate(
|
|
||||||
template="""Here is an API response:\n\n{response}\n\n====
|
|
||||||
Your task is to extract some information according to these instructions: {instructions}
|
|
||||||
When working with API objects, you should usually use ids over names. Do not return any ids or names that are not in the response.
|
|
||||||
If the response indicates an error, you should instead output a summary of the error.
|
|
||||||
|
|
||||||
Output:""",
|
|
||||||
input_variables=["response", "instructions"],
|
|
||||||
)
|
|
||||||
|
|
||||||
REQUESTS_PUT_TOOL_DESCRIPTION = """Use this when you want to PUT to a website.
|
|
||||||
Input to the tool should be a json string with 3 keys: "url", "data", and "output_instructions".
|
|
||||||
The value of "url" should be a string.
|
|
||||||
The value of "data" should be a dictionary of key-value pairs you want to PUT to the url.
|
|
||||||
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the PUT request creates.
|
|
||||||
Always use double quotes for strings in the json string."""
|
|
||||||
|
|
||||||
PARSING_PUT_PROMPT = PromptTemplate(
|
|
||||||
template="""Here is an API response:\n\n{response}\n\n====
|
|
||||||
Your task is to extract some information according to these instructions: {instructions}
|
|
||||||
When working with API objects, you should usually use ids over names. Do not return any ids or names that are not in the response.
|
|
||||||
If the response indicates an error, you should instead output a summary of the error.
|
|
||||||
|
|
||||||
Output:""",
|
|
||||||
input_variables=["response", "instructions"],
|
|
||||||
)
|
|
||||||
|
|
||||||
REQUESTS_DELETE_TOOL_DESCRIPTION = """ONLY USE THIS TOOL WHEN THE USER HAS SPECIFICALLY REQUESTED TO DELETE CONTENT FROM A WEBSITE.
|
|
||||||
Input to the tool should be a json string with 2 keys: "url", and "output_instructions".
|
|
||||||
The value of "url" should be a string.
|
|
||||||
The value of "output_instructions" should be instructions on what information to extract from the response, for example the id(s) for a resource(s) that the DELETE request creates.
|
|
||||||
Always use double quotes for strings in the json string.
|
|
||||||
ONLY USE THIS TOOL IF THE USER HAS SPECIFICALLY REQUESTED TO DELETE SOMETHING."""
|
|
||||||
|
|
||||||
PARSING_DELETE_PROMPT = PromptTemplate(
|
|
||||||
template="""Here is an API response:\n\n{response}\n\n====
|
|
||||||
Your task is to extract some information according to these instructions: {instructions}
|
|
||||||
When working with API objects, you should usually use ids over names. Do not return any ids or names that are not in the response.
|
|
||||||
If the response indicates an error, you should instead output a summary of the error.
|
|
||||||
|
|
||||||
Output:""",
|
|
||||||
input_variables=["response", "instructions"],
|
|
||||||
)
|
|
@ -1,29 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
OPENAPI_PREFIX = """You are an agent designed to answer questions by making web requests to an API given the openapi spec.
|
|
||||||
|
|
||||||
If the question does not seem related to the API, return I don't know. Do not make up an answer.
|
|
||||||
Only use information provided by the tools to construct your response.
|
|
||||||
|
|
||||||
First, find the base URL needed to make the request.
|
|
||||||
|
|
||||||
Second, find the relevant paths needed to answer the question. Take note that, sometimes, you might need to make more than one request to more than one path to answer the question.
|
|
||||||
|
|
||||||
Third, find the required parameters needed to make the request. For GET requests, these are usually URL parameters and for POST requests, these are request body parameters.
|
|
||||||
|
|
||||||
Fourth, make the requests needed to answer the question. Ensure that you are sending the correct parameters to the request by checking which parameters are required. For parameters with a fixed set of values, please use the spec to look at which values are allowed.
|
|
||||||
|
|
||||||
Use the exact parameter names as listed in the spec, do not make up any names or abbreviate the names of parameters.
|
|
||||||
If you get a not found error, ensure that you are using a path that actually exists in the spec.
|
|
||||||
"""
|
|
||||||
OPENAPI_SUFFIX = """Begin!
|
|
||||||
|
|
||||||
Question: {input}
|
|
||||||
Thought: I should explore the spec to find the base server url for the API in the servers node.
|
|
||||||
{agent_scratchpad}"""
|
|
||||||
|
|
||||||
DESCRIPTION = """Can be used to answer questions about the openapi spec for the API. Always use this tool before trying to make a request.
|
|
||||||
Example inputs to this tool:
|
|
||||||
'What are the required query parameters for a GET request to the /bar endpoint?`
|
|
||||||
'What are the required parameters in the request body for a POST request to the /foo endpoint?'
|
|
||||||
Always give this tool a specific question."""
|
|
@ -1,82 +0,0 @@
|
|||||||
"""Quick and dirty representation for OpenAPI specs."""
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import List, Tuple
|
|
||||||
|
|
||||||
from langchain_core.utils.json_schema import dereference_refs
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ReducedOpenAPISpec:
|
|
||||||
"""A reduced OpenAPI spec.
|
|
||||||
|
|
||||||
This is a quick and dirty representation for OpenAPI specs.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
servers: The servers in the spec.
|
|
||||||
description: The description of the spec.
|
|
||||||
endpoints: The endpoints in the spec.
|
|
||||||
"""
|
|
||||||
|
|
||||||
servers: List[dict]
|
|
||||||
description: str
|
|
||||||
endpoints: List[Tuple[str, str, dict]]
|
|
||||||
|
|
||||||
|
|
||||||
def reduce_openapi_spec(spec: dict, dereference: bool = True) -> ReducedOpenAPISpec:
|
|
||||||
"""Simplify/distill/minify a spec somehow.
|
|
||||||
|
|
||||||
I want a smaller target for retrieval and (more importantly)
|
|
||||||
I want smaller results from retrieval.
|
|
||||||
I was hoping https://openapi.tools/ would have some useful bits
|
|
||||||
to this end, but doesn't seem so.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
spec: The OpenAPI spec.
|
|
||||||
dereference: Whether to dereference the spec. Default is True.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ReducedOpenAPISpec: The reduced OpenAPI spec.
|
|
||||||
"""
|
|
||||||
# 1. Consider only get, post, patch, put, delete endpoints.
|
|
||||||
endpoints = [
|
|
||||||
(f"{operation_name.upper()} {route}", docs.get("description"), docs)
|
|
||||||
for route, operation in spec["paths"].items()
|
|
||||||
for operation_name, docs in operation.items()
|
|
||||||
if operation_name in ["get", "post", "patch", "put", "delete"]
|
|
||||||
]
|
|
||||||
|
|
||||||
# 2. Replace any refs so that complete docs are retrieved.
|
|
||||||
# Note: probably want to do this post-retrieval, it blows up the size of the spec.
|
|
||||||
if dereference:
|
|
||||||
endpoints = [
|
|
||||||
(name, description, dereference_refs(docs, full_schema=spec))
|
|
||||||
for name, description, docs in endpoints
|
|
||||||
]
|
|
||||||
|
|
||||||
# 3. Strip docs down to required request args + happy path response.
|
|
||||||
def reduce_endpoint_docs(docs: dict) -> dict:
|
|
||||||
out = {}
|
|
||||||
if docs.get("description"):
|
|
||||||
out["description"] = docs.get("description")
|
|
||||||
if docs.get("parameters"):
|
|
||||||
out["parameters"] = [
|
|
||||||
parameter
|
|
||||||
for parameter in docs.get("parameters", [])
|
|
||||||
if parameter.get("required")
|
|
||||||
]
|
|
||||||
if "200" in docs["responses"]:
|
|
||||||
out["responses"] = docs["responses"]["200"]
|
|
||||||
if docs.get("requestBody"):
|
|
||||||
out["requestBody"] = docs.get("requestBody")
|
|
||||||
return out
|
|
||||||
|
|
||||||
endpoints = [
|
|
||||||
(name, description, reduce_endpoint_docs(docs))
|
|
||||||
for name, description, docs in endpoints
|
|
||||||
]
|
|
||||||
return ReducedOpenAPISpec(
|
|
||||||
servers=spec["servers"],
|
|
||||||
description=spec["info"].get("description", ""),
|
|
||||||
endpoints=endpoints,
|
|
||||||
)
|
|
@ -1,238 +0,0 @@
|
|||||||
"""Requests toolkit."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any, List
|
|
||||||
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.tools import BaseTool, Tool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.json.base import create_json_agent
|
|
||||||
from langchain_community.agent_toolkits.json.toolkit import JsonToolkit
|
|
||||||
from langchain_community.agent_toolkits.openapi.prompt import DESCRIPTION
|
|
||||||
from langchain_community.tools.json.tool import JsonSpec
|
|
||||||
from langchain_community.tools.requests.tool import (
|
|
||||||
RequestsDeleteTool,
|
|
||||||
RequestsGetTool,
|
|
||||||
RequestsPatchTool,
|
|
||||||
RequestsPostTool,
|
|
||||||
RequestsPutTool,
|
|
||||||
)
|
|
||||||
from langchain_community.utilities.requests import TextRequestsWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class RequestsToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for making REST requests.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools to make GET, POST, PATCH, PUT,
|
|
||||||
and DELETE requests to an API.
|
|
||||||
|
|
||||||
Exercise care in who is allowed to use this toolkit. If exposing
|
|
||||||
to end users, consider that users will be able to make arbitrary
|
|
||||||
requests on behalf of the server hosting the code. For example,
|
|
||||||
users could ask the server to make a request to a private API
|
|
||||||
that is only accessible from the server.
|
|
||||||
|
|
||||||
Control access to who can submit issue requests using this toolkit and
|
|
||||||
what network access it has.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
Install ``langchain-community``.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pip install -U langchain-community
|
|
||||||
|
|
||||||
Key init args:
|
|
||||||
requests_wrapper: langchain_community.utilities.requests.GenericRequestsWrapper
|
|
||||||
wrapper for executing requests.
|
|
||||||
allow_dangerous_requests: bool
|
|
||||||
Defaults to False. Must "opt-in" to using dangerous requests by setting to True.
|
|
||||||
|
|
||||||
Instantiate:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.openapi.toolkit import RequestsToolkit
|
|
||||||
from langchain_community.utilities.requests import TextRequestsWrapper
|
|
||||||
|
|
||||||
toolkit = RequestsToolkit(
|
|
||||||
requests_wrapper=TextRequestsWrapper(headers={}),
|
|
||||||
allow_dangerous_requests=ALLOW_DANGEROUS_REQUEST,
|
|
||||||
)
|
|
||||||
|
|
||||||
Tools:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
tools
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
[RequestsGetTool(requests_wrapper=TextRequestsWrapper(headers={}, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),
|
|
||||||
RequestsPostTool(requests_wrapper=TextRequestsWrapper(headers={}, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),
|
|
||||||
RequestsPatchTool(requests_wrapper=TextRequestsWrapper(headers={}, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),
|
|
||||||
RequestsPutTool(requests_wrapper=TextRequestsWrapper(headers={}, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),
|
|
||||||
RequestsDeleteTool(requests_wrapper=TextRequestsWrapper(headers={}, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True)]
|
|
||||||
|
|
||||||
Use within an agent:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langgraph.prebuilt import create_react_agent
|
|
||||||
|
|
||||||
|
|
||||||
api_spec = \"\"\"
|
|
||||||
openapi: 3.0.0
|
|
||||||
info:
|
|
||||||
title: JSONPlaceholder API
|
|
||||||
version: 1.0.0
|
|
||||||
servers:
|
|
||||||
- url: https://jsonplaceholder.typicode.com
|
|
||||||
paths:
|
|
||||||
/posts:
|
|
||||||
get:
|
|
||||||
summary: Get posts
|
|
||||||
parameters: &id001
|
|
||||||
- name: _limit
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
example: 2
|
|
||||||
description: Limit the number of results
|
|
||||||
\"\"\"
|
|
||||||
|
|
||||||
system_message = \"\"\"
|
|
||||||
You have access to an API to help answer user queries.
|
|
||||||
Here is documentation on the API:
|
|
||||||
{api_spec}
|
|
||||||
\"\"\".format(api_spec=api_spec)
|
|
||||||
|
|
||||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
|
||||||
agent_executor = create_react_agent(llm, tools, state_modifier=system_message)
|
|
||||||
|
|
||||||
example_query = "Fetch the top two posts. What are their titles?"
|
|
||||||
|
|
||||||
events = agent_executor.stream(
|
|
||||||
{"messages": [("user", example_query)]},
|
|
||||||
stream_mode="values",
|
|
||||||
)
|
|
||||||
for event in events:
|
|
||||||
event["messages"][-1].pretty_print()
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
================================[1m Human Message [0m=================================
|
|
||||||
|
|
||||||
Fetch the top two posts. What are their titles?
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
Tool Calls:
|
|
||||||
requests_get (call_RV2SOyzCnV5h2sm4WPgG8fND)
|
|
||||||
Call ID: call_RV2SOyzCnV5h2sm4WPgG8fND
|
|
||||||
Args:
|
|
||||||
url: https://jsonplaceholder.typicode.com/posts?_limit=2
|
|
||||||
=================================[1m Tool Message [0m=================================
|
|
||||||
Name: requests_get
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"userId": 1,
|
|
||||||
"id": 1,
|
|
||||||
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
|
|
||||||
"body": "quia et suscipit..."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"userId": 1,
|
|
||||||
"id": 2,
|
|
||||||
"title": "qui est esse",
|
|
||||||
"body": "est rerum tempore vitae..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
|
|
||||||
The titles of the top two posts are:
|
|
||||||
1. "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
|
|
||||||
2. "qui est esse"
|
|
||||||
""" # noqa: E501
|
|
||||||
|
|
||||||
requests_wrapper: TextRequestsWrapper
|
|
||||||
"""The requests wrapper."""
|
|
||||||
allow_dangerous_requests: bool = False
|
|
||||||
"""Allow dangerous requests. See documentation for details."""
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Return a list of tools."""
|
|
||||||
return [
|
|
||||||
RequestsGetTool(
|
|
||||||
requests_wrapper=self.requests_wrapper,
|
|
||||||
allow_dangerous_requests=self.allow_dangerous_requests,
|
|
||||||
),
|
|
||||||
RequestsPostTool(
|
|
||||||
requests_wrapper=self.requests_wrapper,
|
|
||||||
allow_dangerous_requests=self.allow_dangerous_requests,
|
|
||||||
),
|
|
||||||
RequestsPatchTool(
|
|
||||||
requests_wrapper=self.requests_wrapper,
|
|
||||||
allow_dangerous_requests=self.allow_dangerous_requests,
|
|
||||||
),
|
|
||||||
RequestsPutTool(
|
|
||||||
requests_wrapper=self.requests_wrapper,
|
|
||||||
allow_dangerous_requests=self.allow_dangerous_requests,
|
|
||||||
),
|
|
||||||
RequestsDeleteTool(
|
|
||||||
requests_wrapper=self.requests_wrapper,
|
|
||||||
allow_dangerous_requests=self.allow_dangerous_requests,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OpenAPIToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with an OpenAPI API.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit contains tools that can read and modify
|
|
||||||
the state of a service; e.g., by creating, deleting, or updating,
|
|
||||||
reading underlying data.
|
|
||||||
|
|
||||||
For example, this toolkit can be used to delete data exposed via
|
|
||||||
an OpenAPI compliant API.
|
|
||||||
"""
|
|
||||||
|
|
||||||
json_agent: Any
|
|
||||||
"""The JSON agent."""
|
|
||||||
requests_wrapper: TextRequestsWrapper
|
|
||||||
"""The requests wrapper."""
|
|
||||||
allow_dangerous_requests: bool = False
|
|
||||||
"""Allow dangerous requests. See documentation for details."""
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
json_agent_tool = Tool(
|
|
||||||
name="json_explorer",
|
|
||||||
func=self.json_agent.run,
|
|
||||||
description=DESCRIPTION,
|
|
||||||
)
|
|
||||||
request_toolkit = RequestsToolkit(
|
|
||||||
requests_wrapper=self.requests_wrapper,
|
|
||||||
allow_dangerous_requests=self.allow_dangerous_requests,
|
|
||||||
)
|
|
||||||
return [*request_toolkit.get_tools(), json_agent_tool]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_llm(
|
|
||||||
cls,
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
json_spec: JsonSpec,
|
|
||||||
requests_wrapper: TextRequestsWrapper,
|
|
||||||
allow_dangerous_requests: bool = False,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> OpenAPIToolkit:
|
|
||||||
"""Create json agent from llm, then initialize."""
|
|
||||||
json_agent = create_json_agent(llm, JsonToolkit(spec=json_spec), **kwargs)
|
|
||||||
return cls(
|
|
||||||
json_agent=json_agent,
|
|
||||||
requests_wrapper=requests_wrapper,
|
|
||||||
allow_dangerous_requests=allow_dangerous_requests,
|
|
||||||
)
|
|
@ -1,7 +0,0 @@
|
|||||||
"""Playwright browser toolkit."""
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.playwright.toolkit import (
|
|
||||||
PlayWrightBrowserToolkit,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = ["PlayWrightBrowserToolkit"]
|
|
@ -1,122 +0,0 @@
|
|||||||
"""Playwright web browser toolkit."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, List, Optional, Type, cast
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool, BaseToolkit
|
|
||||||
from pydantic import ConfigDict, model_validator
|
|
||||||
|
|
||||||
from langchain_community.tools.playwright.base import (
|
|
||||||
BaseBrowserTool,
|
|
||||||
lazy_import_playwright_browsers,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.playwright.click import ClickTool
|
|
||||||
from langchain_community.tools.playwright.current_page import CurrentWebPageTool
|
|
||||||
from langchain_community.tools.playwright.extract_hyperlinks import (
|
|
||||||
ExtractHyperlinksTool,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.playwright.extract_text import ExtractTextTool
|
|
||||||
from langchain_community.tools.playwright.get_elements import GetElementsTool
|
|
||||||
from langchain_community.tools.playwright.navigate import NavigateTool
|
|
||||||
from langchain_community.tools.playwright.navigate_back import NavigateBackTool
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from playwright.async_api import Browser as AsyncBrowser
|
|
||||||
from playwright.sync_api import Browser as SyncBrowser
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
# We do this so pydantic can resolve the types when instantiating
|
|
||||||
from playwright.async_api import Browser as AsyncBrowser
|
|
||||||
from playwright.sync_api import Browser as SyncBrowser
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PlayWrightBrowserToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for PlayWright browser tools.
|
|
||||||
|
|
||||||
**Security Note**: This toolkit provides code to control a web-browser.
|
|
||||||
|
|
||||||
Careful if exposing this toolkit to end-users. The tools in the toolkit
|
|
||||||
are capable of navigating to arbitrary webpages, clicking on arbitrary
|
|
||||||
elements, and extracting arbitrary text and hyperlinks from webpages.
|
|
||||||
|
|
||||||
Specifically, by default this toolkit allows navigating to:
|
|
||||||
|
|
||||||
- Any URL (including any internal network URLs)
|
|
||||||
- And local files
|
|
||||||
|
|
||||||
If exposing to end-users, consider limiting network access to the
|
|
||||||
server that hosts the agent; in addition, consider it is advised
|
|
||||||
to create a custom NavigationTool wht an args_schema that limits the URLs
|
|
||||||
that can be navigated to (e.g., only allow navigating to URLs that
|
|
||||||
start with a particular prefix).
|
|
||||||
|
|
||||||
Remember to scope permissions to the minimal permissions necessary for
|
|
||||||
the application. If the default tool selection is not appropriate for
|
|
||||||
the application, consider creating a custom toolkit with the appropriate
|
|
||||||
tools.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
sync_browser: Optional. The sync browser. Default is None.
|
|
||||||
async_browser: Optional. The async browser. Default is None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
sync_browser: Optional["SyncBrowser"] = None
|
|
||||||
async_browser: Optional["AsyncBrowser"] = None
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
extra="forbid",
|
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
|
||||||
@classmethod
|
|
||||||
def validate_imports_and_browser_provided(cls, values: dict) -> Any:
|
|
||||||
"""Check that the arguments are valid."""
|
|
||||||
lazy_import_playwright_browsers()
|
|
||||||
if values.get("async_browser") is None and values.get("sync_browser") is None:
|
|
||||||
raise ValueError("Either async_browser or sync_browser must be specified.")
|
|
||||||
return values
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
tool_classes: List[Type[BaseBrowserTool]] = [
|
|
||||||
ClickTool,
|
|
||||||
NavigateTool,
|
|
||||||
NavigateBackTool,
|
|
||||||
ExtractTextTool,
|
|
||||||
ExtractHyperlinksTool,
|
|
||||||
GetElementsTool,
|
|
||||||
CurrentWebPageTool,
|
|
||||||
]
|
|
||||||
|
|
||||||
tools = [
|
|
||||||
tool_cls.from_browser(
|
|
||||||
sync_browser=self.sync_browser, async_browser=self.async_browser
|
|
||||||
)
|
|
||||||
for tool_cls in tool_classes
|
|
||||||
]
|
|
||||||
return cast(List[BaseTool], tools)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_browser(
|
|
||||||
cls,
|
|
||||||
sync_browser: Optional[SyncBrowser] = None,
|
|
||||||
async_browser: Optional[AsyncBrowser] = None,
|
|
||||||
) -> PlayWrightBrowserToolkit:
|
|
||||||
"""Instantiate the toolkit.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sync_browser: Optional. The sync browser. Default is None.
|
|
||||||
async_browser: Optional. The async browser. Default is None.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The toolkit.
|
|
||||||
"""
|
|
||||||
# This is to raise a better error than the forward ref ones Pydantic would have
|
|
||||||
lazy_import_playwright_browsers()
|
|
||||||
return cls(sync_browser=sync_browser, async_browser=async_browser)
|
|
@ -1 +0,0 @@
|
|||||||
"""Polygon Toolkit"""
|
|
@ -1,54 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
|
|
||||||
from langchain_community.tools.polygon import (
|
|
||||||
PolygonAggregates,
|
|
||||||
PolygonFinancials,
|
|
||||||
PolygonLastQuote,
|
|
||||||
PolygonTickerNews,
|
|
||||||
)
|
|
||||||
from langchain_community.utilities.polygon import PolygonAPIWrapper
|
|
||||||
|
|
||||||
|
|
||||||
class PolygonToolkit(BaseToolkit):
|
|
||||||
"""Polygon Toolkit.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
tools: List[BaseTool]. The tools in the toolkit.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools: List[BaseTool] = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_polygon_api_wrapper(
|
|
||||||
cls, polygon_api_wrapper: PolygonAPIWrapper
|
|
||||||
) -> "PolygonToolkit":
|
|
||||||
"""Create a Polygon Toolkit from a Polygon API Wrapper.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
polygon_api_wrapper: PolygonAPIWrapper. The Polygon API Wrapper.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
PolygonToolkit. The Polygon Toolkit.
|
|
||||||
"""
|
|
||||||
tools = [
|
|
||||||
PolygonAggregates(
|
|
||||||
api_wrapper=polygon_api_wrapper,
|
|
||||||
),
|
|
||||||
PolygonLastQuote(
|
|
||||||
api_wrapper=polygon_api_wrapper,
|
|
||||||
),
|
|
||||||
PolygonTickerNews(
|
|
||||||
api_wrapper=polygon_api_wrapper,
|
|
||||||
),
|
|
||||||
PolygonFinancials(
|
|
||||||
api_wrapper=polygon_api_wrapper,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
return cls(tools=tools)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return self.tools
|
|
@ -1 +0,0 @@
|
|||||||
"""Power BI agent."""
|
|
@ -1,94 +0,0 @@
|
|||||||
"""Power BI agent."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.powerbi.prompt import (
|
|
||||||
POWERBI_PREFIX,
|
|
||||||
POWERBI_SUFFIX,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.powerbi.toolkit import PowerBIToolkit
|
|
||||||
from langchain_community.utilities.powerbi import PowerBIDataset
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain.agents import AgentExecutor
|
|
||||||
|
|
||||||
|
|
||||||
def create_pbi_agent(
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
toolkit: Optional[PowerBIToolkit] = None,
|
|
||||||
powerbi: Optional[PowerBIDataset] = None,
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None,
|
|
||||||
prefix: str = POWERBI_PREFIX,
|
|
||||||
suffix: str = POWERBI_SUFFIX,
|
|
||||||
format_instructions: Optional[str] = None,
|
|
||||||
examples: Optional[str] = None,
|
|
||||||
input_variables: Optional[List[str]] = None,
|
|
||||||
top_k: int = 10,
|
|
||||||
verbose: bool = False,
|
|
||||||
agent_executor_kwargs: Optional[Dict[str, Any]] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AgentExecutor:
|
|
||||||
"""Construct a Power BI agent from an LLM and tools.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
toolkit: Optional. The Power BI toolkit. Default is None.
|
|
||||||
powerbi: Optional. The Power BI dataset. Default is None.
|
|
||||||
callback_manager: Optional. The callback manager. Default is None.
|
|
||||||
prefix: Optional. The prefix for the prompt. Default is POWERBI_PREFIX.
|
|
||||||
suffix: Optional. The suffix for the prompt. Default is POWERBI_SUFFIX.
|
|
||||||
format_instructions: Optional. The format instructions for the prompt.
|
|
||||||
Default is None.
|
|
||||||
examples: Optional. The examples for the prompt. Default is None.
|
|
||||||
input_variables: Optional. The input variables for the prompt. Default is None.
|
|
||||||
top_k: Optional. The top k for the prompt. Default is 10.
|
|
||||||
verbose: Optional. Whether to print verbose output. Default is False.
|
|
||||||
agent_executor_kwargs: Optional. The agent executor kwargs. Default is None.
|
|
||||||
kwargs: Any. Additional keyword arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The agent executor.
|
|
||||||
"""
|
|
||||||
from langchain.agents import AgentExecutor
|
|
||||||
from langchain.agents.mrkl.base import ZeroShotAgent
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
if toolkit is None:
|
|
||||||
if powerbi is None:
|
|
||||||
raise ValueError("Must provide either a toolkit or powerbi dataset")
|
|
||||||
toolkit = PowerBIToolkit(powerbi=powerbi, llm=llm, examples=examples)
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
tables = powerbi.table_names if powerbi else toolkit.powerbi.table_names
|
|
||||||
prompt_params = (
|
|
||||||
{"format_instructions": format_instructions}
|
|
||||||
if format_instructions is not None
|
|
||||||
else {}
|
|
||||||
)
|
|
||||||
agent = ZeroShotAgent(
|
|
||||||
llm_chain=LLMChain(
|
|
||||||
llm=llm,
|
|
||||||
prompt=ZeroShotAgent.create_prompt(
|
|
||||||
tools,
|
|
||||||
prefix=prefix.format(top_k=top_k).format(tables=tables),
|
|
||||||
suffix=suffix,
|
|
||||||
input_variables=input_variables,
|
|
||||||
**prompt_params,
|
|
||||||
),
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
verbose=verbose,
|
|
||||||
),
|
|
||||||
allowed_tools=[tool.name for tool in tools],
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
|
||||||
agent=agent,
|
|
||||||
tools=tools,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
verbose=verbose,
|
|
||||||
**(agent_executor_kwargs or {}),
|
|
||||||
)
|
|
@ -1,91 +0,0 @@
|
|||||||
"""Power BI agent."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.language_models.chat_models import BaseChatModel
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.powerbi.prompt import (
|
|
||||||
POWERBI_CHAT_PREFIX,
|
|
||||||
POWERBI_CHAT_SUFFIX,
|
|
||||||
)
|
|
||||||
from langchain_community.agent_toolkits.powerbi.toolkit import PowerBIToolkit
|
|
||||||
from langchain_community.utilities.powerbi import PowerBIDataset
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain.agents import AgentExecutor
|
|
||||||
from langchain.agents.agent import AgentOutputParser
|
|
||||||
from langchain.memory.chat_memory import BaseChatMemory
|
|
||||||
|
|
||||||
|
|
||||||
def create_pbi_chat_agent(
|
|
||||||
llm: BaseChatModel,
|
|
||||||
toolkit: Optional[PowerBIToolkit] = None,
|
|
||||||
powerbi: Optional[PowerBIDataset] = None,
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None,
|
|
||||||
output_parser: Optional[AgentOutputParser] = None,
|
|
||||||
prefix: str = POWERBI_CHAT_PREFIX,
|
|
||||||
suffix: str = POWERBI_CHAT_SUFFIX,
|
|
||||||
examples: Optional[str] = None,
|
|
||||||
input_variables: Optional[List[str]] = None,
|
|
||||||
memory: Optional[BaseChatMemory] = None,
|
|
||||||
top_k: int = 10,
|
|
||||||
verbose: bool = False,
|
|
||||||
agent_executor_kwargs: Optional[Dict[str, Any]] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AgentExecutor:
|
|
||||||
"""Construct a Power BI agent from a Chat LLM and tools.
|
|
||||||
|
|
||||||
If you supply only a toolkit and no Power BI dataset, the same LLM is used for both.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
toolkit: Optional. The Power BI toolkit. Default is None.
|
|
||||||
powerbi: Optional. The Power BI dataset. Default is None.
|
|
||||||
callback_manager: Optional. The callback manager. Default is None.
|
|
||||||
output_parser: Optional. The output parser. Default is None.
|
|
||||||
prefix: Optional. The prefix for the prompt. Default is POWERBI_CHAT_PREFIX.
|
|
||||||
suffix: Optional. The suffix for the prompt. Default is POWERBI_CHAT_SUFFIX.
|
|
||||||
examples: Optional. The examples for the prompt. Default is None.
|
|
||||||
input_variables: Optional. The input variables for the prompt. Default is None.
|
|
||||||
memory: Optional. The memory. Default is None.
|
|
||||||
top_k: Optional. The top k for the prompt. Default is 10.
|
|
||||||
verbose: Optional. Whether to print verbose output. Default is False.
|
|
||||||
agent_executor_kwargs: Optional. The agent executor kwargs. Default is None.
|
|
||||||
kwargs: Any. Additional keyword arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The agent executor.
|
|
||||||
"""
|
|
||||||
from langchain.agents import AgentExecutor
|
|
||||||
from langchain.agents.conversational_chat.base import ConversationalChatAgent
|
|
||||||
from langchain.memory import ConversationBufferMemory
|
|
||||||
|
|
||||||
if toolkit is None:
|
|
||||||
if powerbi is None:
|
|
||||||
raise ValueError("Must provide either a toolkit or powerbi dataset")
|
|
||||||
toolkit = PowerBIToolkit(powerbi=powerbi, llm=llm, examples=examples)
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
tables = powerbi.table_names if powerbi else toolkit.powerbi.table_names
|
|
||||||
agent = ConversationalChatAgent.from_llm_and_tools(
|
|
||||||
llm=llm,
|
|
||||||
tools=tools,
|
|
||||||
system_message=prefix.format(top_k=top_k).format(tables=tables),
|
|
||||||
human_message=suffix,
|
|
||||||
input_variables=input_variables,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
output_parser=output_parser,
|
|
||||||
verbose=verbose,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
|
||||||
agent=agent,
|
|
||||||
tools=tools,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
memory=memory
|
|
||||||
or ConversationBufferMemory(memory_key="chat_history", return_messages=True),
|
|
||||||
verbose=verbose,
|
|
||||||
**(agent_executor_kwargs or {}),
|
|
||||||
)
|
|
@ -1,37 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
"""Prompts for PowerBI agent."""
|
|
||||||
|
|
||||||
POWERBI_PREFIX = """You are an agent designed to help users interact with a PowerBI Dataset.
|
|
||||||
|
|
||||||
Agent has access to a tool that can write a query based on the question and then run those against PowerBI, Microsofts business intelligence tool. The questions from the users should be interpreted as related to the dataset that is available and not general questions about the world. If the question does not seem related to the dataset, return "This does not appear to be part of this dataset." as the answer.
|
|
||||||
|
|
||||||
Given an input question, ask to run the questions against the dataset, then look at the results and return the answer, the answer should be a complete sentence that answers the question, if multiple rows are asked find a way to write that in a easily readable format for a human, also make sure to represent numbers in readable ways, like 1M instead of 1000000. Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most {top_k} results.
|
|
||||||
"""
|
|
||||||
|
|
||||||
POWERBI_SUFFIX = """Begin!
|
|
||||||
|
|
||||||
Question: {input}
|
|
||||||
Thought: I can first ask which tables I have, then how each table is defined and then ask the query tool the question I need, and finally create a nice sentence that answers the question.
|
|
||||||
{agent_scratchpad}"""
|
|
||||||
|
|
||||||
POWERBI_CHAT_PREFIX = """Assistant is a large language model built to help users interact with a PowerBI Dataset.
|
|
||||||
|
|
||||||
Assistant should try to create a correct and complete answer to the question from the user. If the user asks a question not related to the dataset it should return "This does not appear to be part of this dataset." as the answer. The user might make a mistake with the spelling of certain values, if you think that is the case, ask the user to confirm the spelling of the value and then run the query again. Unless the user specifies a specific number of examples they wish to obtain, and the results are too large, limit your query to at most {top_k} results, but make it clear when answering which field was used for the filtering. The user has access to these tables: {{tables}}.
|
|
||||||
|
|
||||||
The answer should be a complete sentence that answers the question, if multiple rows are asked find a way to write that in a easily readable format for a human, also make sure to represent numbers in readable ways, like 1M instead of 1000000.
|
|
||||||
"""
|
|
||||||
|
|
||||||
POWERBI_CHAT_SUFFIX = """TOOLS
|
|
||||||
------
|
|
||||||
Assistant can ask the user to use tools to look up information that may be helpful in answering the users original question. The tools the human can use are:
|
|
||||||
|
|
||||||
{{tools}}
|
|
||||||
|
|
||||||
{format_instructions}
|
|
||||||
|
|
||||||
USER'S INPUT
|
|
||||||
--------------------
|
|
||||||
Here is the user's input (remember to respond with a markdown code snippet of a json blob with a single action, and NOTHING else):
|
|
||||||
|
|
||||||
{{{{input}}}}
|
|
||||||
"""
|
|
@ -1,117 +0,0 @@
|
|||||||
"""Toolkit for interacting with a Power BI dataset."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, List, Optional, Union
|
|
||||||
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
from langchain_core.language_models.chat_models import BaseChatModel
|
|
||||||
from langchain_core.prompts import PromptTemplate
|
|
||||||
from langchain_core.prompts.chat import (
|
|
||||||
ChatPromptTemplate,
|
|
||||||
HumanMessagePromptTemplate,
|
|
||||||
SystemMessagePromptTemplate,
|
|
||||||
)
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.powerbi.prompt import (
|
|
||||||
QUESTION_TO_QUERY_BASE,
|
|
||||||
SINGLE_QUESTION_TO_QUERY,
|
|
||||||
USER_INPUT,
|
|
||||||
)
|
|
||||||
from langchain_community.tools.powerbi.tool import (
|
|
||||||
InfoPowerBITool,
|
|
||||||
ListPowerBITool,
|
|
||||||
QueryPowerBITool,
|
|
||||||
)
|
|
||||||
from langchain_community.utilities.powerbi import PowerBIDataset
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
|
|
||||||
class PowerBIToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with Power BI dataset.
|
|
||||||
|
|
||||||
*Security Note*: This toolkit interacts with an external service.
|
|
||||||
|
|
||||||
Control access to who can use this toolkit.
|
|
||||||
|
|
||||||
Make sure that the capabilities given by this toolkit to the calling
|
|
||||||
code are appropriately scoped to the application.
|
|
||||||
|
|
||||||
See https://python.langchain.com/docs/security for more information.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
powerbi: The Power BI dataset.
|
|
||||||
llm: The language model to use.
|
|
||||||
examples: Optional. The examples for the prompt. Default is None.
|
|
||||||
max_iterations: Optional. The maximum iterations to run. Default is 5.
|
|
||||||
callback_manager: Optional. The callback manager. Default is None.
|
|
||||||
output_token_limit: The output token limit. Default is 4000.
|
|
||||||
tiktoken_model_name: Optional. The TikToken model name. Default is None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
powerbi: PowerBIDataset = Field(exclude=True)
|
|
||||||
llm: Union[BaseLanguageModel, BaseChatModel] = Field(exclude=True)
|
|
||||||
examples: Optional[str] = None
|
|
||||||
max_iterations: int = 5
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None
|
|
||||||
output_token_limit: int = 4000
|
|
||||||
tiktoken_model_name: Optional[str] = None
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
QueryPowerBITool(
|
|
||||||
llm_chain=self._get_chain(),
|
|
||||||
powerbi=self.powerbi,
|
|
||||||
examples=self.examples,
|
|
||||||
max_iterations=self.max_iterations,
|
|
||||||
output_token_limit=self.output_token_limit, # type: ignore[arg-type]
|
|
||||||
tiktoken_model_name=self.tiktoken_model_name,
|
|
||||||
),
|
|
||||||
InfoPowerBITool(powerbi=self.powerbi),
|
|
||||||
ListPowerBITool(powerbi=self.powerbi),
|
|
||||||
]
|
|
||||||
|
|
||||||
def _get_chain(self) -> LLMChain:
|
|
||||||
"""Construct the chain based on the callback manager and model type."""
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
if isinstance(self.llm, BaseLanguageModel):
|
|
||||||
return LLMChain(
|
|
||||||
llm=self.llm,
|
|
||||||
callback_manager=self.callback_manager
|
|
||||||
if self.callback_manager
|
|
||||||
else None,
|
|
||||||
prompt=PromptTemplate(
|
|
||||||
template=SINGLE_QUESTION_TO_QUERY,
|
|
||||||
input_variables=["tool_input", "tables", "schemas", "examples"],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
system_prompt = SystemMessagePromptTemplate(
|
|
||||||
prompt=PromptTemplate(
|
|
||||||
template=QUESTION_TO_QUERY_BASE,
|
|
||||||
input_variables=["tables", "schemas", "examples"],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
human_prompt = HumanMessagePromptTemplate(
|
|
||||||
prompt=PromptTemplate(
|
|
||||||
template=USER_INPUT,
|
|
||||||
input_variables=["tool_input"],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return LLMChain(
|
|
||||||
llm=self.llm,
|
|
||||||
callback_manager=self.callback_manager if self.callback_manager else None,
|
|
||||||
prompt=ChatPromptTemplate.from_messages([system_prompt, human_prompt]),
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
"""Slack toolkit."""
|
|
@ -1,112 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, List
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_core.tools.base import BaseToolkit
|
|
||||||
from pydantic import ConfigDict, Field
|
|
||||||
|
|
||||||
from langchain_community.tools.slack.get_channel import SlackGetChannel
|
|
||||||
from langchain_community.tools.slack.get_message import SlackGetMessage
|
|
||||||
from langchain_community.tools.slack.schedule_message import SlackScheduleMessage
|
|
||||||
from langchain_community.tools.slack.send_message import SlackSendMessage
|
|
||||||
from langchain_community.tools.slack.utils import login
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
# This is for linting and IDE typehints
|
|
||||||
from slack_sdk import WebClient
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
# We do this so pydantic can resolve the types when instantiating
|
|
||||||
from slack_sdk import WebClient
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SlackToolkit(BaseToolkit):
|
|
||||||
"""Toolkit for interacting with Slack.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
client: The Slack client.
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
Install ``slack_sdk`` and set environment variable ``SLACK_USER_TOKEN``.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pip install -U slack_sdk
|
|
||||||
export SLACK_USER_TOKEN="your-user-token"
|
|
||||||
|
|
||||||
Key init args:
|
|
||||||
client: slack_sdk.WebClient
|
|
||||||
The Slack client.
|
|
||||||
|
|
||||||
Instantiate:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits import SlackToolkit
|
|
||||||
|
|
||||||
toolkit = SlackToolkit()
|
|
||||||
|
|
||||||
Tools:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
tools
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
[SlackGetChannel(client=<slack_sdk.web.client.WebClient object at 0x113caa8c0>),
|
|
||||||
SlackGetMessage(client=<slack_sdk.web.client.WebClient object at 0x113caa4d0>),
|
|
||||||
SlackScheduleMessage(client=<slack_sdk.web.client.WebClient object at 0x113caa440>),
|
|
||||||
SlackSendMessage(client=<slack_sdk.web.client.WebClient object at 0x113caa410>)]
|
|
||||||
|
|
||||||
Use within an agent:
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langgraph.prebuilt import create_react_agent
|
|
||||||
|
|
||||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
|
||||||
agent_executor = create_react_agent(llm, tools)
|
|
||||||
|
|
||||||
example_query = "When was the #general channel created?"
|
|
||||||
|
|
||||||
events = agent_executor.stream(
|
|
||||||
{"messages": [("user", example_query)]},
|
|
||||||
stream_mode="values",
|
|
||||||
)
|
|
||||||
for event in events:
|
|
||||||
message = event["messages"][-1]
|
|
||||||
if message.type != "tool": # mask sensitive information
|
|
||||||
event["messages"][-1].pretty_print()
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
================================[1m Human Message [0m=================================
|
|
||||||
|
|
||||||
When was the #general channel created?
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
Tool Calls:
|
|
||||||
get_channelid_name_dict (call_NXDkALjoOx97uF1v0CoZTqtJ)
|
|
||||||
Call ID: call_NXDkALjoOx97uF1v0CoZTqtJ
|
|
||||||
Args:
|
|
||||||
==================================[1m Ai Message [0m==================================
|
|
||||||
|
|
||||||
The #general channel was created on timestamp 1671043305.
|
|
||||||
""" # noqa: E501
|
|
||||||
|
|
||||||
client: WebClient = Field(default_factory=login)
|
|
||||||
|
|
||||||
model_config = ConfigDict(
|
|
||||||
arbitrary_types_allowed=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tools(self) -> List[BaseTool]:
|
|
||||||
"""Get the tools in the toolkit."""
|
|
||||||
return [
|
|
||||||
SlackGetChannel(),
|
|
||||||
SlackGetMessage(),
|
|
||||||
SlackScheduleMessage(),
|
|
||||||
SlackSendMessage(),
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
"""Spark SQL agent."""
|
|
@ -1,93 +0,0 @@
|
|||||||
"""Spark SQL agent."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from langchain_core.callbacks import BaseCallbackManager, Callbacks
|
|
||||||
from langchain_core.language_models import BaseLanguageModel
|
|
||||||
|
|
||||||
from langchain_community.agent_toolkits.spark_sql.prompt import SQL_PREFIX, SQL_SUFFIX
|
|
||||||
from langchain_community.agent_toolkits.spark_sql.toolkit import SparkSQLToolkit
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
|
|
||||||
|
|
||||||
def create_spark_sql_agent(
|
|
||||||
llm: BaseLanguageModel,
|
|
||||||
toolkit: SparkSQLToolkit,
|
|
||||||
callback_manager: Optional[BaseCallbackManager] = None,
|
|
||||||
callbacks: Callbacks = None,
|
|
||||||
prefix: str = SQL_PREFIX,
|
|
||||||
suffix: str = SQL_SUFFIX,
|
|
||||||
format_instructions: Optional[str] = None,
|
|
||||||
input_variables: Optional[List[str]] = None,
|
|
||||||
top_k: int = 10,
|
|
||||||
max_iterations: Optional[int] = 15,
|
|
||||||
max_execution_time: Optional[float] = None,
|
|
||||||
early_stopping_method: str = "force",
|
|
||||||
verbose: bool = False,
|
|
||||||
agent_executor_kwargs: Optional[Dict[str, Any]] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> AgentExecutor:
|
|
||||||
"""Construct a Spark SQL agent from an LLM and tools.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm: The language model to use.
|
|
||||||
toolkit: The Spark SQL toolkit.
|
|
||||||
callback_manager: Optional. The callback manager. Default is None.
|
|
||||||
callbacks: Optional. The callbacks. Default is None.
|
|
||||||
prefix: Optional. The prefix for the prompt. Default is SQL_PREFIX.
|
|
||||||
suffix: Optional. The suffix for the prompt. Default is SQL_SUFFIX.
|
|
||||||
format_instructions: Optional. The format instructions for the prompt.
|
|
||||||
Default is None.
|
|
||||||
input_variables: Optional. The input variables for the prompt. Default is None.
|
|
||||||
top_k: Optional. The top k for the prompt. Default is 10.
|
|
||||||
max_iterations: Optional. The maximum iterations to run. Default is 15.
|
|
||||||
max_execution_time: Optional. The maximum execution time. Default is None.
|
|
||||||
early_stopping_method: Optional. The early stopping method. Default is "force".
|
|
||||||
verbose: Optional. Whether to print verbose output. Default is False.
|
|
||||||
agent_executor_kwargs: Optional. The agent executor kwargs. Default is None.
|
|
||||||
kwargs: Any. Additional keyword arguments.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The agent executor.
|
|
||||||
"""
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
|
||||||
from langchain.agents.mrkl.base import ZeroShotAgent
|
|
||||||
from langchain.chains.llm import LLMChain
|
|
||||||
|
|
||||||
tools = toolkit.get_tools()
|
|
||||||
prefix = prefix.format(top_k=top_k)
|
|
||||||
prompt_params = (
|
|
||||||
{"format_instructions": format_instructions}
|
|
||||||
if format_instructions is not None
|
|
||||||
else {}
|
|
||||||
)
|
|
||||||
prompt = ZeroShotAgent.create_prompt(
|
|
||||||
tools,
|
|
||||||
prefix=prefix,
|
|
||||||
suffix=suffix,
|
|
||||||
input_variables=input_variables,
|
|
||||||
**prompt_params,
|
|
||||||
)
|
|
||||||
llm_chain = LLMChain(
|
|
||||||
llm=llm,
|
|
||||||
prompt=prompt,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
callbacks=callbacks,
|
|
||||||
)
|
|
||||||
tool_names = [tool.name for tool in tools]
|
|
||||||
agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names, **kwargs)
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
|
||||||
agent=agent,
|
|
||||||
tools=tools,
|
|
||||||
callback_manager=callback_manager,
|
|
||||||
callbacks=callbacks,
|
|
||||||
verbose=verbose,
|
|
||||||
max_iterations=max_iterations,
|
|
||||||
max_execution_time=max_execution_time,
|
|
||||||
early_stopping_method=early_stopping_method,
|
|
||||||
**(agent_executor_kwargs or {}),
|
|
||||||
)
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user