mirror of
https://github.com/hwchase17/langchain.git
synced 2025-07-30 16:24:24 +00:00
Merge branch 'langchain-ai:master' into master
This commit is contained in:
commit
0bf2dba44a
6
.github/workflows/.codespell-exclude
vendored
6
.github/workflows/.codespell-exclude
vendored
@ -1,6 +0,0 @@
|
||||
"NotIn": "not in",
|
||||
- `/checkin`: Check-in
|
||||
docs/docs/integrations/providers/trulens.mdx
|
||||
self.assertIn(
|
||||
from trulens_eval import Tru
|
||||
tru = Tru()
|
4
.github/workflows/_integration_test.yml
vendored
4
.github/workflows/_integration_test.yml
vendored
@ -1,4 +1,5 @@
|
||||
name: '🚀 Integration Tests'
|
||||
run-name: 'Test ${{ inputs.working-directory }} on Python ${{ inputs.python-version }}'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@ -11,6 +12,7 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
description: "Python version to use"
|
||||
default: "3.11"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@ -24,7 +26,7 @@ jobs:
|
||||
run:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
runs-on: ubuntu-latest
|
||||
name: '🚀 Integration Tests (Python ${{ inputs.python-version }})'
|
||||
name: 'Python ${{ inputs.python-version }}'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
13
.github/workflows/_release.yml
vendored
13
.github/workflows/_release.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: '🚀 Package Release'
|
||||
run-name: '🚀 Release ${{ inputs.working-directory }} by @${{ github.actor }}'
|
||||
run-name: 'Release ${{ inputs.working-directory }} ${{ inputs.release-version }}'
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
@ -14,6 +14,11 @@ on:
|
||||
type: string
|
||||
description: "From which folder this pipeline executes"
|
||||
default: 'libs/langchain'
|
||||
release-version:
|
||||
required: true
|
||||
type: string
|
||||
default: '0.1.0'
|
||||
description: "New version of package being released"
|
||||
dangerous-nonmaster-release:
|
||||
required: false
|
||||
type: boolean
|
||||
@ -111,7 +116,7 @@ jobs:
|
||||
# Look for the latest release of the same base version
|
||||
REGEX="^$PKG_NAME==$BASE_VERSION\$"
|
||||
PREV_TAG=$(git tag --sort=-creatordate | (grep -P "$REGEX" || true) | head -1)
|
||||
|
||||
|
||||
# If no exact base version match, look for the latest release of any kind
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
REGEX="^$PKG_NAME==\\d+\\.\\d+\\.\\d+\$"
|
||||
@ -122,7 +127,7 @@ jobs:
|
||||
PREV_TAG="$PKG_NAME==${VERSION%.*}.$(( ${VERSION##*.} - 1 ))"; [[ "${VERSION##*.}" -eq 0 ]] && PREV_TAG=""
|
||||
|
||||
# backup case if releasing e.g. 0.3.0, looks up last release
|
||||
# note if last release (chronologically) was e.g. 0.1.47 it will get
|
||||
# note if last release (chronologically) was e.g. 0.1.47 it will get
|
||||
# that instead of the last 0.2 release
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
REGEX="^$PKG_NAME==\\d+\\.\\d+\\.\\d+\$"
|
||||
@ -484,7 +489,7 @@ jobs:
|
||||
with:
|
||||
name: dist
|
||||
path: ${{ inputs.working-directory }}/dist/
|
||||
|
||||
|
||||
- name: Create Tag
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
|
11
.github/workflows/api_doc_build.yml
vendored
11
.github/workflows/api_doc_build.yml
vendored
@ -1,4 +1,5 @@
|
||||
name: '📚 API Documentation Build'
|
||||
name: '📚 API Docs'
|
||||
run-name: 'Build & Deploy API Reference'
|
||||
# Runs daily or can be triggered manually for immediate updates
|
||||
|
||||
on:
|
||||
@ -51,7 +52,7 @@ jobs:
|
||||
run: |
|
||||
# Get unique repositories
|
||||
REPOS=$(echo "$REPOS_UNSORTED" | sort -u)
|
||||
|
||||
|
||||
# Checkout each unique repository
|
||||
for repo in $REPOS; do
|
||||
# Validate repository format (allow any org with proper format)
|
||||
@ -59,15 +60,15 @@ jobs:
|
||||
echo "Error: Invalid repository format: $repo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
REPO_NAME=$(echo $repo | cut -d'/' -f2)
|
||||
|
||||
|
||||
# Additional validation for repo name
|
||||
if [[ ! "$REPO_NAME" =~ ^[a-zA-Z0-9_.-]+$ ]]; then
|
||||
echo "Error: Invalid repository name: $REPO_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "Checking out $repo to $REPO_NAME"
|
||||
git clone --depth 1 https://github.com/$repo.git $REPO_NAME
|
||||
done
|
||||
|
3
.github/workflows/people.yml
vendored
3
.github/workflows/people.yml
vendored
@ -1,5 +1,6 @@
|
||||
name: '👥 LangChain People'
|
||||
|
||||
run-name: 'Update People Data'
|
||||
# This workflow updates the LangChain People data by fetching the latest information from the LangChain Git
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 14 1 * *"
|
||||
|
4
.github/workflows/run_notebooks.yml
vendored
4
.github/workflows/run_notebooks.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: '📝 Run Documentation Notebooks'
|
||||
|
||||
name: '📓 Validate Documentation Notebooks'
|
||||
run-name: 'Test notebooks in ${{ inputs.working-directory }}'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
|
3
.github/workflows/scheduled_test.yml
vendored
3
.github/workflows/scheduled_test.yml
vendored
@ -1,4 +1,5 @@
|
||||
name: '⏰ Scheduled Integration Tests'
|
||||
run-name: "Run Integration Tests - ${{ inputs.working-directory-force || 'all libs' }} (Python ${{ inputs.python-version-force || '3.9, 3.11' }})"
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows maintainers to trigger the workflow manually in GitHub UI
|
||||
@ -161,7 +162,7 @@ jobs:
|
||||
|
||||
- name: '🧹 Clean up External Libraries'
|
||||
# Clean up external libraries to avoid affecting git status check
|
||||
run: |
|
||||
run: |
|
||||
rm -rf \
|
||||
langchain/libs/partners/google-genai \
|
||||
langchain/libs/partners/google-vertexai \
|
||||
|
@ -15,7 +15,7 @@
|
||||
[](https://star-history.com/#langchain-ai/langchain)
|
||||
[](https://github.com/langchain-ai/langchain/issues)
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/langchain-ai/langchain)
|
||||
[<img src="https://github.com/codespaces/badge.svg" title="Open in Github Codespace" width="150" height="20">](https://codespaces.new/langchain-ai/langchain)
|
||||
[<img src="https://github.com/codespaces/badge.svg" alt="Open in Github Codespace" title="Open in Github Codespace" width="150" height="20">](https://codespaces.new/langchain-ai/langchain)
|
||||
[](https://twitter.com/langchainai)
|
||||
[](https://codspeed.io/langchain-ai/langchain)
|
||||
|
||||
|
@ -32,7 +32,7 @@ LangChain is partnered with [huntr by Protect AI](https://huntr.com/) to provide
|
||||
a bounty program for our open source projects.
|
||||
|
||||
Please report security vulnerabilities associated with the LangChain
|
||||
open source projects [here](https://huntr.com/bounties/disclose/?target=https%3A%2F%2Fgithub.com%2Flangchain-ai%2Flangchain&validSearch=true).
|
||||
open source projects at [huntr](https://huntr.com/bounties/disclose/?target=https%3A%2F%2Fgithub.com%2Flangchain-ai%2Flangchain&validSearch=true).
|
||||
|
||||
Before reporting a vulnerability, please review:
|
||||
|
||||
|
@ -144,7 +144,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": null,
|
||||
"id": "kWDWfSDBMPl8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -185,7 +185,7 @@
|
||||
" )\n",
|
||||
" # Text summary chain\n",
|
||||
" model = VertexAI(\n",
|
||||
" temperature=0, model_name=\"gemini-2.0-flash-lite-001\", max_tokens=1024\n",
|
||||
" temperature=0, model_name=\"gemini-2.5-flash\", max_tokens=1024\n",
|
||||
" ).with_fallbacks([empty_response])\n",
|
||||
" summarize_chain = {\"element\": lambda x: x} | prompt | model | StrOutputParser()\n",
|
||||
"\n",
|
||||
@ -235,7 +235,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": null,
|
||||
"id": "PeK9bzXv3olF",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@ -254,7 +254,7 @@
|
||||
"\n",
|
||||
"def image_summarize(img_base64, prompt):\n",
|
||||
" \"\"\"Make image summary\"\"\"\n",
|
||||
" model = ChatVertexAI(model=\"gemini-2.0-flash\", max_tokens=1024)\n",
|
||||
" model = ChatVertexAI(model=\"gemini-2.5-flash\", max_tokens=1024)\n",
|
||||
"\n",
|
||||
" msg = model.invoke(\n",
|
||||
" [\n",
|
||||
@ -431,7 +431,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": null,
|
||||
"id": "GlwCErBaCKQW",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@ -553,7 +553,7 @@
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" # Multi-modal LLM\n",
|
||||
" model = ChatVertexAI(temperature=0, model_name=\"gemini-2.0-flash\", max_tokens=1024)\n",
|
||||
" model = ChatVertexAI(temperature=0, model_name=\"gemini-2.5-flash\", max_tokens=1024)\n",
|
||||
"\n",
|
||||
" # RAG pipeline\n",
|
||||
" chain = (\n",
|
||||
|
@ -373,7 +373,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": null,
|
||||
"id": "a0b91b29-dbd6-4c94-8f24-05471adc7598",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -397,7 +397,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# Pass to LLM\n",
|
||||
"llm = init_chat_model(\"google_genai:gemini-2.0-flash-001\")\n",
|
||||
"llm = init_chat_model(\"google_genai:gemini-2.5-flash\")\n",
|
||||
"\n",
|
||||
"message = {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
|
@ -23,9 +23,9 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'token_usage': {'completion_tokens': 93,\n",
|
||||
"{'token_usage': {'completion_tokens': 88,\n",
|
||||
" 'prompt_tokens': 16,\n",
|
||||
" 'total_tokens': 109,\n",
|
||||
" 'total_tokens': 104,\n",
|
||||
" 'completion_tokens_details': {'accepted_prediction_tokens': 0,\n",
|
||||
" 'audio_tokens': 0,\n",
|
||||
" 'reasoning_tokens': 0,\n",
|
||||
@ -33,7 +33,7 @@
|
||||
" 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}},\n",
|
||||
" 'model_name': 'gpt-4o-mini-2024-07-18',\n",
|
||||
" 'system_fingerprint': 'fp_34a54ae93c',\n",
|
||||
" 'id': 'chatcmpl-ByJtse6I3U1lmVyPscLCjzydCvfDO',\n",
|
||||
" 'id': 'chatcmpl-ByN1Qkvqb5fAGKKzXXxZ3rBlnqkWs',\n",
|
||||
" 'service_tier': 'default',\n",
|
||||
" 'finish_reason': 'stop',\n",
|
||||
" 'logprobs': None}"
|
||||
@ -69,14 +69,14 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'id': 'msg_017S9H7GMwA5RdZ1wHxzXoeX',\n",
|
||||
"{'id': 'msg_01NTWnqvbNKSjGfqQL7xikau',\n",
|
||||
" 'model': 'claude-3-7-sonnet-20250219',\n",
|
||||
" 'stop_reason': 'end_turn',\n",
|
||||
" 'stop_sequence': None,\n",
|
||||
" 'usage': {'cache_creation_input_tokens': 0,\n",
|
||||
" 'cache_read_input_tokens': 0,\n",
|
||||
" 'input_tokens': 17,\n",
|
||||
" 'output_tokens': 180,\n",
|
||||
" 'output_tokens': 197,\n",
|
||||
" 'server_tool_use': None,\n",
|
||||
" 'service_tier': 'standard'},\n",
|
||||
" 'model_name': 'claude-3-7-sonnet-20250219'}"
|
||||
@ -100,30 +100,22 @@
|
||||
"id": "c1f24f69-18f6-43c1-8b26-3f88ec515259",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Google VertexAI"
|
||||
"## Google Generative AI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "39549336-25f5-4839-9846-f687cd77e59b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'is_blocked': False,\n",
|
||||
" 'safety_ratings': [],\n",
|
||||
" 'usage_metadata': {'prompt_token_count': 10,\n",
|
||||
" 'candidates_token_count': 55,\n",
|
||||
" 'total_token_count': 65,\n",
|
||||
" 'prompt_tokens_details': [{'modality': 1, 'token_count': 10}],\n",
|
||||
" 'candidates_tokens_details': [{'modality': 1, 'token_count': 55}],\n",
|
||||
" 'cached_content_token_count': 0,\n",
|
||||
" 'cache_tokens_details': []},\n",
|
||||
"{'prompt_feedback': {'block_reason': 0, 'safety_ratings': []},\n",
|
||||
" 'finish_reason': 'STOP',\n",
|
||||
" 'avg_logprobs': -0.251378042047674,\n",
|
||||
" 'model_name': 'gemini-2.0-flash-001'}"
|
||||
" 'model_name': 'gemini-2.5-flash',\n",
|
||||
" 'safety_ratings': []}"
|
||||
]
|
||||
},
|
||||
"execution_count": 1,
|
||||
@ -132,9 +124,9 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-2.0-flash-001\")\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.5-flash\")\n",
|
||||
"msg = llm.invoke(\"What's the oldest known example of cuneiform\")\n",
|
||||
"msg.response_metadata"
|
||||
]
|
||||
@ -199,14 +191,14 @@
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'token_usage': {'prompt_tokens': 13,\n",
|
||||
" 'total_tokens': 219,\n",
|
||||
" 'completion_tokens': 206},\n",
|
||||
" 'total_tokens': 306,\n",
|
||||
" 'completion_tokens': 293},\n",
|
||||
" 'model_name': 'mistral-small-latest',\n",
|
||||
" 'model': 'mistral-small-latest',\n",
|
||||
" 'finish_reason': 'stop'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
"\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",
|
||||
"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",
|
||||
"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 model ids can be found in the [Gemini API docs](https://ai.google.dev/gemini-api/docs/models).\n",
|
||||
"\n",
|
||||
"### Integration details\n",
|
||||
"\n",
|
||||
@ -117,7 +117,7 @@
|
||||
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||
"\n",
|
||||
"llm = ChatGoogleGenerativeAI(\n",
|
||||
" model=\"gemini-2.0-flash\",\n",
|
||||
" model=\"gemini-2.5-flash\",\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=None,\n",
|
||||
" timeout=None,\n",
|
||||
@ -242,7 +242,7 @@
|
||||
"\n",
|
||||
"### Image Input\n",
|
||||
"\n",
|
||||
"Provide image inputs along with text using a `HumanMessage` with a list content format. The `gemini-2.0-flash` model can handle images."
|
||||
"Provide image inputs along with text using a `HumanMessage` with a list content format. Make sure to use a model that supports image input, such as `gemini-2.5-flash`."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -297,7 +297,7 @@
|
||||
"\n",
|
||||
"### Audio Input\n",
|
||||
"\n",
|
||||
"Provide audio file inputs along with text. Use a model like `gemini-2.0-flash`."
|
||||
"Provide audio file inputs along with text."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -340,7 +340,7 @@
|
||||
"source": [
|
||||
"### Video Input\n",
|
||||
"\n",
|
||||
"Provide video file inputs along with text. Use a model like `gemini-2.0-flash`."
|
||||
"Provide video file inputs along with text."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -384,7 +384,7 @@
|
||||
"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`."
|
||||
"Certain models (such as `gemini-2.0-flash-preview-image-generation`) can generate text and images inline. You need to specify the desired `response_modalities`. See more information on the [Gemini API docs](https://ai.google.dev/gemini-api/docs/image-generation) for details."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -830,7 +830,7 @@
|
||||
"source": [
|
||||
"from langchain_google_genai import ChatGoogleGenerativeAI\n",
|
||||
"\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\")\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.5-flash\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def run_async_calls():\n",
|
||||
@ -900,7 +900,7 @@
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"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"
|
||||
"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)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -19,7 +19,7 @@
|
||||
"\n",
|
||||
"This page provides a quick overview for getting started with VertexAI [chat models](/docs/concepts/chat_models). For detailed documentation of all ChatVertexAI features and configurations head to the [API reference](https://python.langchain.com/api_reference/google_vertexai/chat_models/langchain_google_vertexai.chat_models.ChatVertexAI.html).\n",
|
||||
"\n",
|
||||
"ChatVertexAI exposes all foundational models available in Google Cloud, like `gemini-1.5-pro`, `gemini-1.5-flash`, etc. For a full and updated list of available models visit [VertexAI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/overview).\n",
|
||||
"ChatVertexAI exposes all foundational models available in Google Cloud, like `gemini-2.5-pro`, `gemini-2.5-flash`, etc. For a full and updated list of available models visit [VertexAI documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/models).\n",
|
||||
"\n",
|
||||
":::info Google Cloud VertexAI vs Google PaLM\n",
|
||||
"\n",
|
||||
@ -60,7 +60,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "a15d341e-3e26-4ca3-830b-5aab30ed66de",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@ -109,7 +109,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": null,
|
||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@ -117,7 +117,7 @@
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(\n",
|
||||
" model=\"gemini-1.5-flash-001\",\n",
|
||||
" model=\"gemini-2.5-flash\",\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=None,\n",
|
||||
" max_retries=6,\n",
|
||||
@ -210,7 +210,7 @@
|
||||
"source": [
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-2.0-flash-001\").bind_tools([{\"google_search\": {}}])\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-2.5-flash\").bind_tools([{\"google_search\": {}}])\n",
|
||||
"\n",
|
||||
"response = llm.invoke(\"What is today's news?\")"
|
||||
]
|
||||
@ -237,7 +237,7 @@
|
||||
"source": [
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-2.0-flash-001\").bind_tools([{\"code_execution\": {}}])\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-2.5-flash\").bind_tools([{\"code_execution\": {}}])\n",
|
||||
"\n",
|
||||
"response = llm.invoke(\"What is 3^3?\")"
|
||||
]
|
||||
|
@ -23,13 +23,9 @@
|
||||
"\n",
|
||||
"**Note:** This is separate from the `Google Generative AI` integration, it exposes [Vertex AI Generative API](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/overview) on `Google Cloud`.\n",
|
||||
"\n",
|
||||
"VertexAI exposes all foundational models available in google cloud:\n",
|
||||
"- Gemini for Text ( `gemini-1.0-pro` )\n",
|
||||
"- Gemini with Multimodality ( `gemini-1.5-pro-001` and `gemini-pro-vision`)\n",
|
||||
"- Palm 2 for Text (`text-bison`)\n",
|
||||
"- Codey for Code Generation (`code-bison`)\n",
|
||||
"VertexAI exposes all foundational models available in google cloud.\n",
|
||||
"\n",
|
||||
"For a full and updated list of available models visit [VertexAI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/overview)"
|
||||
"For a full and updated list of available models visit [VertexAI documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/models)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -47,7 +43,7 @@
|
||||
"\n",
|
||||
"To use `Vertex AI Generative AI` you must have the `langchain-google-vertexai` Python package installed and either:\n",
|
||||
"- Have credentials configured for your environment (gcloud, workload identity, etc...)\n",
|
||||
"- Store the path to a service account JSON file as the GOOGLE_APPLICATION_CREDENTIALS environment variable\n",
|
||||
"- Store the path to a service account JSON file as the `GOOGLE_APPLICATION_CREDENTIALS` environment variable\n",
|
||||
"\n",
|
||||
"This codebase uses the `google.auth` library which first looks for the application credentials variable mentioned above, and then looks for system-level auth.\n",
|
||||
"\n",
|
||||
@ -84,31 +80,14 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_vertexai import VertexAI\n",
|
||||
"\n",
|
||||
"# To use model\n",
|
||||
"model = VertexAI(model_name=\"gemini-pro\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"NOTE : You can also specify a [Gemini Version](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning#gemini-model-versions)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# To specify a particular model version\n",
|
||||
"model = VertexAI(model_name=\"gemini-1.0-pro-002\")"
|
||||
"model = VertexAI(model_name=\"gemini-2.5-pro\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -285,7 +264,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@ -301,7 +280,7 @@
|
||||
],
|
||||
"source": [
|
||||
"# You may also pass safety_settings to generate method\n",
|
||||
"llm = VertexAI(model_name=\"gemini-1.0-pro-001\")\n",
|
||||
"llm = VertexAI(model_name=\"gemini-2.5-pro\")\n",
|
||||
"\n",
|
||||
"# invoke a model response\n",
|
||||
"output = llm.invoke(\n",
|
||||
@ -622,15 +601,14 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"# Use Gemini 1.5 Pro\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-1.5-pro-001\")"
|
||||
"llm = ChatVertexAI(model=\"gemini-2.5-pro\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -683,15 +661,14 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"# Use Gemini 1.5 Pro\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-1.5-pro-001\")"
|
||||
"llm = ChatVertexAI(model=\"gemini-2.5-pro\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -741,20 +718,19 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Using Audio with Gemini 1.5 Pro"
|
||||
"### Using Audio with Gemini Models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"# Use Gemini 1.5 Pro\n",
|
||||
"llm = ChatVertexAI(model=\"gemini-1.5-pro-001\")"
|
||||
"llm = ChatVertexAI(model=\"gemini-2.5-pro\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -1226,9 +1202,6 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"NOTE : Specify the correct [Claude 3 Model Versions](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude#claude-opus)\n",
|
||||
"- For Claude 3 Opus (Preview), use `claude-3-opus@20240229`.\n",
|
||||
"- For Claude 3 Sonnet, use `claude-3-sonnet@20240229`.\n",
|
||||
"- For Claude 3 Haiku, use `claude-3-haiku@20240307`.\n",
|
||||
"\n",
|
||||
"We don't recommend using the Anthropic Claude 3 model versions that don't include a suffix that starts with an @ symbol (claude-3-opus, claude-3-sonnet, or claude-3-haiku)."
|
||||
]
|
||||
|
@ -29,14 +29,14 @@ export GOOGLE_API_KEY="YOUR_API_KEY"
|
||||
|
||||
### Chat Models
|
||||
|
||||
Use the `ChatGoogleGenerativeAI` class to interact with Gemini 2.0 and 2.5 models. See
|
||||
Use the `ChatGoogleGenerativeAI` class to interact with Gemini models. See
|
||||
details in [this guide](/docs/integrations/chat/google_generative_ai).
|
||||
|
||||
```python
|
||||
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash")
|
||||
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash")
|
||||
|
||||
# Simple text invocation
|
||||
result = llm.invoke("Sing a ballad of LangChain.")
|
||||
@ -61,14 +61,14 @@ The `image_url` can be a public URL, a GCS URI (`gs://...`), a local file path,
|
||||
|
||||
### Embedding Models
|
||||
|
||||
Generate text embeddings using models like `gemini-embedding-exp-03-07` with the `GoogleGenerativeAIEmbeddings` class.
|
||||
Generate text embeddings using models like `gemini-embedding-001` with the `GoogleGenerativeAIEmbeddings` class.
|
||||
|
||||
See a [usage example](/docs/integrations/text_embedding/google_generative_ai).
|
||||
|
||||
```python
|
||||
from langchain_google_genai import GoogleGenerativeAIEmbeddings
|
||||
|
||||
embeddings = GoogleGenerativeAIEmbeddings(model="models/gemini-embedding-exp-03-07")
|
||||
embeddings = GoogleGenerativeAIEmbeddings(model="models/gemini-embedding-001")
|
||||
vector = embeddings.embed_query("What are embeddings?")
|
||||
print(vector[:5])
|
||||
```
|
||||
@ -83,7 +83,7 @@ See a [usage example](/docs/integrations/llms/google_ai).
|
||||
```python
|
||||
from langchain_google_genai import GoogleGenerativeAI
|
||||
|
||||
llm = GoogleGenerativeAI(model="gemini-2.0-flash")
|
||||
llm = GoogleGenerativeAI(model="gemini-2.5-flash")
|
||||
result = llm.invoke("Sing a ballad of LangChain.")
|
||||
print(result)
|
||||
```
|
||||
@ -105,7 +105,7 @@ Google Cloud integrations typically use Application Default Credentials (ADC). R
|
||||
|
||||
#### Vertex AI
|
||||
|
||||
Access chat models like `Gemini` via the Vertex AI platform.
|
||||
Access chat models like Gemini via the Vertex AI platform.
|
||||
|
||||
See a [usage example](/docs/integrations/chat/google_vertex_ai_palm).
|
||||
|
||||
@ -135,7 +135,7 @@ from langchain_google_vertexai.model_garden_maas.mistral import VertexModelGarde
|
||||
|
||||
#### Gemma local from Hugging Face
|
||||
|
||||
>Local `Gemma` model loaded from `HuggingFace`. Requires `langchain-google-vertexai`.
|
||||
>Local Gemma model loaded from HuggingFace. Requires `langchain-google-vertexai`.
|
||||
|
||||
```python
|
||||
from langchain_google_vertexai.gemma import GemmaChatLocalHF
|
||||
@ -143,7 +143,7 @@ from langchain_google_vertexai.gemma import GemmaChatLocalHF
|
||||
|
||||
#### Gemma local from Kaggle
|
||||
|
||||
>Local `Gemma` model loaded from `Kaggle`. Requires `langchain-google-vertexai`.
|
||||
>Local Gemma model loaded from Kaggle. Requires `langchain-google-vertexai`.
|
||||
|
||||
```python
|
||||
from langchain_google_vertexai.gemma import GemmaChatLocalKaggle
|
||||
@ -159,7 +159,7 @@ from langchain_google_vertexai.gemma import GemmaChatVertexAIModelGarden
|
||||
|
||||
#### Vertex AI image captioning
|
||||
|
||||
>Implementation of the `Image Captioning model` as a chat. Requires `langchain-google-vertexai`.
|
||||
>Implementation of the Image Captioning model as a chat. Requires `langchain-google-vertexai`.
|
||||
|
||||
```python
|
||||
from langchain_google_vertexai.vision_models import VertexAIImageCaptioningChat
|
||||
@ -196,7 +196,7 @@ interface.
|
||||
|
||||
#### Vertex AI Model Garden
|
||||
|
||||
Access `Gemini`, and hundreds of OSS models via `Vertex AI Model Garden` service. Requires `langchain-google-vertexai`.
|
||||
Access Gemini, and hundreds of OSS models via Vertex AI Model Garden service. Requires `langchain-google-vertexai`.
|
||||
|
||||
See a [usage example](/docs/integrations/llms/google_vertex_ai_palm#vertex-model-garden).
|
||||
|
||||
@ -206,7 +206,7 @@ from langchain_google_vertexai import VertexAIModelGarden
|
||||
|
||||
#### Gemma local from Hugging Face
|
||||
|
||||
>Local `Gemma` model loaded from `HuggingFace`. Requires `langchain-google-vertexai`.
|
||||
>Local Gemma model loaded from HuggingFace. Requires `langchain-google-vertexai`.
|
||||
|
||||
```python
|
||||
from langchain_google_vertexai.gemma import GemmaLocalHF
|
||||
@ -214,7 +214,7 @@ from langchain_google_vertexai.gemma import GemmaLocalHF
|
||||
|
||||
#### Gemma local from Kaggle
|
||||
|
||||
>Local `Gemma` model loaded from `Kaggle`. Requires `langchain-google-vertexai`.
|
||||
>Local Gemma model loaded from Kaggle. Requires `langchain-google-vertexai`.
|
||||
|
||||
```python
|
||||
from langchain_google_vertexai.gemma import GemmaLocalKaggle
|
||||
@ -230,7 +230,7 @@ from langchain_google_vertexai.gemma import GemmaVertexAIModelGarden
|
||||
|
||||
#### Vertex AI image captioning
|
||||
|
||||
>Implementation of the `Image Captioning model` as an LLM. Requires `langchain-google-vertexai`.
|
||||
>Implementation of the Image Captioning model as an LLM. Requires `langchain-google-vertexai`.
|
||||
|
||||
```python
|
||||
from langchain_google_vertexai.vision_models import VertexAIImageCaptioning
|
||||
@ -1138,7 +1138,7 @@ Integrations with various Google services beyond the core Cloud Platform.
|
||||
|
||||
#### Google Drive
|
||||
|
||||
>[Google Drive](https://en.wikipedia.org/wiki/Google_Drive) file storage. Currently supports `Google Docs`.
|
||||
>[Google Drive](https://en.wikipedia.org/wiki/Google_Drive) file storage. Currently supports Google Docs.
|
||||
|
||||
Install with Drive dependencies:
|
||||
|
||||
@ -1416,7 +1416,7 @@ from langchain_community.utilities import GoogleSerperAPIWrapper
|
||||
|
||||
#### YouTube Search Tool
|
||||
|
||||
>Search `YouTube` videos without the official API. Requires `youtube_search` package.
|
||||
>Search YouTube videos without the official API. Requires `youtube_search` package.
|
||||
|
||||
```bash
|
||||
pip install youtube_search langchain # Requires base langchain
|
||||
|
@ -101,7 +101,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"execution_count": null,
|
||||
"id": "eedc551e-a1f3-4fd8-8d65-4e0784c4441b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -123,7 +123,7 @@
|
||||
"source": [
|
||||
"from langchain_google_genai import GoogleGenerativeAIEmbeddings\n",
|
||||
"\n",
|
||||
"embeddings = GoogleGenerativeAIEmbeddings(model=\"models/gemini-embedding-exp-03-07\")\n",
|
||||
"embeddings = GoogleGenerativeAIEmbeddings(model=\"models/gemini-embedding-001\")\n",
|
||||
"vector = embeddings.embed_query(\"hello, world!\")\n",
|
||||
"vector[:5]"
|
||||
]
|
||||
@ -245,7 +245,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"execution_count": null,
|
||||
"id": "f1f077db-8eb4-49f7-8866-471a8528dcdb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -267,10 +267,10 @@
|
||||
"from sklearn.metrics.pairwise import cosine_similarity\n",
|
||||
"\n",
|
||||
"query_embeddings = GoogleGenerativeAIEmbeddings(\n",
|
||||
" model=\"models/gemini-embedding-exp-03-07\", task_type=\"RETRIEVAL_QUERY\"\n",
|
||||
" model=\"models/gemini-embedding-001\", task_type=\"RETRIEVAL_QUERY\"\n",
|
||||
")\n",
|
||||
"doc_embeddings = GoogleGenerativeAIEmbeddings(\n",
|
||||
" model=\"models/gemini-embedding-exp-03-07\", task_type=\"RETRIEVAL_DOCUMENT\"\n",
|
||||
" model=\"models/gemini-embedding-001\", task_type=\"RETRIEVAL_DOCUMENT\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"q_embed = query_embeddings.embed_query(\"What is the capital of France?\")\n",
|
||||
|
@ -253,7 +253,7 @@
|
||||
"from langgraph.prebuilt import create_react_agent\n",
|
||||
"\n",
|
||||
"# Initialize the LLM\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\", google_api_key=\"your-api-key\")\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.5-flash\", google_api_key=\"your-api-key\")\n",
|
||||
"\n",
|
||||
"# Initialize the Bright Data Web Scraper API tool\n",
|
||||
"scraper_tool = BrightDataWebScraperAPI(bright_data_api_key=\"your-api-key\")\n",
|
||||
|
@ -233,7 +233,7 @@
|
||||
"from langgraph.prebuilt import create_react_agent\n",
|
||||
"\n",
|
||||
"# Initialize the LLM\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\", google_api_key=\"your-api-key\")\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.5-flash\", google_api_key=\"your-api-key\")\n",
|
||||
"\n",
|
||||
"# Initialize the Bright Data SERP tool\n",
|
||||
"serp_tool = BrightDataSERP(\n",
|
||||
|
@ -275,7 +275,7 @@
|
||||
"from langgraph.prebuilt import create_react_agent\n",
|
||||
"\n",
|
||||
"# Initialize the LLM\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash\", google_api_key=\"your-api-key\")\n",
|
||||
"llm = ChatGoogleGenerativeAI(model=\"gemini-2.5-flash\", google_api_key=\"your-api-key\")\n",
|
||||
"\n",
|
||||
"# Initialize the tool\n",
|
||||
"bright_data_tool = BrightDataUnlocker(bright_data_api_key=\"your-api-key\")\n",
|
||||
|
@ -36,17 +36,17 @@ export const CustomDropdown = ({ selectedOption, options, onSelect, modelType })
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem', gap: '0.75rem' }}>
|
||||
<span style={{
|
||||
<span style={{
|
||||
fontSize: '1rem',
|
||||
fontWeight: '500',
|
||||
}}>
|
||||
Select <a href={link}>{text}</a>:
|
||||
</span>
|
||||
<div className={`dropdown ${isOpen ? 'dropdown--show' : ''}`}>
|
||||
<button
|
||||
className="button button--secondary"
|
||||
<button
|
||||
className="button button--secondary"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
style={{
|
||||
style={{
|
||||
backgroundColor: 'var(--ifm-background-color)',
|
||||
border: '1px solid var(--ifm-color-emphasis-300)',
|
||||
fontWeight: 'normal',
|
||||
@ -56,7 +56,7 @@ export const CustomDropdown = ({ selectedOption, options, onSelect, modelType })
|
||||
}}
|
||||
>
|
||||
{selectedOption.label}
|
||||
<span style={{
|
||||
<span style={{
|
||||
marginLeft: '0.4rem',
|
||||
fontSize: '0.875rem'
|
||||
}}>▾</span>
|
||||
@ -69,9 +69,9 @@ export const CustomDropdown = ({ selectedOption, options, onSelect, modelType })
|
||||
}}>
|
||||
{options.map((option) => (
|
||||
<li key={option.value}>
|
||||
<a
|
||||
<a
|
||||
className={`dropdown__link ${option.value === selectedOption.value ? 'dropdown__link--active' : ''}`}
|
||||
href="#"
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onSelect(option.value);
|
||||
@ -138,14 +138,14 @@ ${llmVarName} = AzureChatOpenAI(
|
||||
{
|
||||
value: "google_genai",
|
||||
label: "Google Gemini",
|
||||
model: "gemini-2.0-flash",
|
||||
model: "gemini-2.5-flash",
|
||||
apiKeyName: "GOOGLE_API_KEY",
|
||||
packageName: "langchain[google-genai]",
|
||||
},
|
||||
{
|
||||
value: "google_vertexai",
|
||||
label: "Google Vertex",
|
||||
model: "gemini-2.0-flash-001",
|
||||
model: "gemini-2.5-flash",
|
||||
apiKeyText: "# Ensure your VertexAI credentials are configured",
|
||||
packageName: "langchain[google-vertexai]",
|
||||
},
|
||||
@ -204,8 +204,8 @@ ${llmVarName} = AzureChatOpenAI(
|
||||
text: `from langchain_ibm import ChatWatsonx
|
||||
|
||||
${llmVarName} = ChatWatsonx(
|
||||
model_id="ibm/granite-34b-code-instruct",
|
||||
url="https://us-south.ml.cloud.ibm.com",
|
||||
model_id="ibm/granite-34b-code-instruct",
|
||||
url="https://us-south.ml.cloud.ibm.com",
|
||||
project_id="<WATSONX PROJECT_ID>"
|
||||
)`,
|
||||
apiKeyName: "WATSONX_APIKEY",
|
||||
@ -238,18 +238,18 @@ ${llmVarName} = ChatWatsonx(
|
||||
}));
|
||||
|
||||
const modelOptions = tabItems
|
||||
.map((item) => ({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
}));
|
||||
.map((item) => ({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
}));
|
||||
|
||||
const selectedTabItem = tabItems.find(
|
||||
(option) => option.value === selectedModel
|
||||
);
|
||||
|
||||
let apiKeyText = "";
|
||||
if (selectedTabItem.apiKeyName) {
|
||||
apiKeyText = `import getpass
|
||||
let apiKeyText = "";
|
||||
if (selectedTabItem.apiKeyName) {
|
||||
apiKeyText = `import getpass
|
||||
import os
|
||||
|
||||
if not os.environ.get("${selectedTabItem.apiKeyName}"):
|
||||
@ -264,7 +264,7 @@ ${llmVarName} = init_chat_model("${selectedTabItem.model}", model_provider="${se
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CustomDropdown
|
||||
<CustomDropdown
|
||||
selectedOption={selectedTabItem}
|
||||
options={modelOptions}
|
||||
onSelect={setSelectedModel}
|
||||
@ -279,4 +279,4 @@ ${llmVarName} = init_chat_model("${selectedTabItem.model}", model_provider="${se
|
||||
</CodeBlock>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,206 +3,206 @@ import CodeBlock from "@theme-original/CodeBlock";
|
||||
import { CustomDropdown } from './ChatModelTabs';
|
||||
|
||||
export default function EmbeddingTabs(props) {
|
||||
const [selectedModel, setSelectedModel] = useState("OpenAI");
|
||||
const {
|
||||
openaiParams,
|
||||
hideOpenai,
|
||||
azureOpenaiParams,
|
||||
hideAzureOpenai,
|
||||
googleGenAIParams,
|
||||
hideGoogleGenAI,
|
||||
googleVertexAIParams,
|
||||
hideGoogleVertexAI,
|
||||
awsParams,
|
||||
hideAws,
|
||||
huggingFaceParams,
|
||||
hideHuggingFace,
|
||||
ollamaParams,
|
||||
hideOllama,
|
||||
cohereParams,
|
||||
hideCohere,
|
||||
mistralParams,
|
||||
hideMistral,
|
||||
nomicParams,
|
||||
hideNomic,
|
||||
nvidiaParams,
|
||||
hideNvidia,
|
||||
voyageaiParams,
|
||||
hideVoyageai,
|
||||
ibmParams,
|
||||
hideIBM,
|
||||
fakeEmbeddingParams,
|
||||
hideFakeEmbedding,
|
||||
customVarName,
|
||||
} = props;
|
||||
const [selectedModel, setSelectedModel] = useState("OpenAI");
|
||||
const {
|
||||
openaiParams,
|
||||
hideOpenai,
|
||||
azureOpenaiParams,
|
||||
hideAzureOpenai,
|
||||
googleGenAIParams,
|
||||
hideGoogleGenAI,
|
||||
googleVertexAIParams,
|
||||
hideGoogleVertexAI,
|
||||
awsParams,
|
||||
hideAws,
|
||||
huggingFaceParams,
|
||||
hideHuggingFace,
|
||||
ollamaParams,
|
||||
hideOllama,
|
||||
cohereParams,
|
||||
hideCohere,
|
||||
mistralParams,
|
||||
hideMistral,
|
||||
nomicParams,
|
||||
hideNomic,
|
||||
nvidiaParams,
|
||||
hideNvidia,
|
||||
voyageaiParams,
|
||||
hideVoyageai,
|
||||
ibmParams,
|
||||
hideIBM,
|
||||
fakeEmbeddingParams,
|
||||
hideFakeEmbedding,
|
||||
customVarName,
|
||||
} = props;
|
||||
|
||||
const openAIParamsOrDefault = openaiParams ?? `model="text-embedding-3-large"`;
|
||||
const azureParamsOrDefault =
|
||||
azureOpenaiParams ??
|
||||
`\n azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],\n azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],\n openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],\n`;
|
||||
const googleGenAIParamsOrDefault = googleGenAIParams ?? `model="models/embedding-001"`;
|
||||
const googleVertexAIParamsOrDefault = googleVertexAIParams ?? `model="text-embedding-004"`;
|
||||
const awsParamsOrDefault = awsParams ?? `model_id="amazon.titan-embed-text-v2:0"`;
|
||||
const huggingFaceParamsOrDefault = huggingFaceParams ?? `model_name="sentence-transformers/all-mpnet-base-v2"`;
|
||||
const ollamaParamsOrDefault = ollamaParams ?? `model="llama3"`;
|
||||
const cohereParamsOrDefault = cohereParams ?? `model="embed-english-v3.0"`;
|
||||
const mistralParamsOrDefault = mistralParams ?? `model="mistral-embed"`;
|
||||
const nomicsParamsOrDefault = nomicParams ?? `model="nomic-embed-text-v1.5"`;
|
||||
const nvidiaParamsOrDefault = nvidiaParams ?? `model="NV-Embed-QA"`;
|
||||
const voyageaiParamsOrDefault = voyageaiParams ?? `model="voyage-3"`;
|
||||
const ibmParamsOrDefault = ibmParams ??
|
||||
`\n model_id="ibm/slate-125m-english-rtrvr",\n url="https://us-south.ml.cloud.ibm.com",\n project_id="<WATSONX PROJECT_ID>",\n`;
|
||||
const fakeEmbeddingParamsOrDefault = fakeEmbeddingParams ?? `size=4096`;
|
||||
const openAIParamsOrDefault = openaiParams ?? `model="text-embedding-3-large"`;
|
||||
const azureParamsOrDefault =
|
||||
azureOpenaiParams ??
|
||||
`\n azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],\n azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],\n openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],\n`;
|
||||
const googleGenAIParamsOrDefault = googleGenAIParams ?? `model="models/gemini-embedding-001"`;
|
||||
const googleVertexAIParamsOrDefault = googleVertexAIParams ?? `model="text-embedding-005"`;
|
||||
const awsParamsOrDefault = awsParams ?? `model_id="amazon.titan-embed-text-v2:0"`;
|
||||
const huggingFaceParamsOrDefault = huggingFaceParams ?? `model_name="sentence-transformers/all-mpnet-base-v2"`;
|
||||
const ollamaParamsOrDefault = ollamaParams ?? `model="llama3"`;
|
||||
const cohereParamsOrDefault = cohereParams ?? `model="embed-english-v3.0"`;
|
||||
const mistralParamsOrDefault = mistralParams ?? `model="mistral-embed"`;
|
||||
const nomicsParamsOrDefault = nomicParams ?? `model="nomic-embed-text-v1.5"`;
|
||||
const nvidiaParamsOrDefault = nvidiaParams ?? `model="NV-Embed-QA"`;
|
||||
const voyageaiParamsOrDefault = voyageaiParams ?? `model="voyage-3"`;
|
||||
const ibmParamsOrDefault = ibmParams ??
|
||||
`\n model_id="ibm/slate-125m-english-rtrvr",\n url="https://us-south.ml.cloud.ibm.com",\n project_id="<WATSONX PROJECT_ID>",\n`;
|
||||
const fakeEmbeddingParamsOrDefault = fakeEmbeddingParams ?? `size=4096`;
|
||||
|
||||
const embeddingVarName = customVarName ?? "embeddings";
|
||||
const embeddingVarName = customVarName ?? "embeddings";
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
value: "OpenAI",
|
||||
label: "OpenAI",
|
||||
text: `from langchain_openai import OpenAIEmbeddings\n\n${embeddingVarName} = OpenAIEmbeddings(${openAIParamsOrDefault})`,
|
||||
apiKeyName: "OPENAI_API_KEY",
|
||||
packageName: "langchain-openai",
|
||||
default: true,
|
||||
shouldHide: hideOpenai,
|
||||
},
|
||||
{
|
||||
value: "Azure",
|
||||
label: "Azure",
|
||||
text: `from langchain_openai import AzureOpenAIEmbeddings\n\n${embeddingVarName} = AzureOpenAIEmbeddings(${azureParamsOrDefault})`,
|
||||
apiKeyName: "AZURE_OPENAI_API_KEY",
|
||||
packageName: "langchain-openai",
|
||||
default: false,
|
||||
shouldHide: hideAzureOpenai,
|
||||
},
|
||||
{
|
||||
value: "GoogleGenAI",
|
||||
label: "Google Gemini",
|
||||
text: `from langchain_google_genai import GoogleGenerativeAIEmbeddings\n\n${embeddingVarName} = GoogleGenerativeAIEmbeddings(${googleGenAIParamsOrDefault})`,
|
||||
apiKeyName: "GOOGLE_API_KEY",
|
||||
packageName: "langchain-google-genai",
|
||||
default: false,
|
||||
shouldHide: hideGoogleGenAI,
|
||||
},
|
||||
{
|
||||
value: "GoogleVertexAI",
|
||||
label: "Google Vertex",
|
||||
text: `from langchain_google_vertexai import VertexAIEmbeddings\n\n${embeddingVarName} = VertexAIEmbeddings(${googleVertexAIParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-google-vertexai",
|
||||
default: false,
|
||||
shouldHide: hideGoogleVertexAI,
|
||||
},
|
||||
{
|
||||
value: "AWS",
|
||||
label: "AWS",
|
||||
text: `from langchain_aws import BedrockEmbeddings\n\n${embeddingVarName} = BedrockEmbeddings(${awsParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-aws",
|
||||
default: false,
|
||||
shouldHide: hideAws,
|
||||
},
|
||||
{
|
||||
value: "HuggingFace",
|
||||
label: "HuggingFace",
|
||||
text: `from langchain_huggingface import HuggingFaceEmbeddings\n\n${embeddingVarName} = HuggingFaceEmbeddings(${huggingFaceParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-huggingface",
|
||||
default: false,
|
||||
shouldHide: hideHuggingFace,
|
||||
},
|
||||
{
|
||||
value: "Ollama",
|
||||
label: "Ollama",
|
||||
text: `from langchain_ollama import OllamaEmbeddings\n\n${embeddingVarName} = OllamaEmbeddings(${ollamaParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-ollama",
|
||||
default: false,
|
||||
shouldHide: hideOllama,
|
||||
},
|
||||
{
|
||||
value: "Cohere",
|
||||
label: "Cohere",
|
||||
text: `from langchain_cohere import CohereEmbeddings\n\n${embeddingVarName} = CohereEmbeddings(${cohereParamsOrDefault})`,
|
||||
apiKeyName: "COHERE_API_KEY",
|
||||
packageName: "langchain-cohere",
|
||||
default: false,
|
||||
shouldHide: hideCohere,
|
||||
},
|
||||
{
|
||||
value: "MistralAI",
|
||||
label: "MistralAI",
|
||||
text: `from langchain_mistralai import MistralAIEmbeddings\n\n${embeddingVarName} = MistralAIEmbeddings(${mistralParamsOrDefault})`,
|
||||
apiKeyName: "MISTRALAI_API_KEY",
|
||||
packageName: "langchain-mistralai",
|
||||
default: false,
|
||||
shouldHide: hideMistral,
|
||||
},
|
||||
{
|
||||
value: "Nomic",
|
||||
label: "Nomic",
|
||||
text: `from langchain_nomic import NomicEmbeddings\n\n${embeddingVarName} = NomicEmbeddings(${nomicsParamsOrDefault})`,
|
||||
apiKeyName: "NOMIC_API_KEY",
|
||||
packageName: "langchain-nomic",
|
||||
default: false,
|
||||
shouldHide: hideNomic,
|
||||
},
|
||||
{
|
||||
value: "NVIDIA",
|
||||
label: "NVIDIA",
|
||||
text: `from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings\n\n${embeddingVarName} = NVIDIAEmbeddings(${nvidiaParamsOrDefault})`,
|
||||
apiKeyName: "NVIDIA_API_KEY",
|
||||
packageName: "langchain-nvidia-ai-endpoints",
|
||||
default: false,
|
||||
shouldHide: hideNvidia,
|
||||
},
|
||||
{
|
||||
value: "Voyage AI",
|
||||
label: "Voyage AI",
|
||||
text: `from langchain-voyageai import VoyageAIEmbeddings\n\n${embeddingVarName} = VoyageAIEmbeddings(${voyageaiParamsOrDefault})`,
|
||||
apiKeyName: "VOYAGE_API_KEY",
|
||||
packageName: "langchain-voyageai",
|
||||
default: false,
|
||||
shouldHide: hideVoyageai,
|
||||
},
|
||||
{
|
||||
value: "IBM",
|
||||
label: "IBM watsonx",
|
||||
text: `from langchain_ibm import WatsonxEmbeddings\n\n${embeddingVarName} = WatsonxEmbeddings(${ibmParamsOrDefault})`,
|
||||
apiKeyName: "WATSONX_APIKEY",
|
||||
packageName: "langchain-ibm",
|
||||
default: false,
|
||||
shouldHide: hideIBM,
|
||||
},
|
||||
{
|
||||
value: "Fake",
|
||||
label: "Fake",
|
||||
text: `from langchain_core.embeddings import DeterministicFakeEmbedding\n\n${embeddingVarName} = DeterministicFakeEmbedding(${fakeEmbeddingParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-core",
|
||||
default: false,
|
||||
shouldHide: hideFakeEmbedding,
|
||||
},
|
||||
];
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
value: "OpenAI",
|
||||
label: "OpenAI",
|
||||
text: `from langchain_openai import OpenAIEmbeddings\n\n${embeddingVarName} = OpenAIEmbeddings(${openAIParamsOrDefault})`,
|
||||
apiKeyName: "OPENAI_API_KEY",
|
||||
packageName: "langchain-openai",
|
||||
default: true,
|
||||
shouldHide: hideOpenai,
|
||||
},
|
||||
{
|
||||
value: "Azure",
|
||||
label: "Azure",
|
||||
text: `from langchain_openai import AzureOpenAIEmbeddings\n\n${embeddingVarName} = AzureOpenAIEmbeddings(${azureParamsOrDefault})`,
|
||||
apiKeyName: "AZURE_OPENAI_API_KEY",
|
||||
packageName: "langchain-openai",
|
||||
default: false,
|
||||
shouldHide: hideAzureOpenai,
|
||||
},
|
||||
{
|
||||
value: "GoogleGenAI",
|
||||
label: "Google Gemini",
|
||||
text: `from langchain_google_genai import GoogleGenerativeAIEmbeddings\n\n${embeddingVarName} = GoogleGenerativeAIEmbeddings(${googleGenAIParamsOrDefault})`,
|
||||
apiKeyName: "GOOGLE_API_KEY",
|
||||
packageName: "langchain-google-genai",
|
||||
default: false,
|
||||
shouldHide: hideGoogleGenAI,
|
||||
},
|
||||
{
|
||||
value: "GoogleVertexAI",
|
||||
label: "Google Vertex",
|
||||
text: `from langchain_google_vertexai import VertexAIEmbeddings\n\n${embeddingVarName} = VertexAIEmbeddings(${googleVertexAIParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-google-vertexai",
|
||||
default: false,
|
||||
shouldHide: hideGoogleVertexAI,
|
||||
},
|
||||
{
|
||||
value: "AWS",
|
||||
label: "AWS",
|
||||
text: `from langchain_aws import BedrockEmbeddings\n\n${embeddingVarName} = BedrockEmbeddings(${awsParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-aws",
|
||||
default: false,
|
||||
shouldHide: hideAws,
|
||||
},
|
||||
{
|
||||
value: "HuggingFace",
|
||||
label: "HuggingFace",
|
||||
text: `from langchain_huggingface import HuggingFaceEmbeddings\n\n${embeddingVarName} = HuggingFaceEmbeddings(${huggingFaceParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-huggingface",
|
||||
default: false,
|
||||
shouldHide: hideHuggingFace,
|
||||
},
|
||||
{
|
||||
value: "Ollama",
|
||||
label: "Ollama",
|
||||
text: `from langchain_ollama import OllamaEmbeddings\n\n${embeddingVarName} = OllamaEmbeddings(${ollamaParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-ollama",
|
||||
default: false,
|
||||
shouldHide: hideOllama,
|
||||
},
|
||||
{
|
||||
value: "Cohere",
|
||||
label: "Cohere",
|
||||
text: `from langchain_cohere import CohereEmbeddings\n\n${embeddingVarName} = CohereEmbeddings(${cohereParamsOrDefault})`,
|
||||
apiKeyName: "COHERE_API_KEY",
|
||||
packageName: "langchain-cohere",
|
||||
default: false,
|
||||
shouldHide: hideCohere,
|
||||
},
|
||||
{
|
||||
value: "MistralAI",
|
||||
label: "MistralAI",
|
||||
text: `from langchain_mistralai import MistralAIEmbeddings\n\n${embeddingVarName} = MistralAIEmbeddings(${mistralParamsOrDefault})`,
|
||||
apiKeyName: "MISTRALAI_API_KEY",
|
||||
packageName: "langchain-mistralai",
|
||||
default: false,
|
||||
shouldHide: hideMistral,
|
||||
},
|
||||
{
|
||||
value: "Nomic",
|
||||
label: "Nomic",
|
||||
text: `from langchain_nomic import NomicEmbeddings\n\n${embeddingVarName} = NomicEmbeddings(${nomicsParamsOrDefault})`,
|
||||
apiKeyName: "NOMIC_API_KEY",
|
||||
packageName: "langchain-nomic",
|
||||
default: false,
|
||||
shouldHide: hideNomic,
|
||||
},
|
||||
{
|
||||
value: "NVIDIA",
|
||||
label: "NVIDIA",
|
||||
text: `from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings\n\n${embeddingVarName} = NVIDIAEmbeddings(${nvidiaParamsOrDefault})`,
|
||||
apiKeyName: "NVIDIA_API_KEY",
|
||||
packageName: "langchain-nvidia-ai-endpoints",
|
||||
default: false,
|
||||
shouldHide: hideNvidia,
|
||||
},
|
||||
{
|
||||
value: "Voyage AI",
|
||||
label: "Voyage AI",
|
||||
text: `from langchain-voyageai import VoyageAIEmbeddings\n\n${embeddingVarName} = VoyageAIEmbeddings(${voyageaiParamsOrDefault})`,
|
||||
apiKeyName: "VOYAGE_API_KEY",
|
||||
packageName: "langchain-voyageai",
|
||||
default: false,
|
||||
shouldHide: hideVoyageai,
|
||||
},
|
||||
{
|
||||
value: "IBM",
|
||||
label: "IBM watsonx",
|
||||
text: `from langchain_ibm import WatsonxEmbeddings\n\n${embeddingVarName} = WatsonxEmbeddings(${ibmParamsOrDefault})`,
|
||||
apiKeyName: "WATSONX_APIKEY",
|
||||
packageName: "langchain-ibm",
|
||||
default: false,
|
||||
shouldHide: hideIBM,
|
||||
},
|
||||
{
|
||||
value: "Fake",
|
||||
label: "Fake",
|
||||
text: `from langchain_core.embeddings import DeterministicFakeEmbedding\n\n${embeddingVarName} = DeterministicFakeEmbedding(${fakeEmbeddingParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-core",
|
||||
default: false,
|
||||
shouldHide: hideFakeEmbedding,
|
||||
},
|
||||
];
|
||||
|
||||
const modelOptions = tabItems
|
||||
.filter((item) => !item.shouldHide)
|
||||
.map((item) => ({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
text: item.text,
|
||||
apiKeyName: item.apiKeyName,
|
||||
apiKeyText: item.apiKeyText,
|
||||
packageName: item.packageName,
|
||||
}));
|
||||
.filter((item) => !item.shouldHide)
|
||||
.map((item) => ({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
text: item.text,
|
||||
apiKeyName: item.apiKeyName,
|
||||
apiKeyText: item.apiKeyText,
|
||||
packageName: item.packageName,
|
||||
}));
|
||||
|
||||
const selectedOption = modelOptions.find(
|
||||
(option) => option.value === selectedModel
|
||||
);
|
||||
const selectedOption = modelOptions.find(
|
||||
(option) => option.value === selectedModel
|
||||
);
|
||||
|
||||
let apiKeyText = "";
|
||||
if (selectedOption.apiKeyName) {
|
||||
apiKeyText = `import getpass
|
||||
let apiKeyText = "";
|
||||
if (selectedOption.apiKeyName) {
|
||||
apiKeyText = `import getpass
|
||||
import os
|
||||
|
||||
if not os.environ.get("${selectedOption.apiKeyName}"):
|
||||
@ -211,21 +211,21 @@ if not os.environ.get("${selectedOption.apiKeyName}"):
|
||||
apiKeyText = selectedOption.apiKeyText;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CustomDropdown
|
||||
selectedOption={selectedOption}
|
||||
options={modelOptions}
|
||||
onSelect={setSelectedModel}
|
||||
modelType="embeddings"
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<CustomDropdown
|
||||
selectedOption={selectedOption}
|
||||
options={modelOptions}
|
||||
onSelect={setSelectedModel}
|
||||
modelType="embeddings"
|
||||
/>
|
||||
|
||||
<CodeBlock language="bash">
|
||||
{`pip install -qU ${selectedOption.packageName}`}
|
||||
</CodeBlock>
|
||||
<CodeBlock language="python">
|
||||
{apiKeyText ? apiKeyText + "\n\n" + selectedOption.text : selectedOption.text}
|
||||
</CodeBlock>
|
||||
</div>
|
||||
);
|
||||
<CodeBlock language="bash">
|
||||
{`pip install -qU ${selectedOption.packageName}`}
|
||||
</CodeBlock>
|
||||
<CodeBlock language="python">
|
||||
{apiKeyText ? apiKeyText + "\n\n" + selectedOption.text : selectedOption.text}
|
||||
</CodeBlock>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -11,22 +11,9 @@ from abc import ABC, abstractmethod
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from functools import cached_property
|
||||
from operator import itemgetter
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Literal,
|
||||
Optional,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, cast
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
model_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from typing_extensions import override
|
||||
|
||||
from langchain_core._api import deprecated
|
||||
@ -63,6 +50,7 @@ from langchain_core.outputs import (
|
||||
ChatGeneration,
|
||||
ChatGenerationChunk,
|
||||
ChatResult,
|
||||
Generation,
|
||||
LLMResult,
|
||||
RunInfo,
|
||||
)
|
||||
@ -653,6 +641,34 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: # noqa: ARG002
|
||||
return {}
|
||||
|
||||
def _convert_cached_generations(self, cache_val: list) -> list[ChatGeneration]:
|
||||
"""Convert cached Generation objects to ChatGeneration objects.
|
||||
|
||||
Handle case where cache contains Generation objects instead of
|
||||
ChatGeneration objects. This can happen due to serialization/deserialization
|
||||
issues or legacy cache data (see #22389).
|
||||
|
||||
Args:
|
||||
cache_val: List of cached generation objects.
|
||||
|
||||
Returns:
|
||||
List of ChatGeneration objects.
|
||||
"""
|
||||
converted_generations = []
|
||||
for gen in cache_val:
|
||||
if isinstance(gen, Generation) and not isinstance(gen, ChatGeneration):
|
||||
# Convert Generation to ChatGeneration by creating AIMessage
|
||||
# from the text content
|
||||
chat_gen = ChatGeneration(
|
||||
message=AIMessage(content=gen.text),
|
||||
generation_info=gen.generation_info,
|
||||
)
|
||||
converted_generations.append(chat_gen)
|
||||
else:
|
||||
# Already a ChatGeneration or other expected type
|
||||
converted_generations.append(gen)
|
||||
return converted_generations
|
||||
|
||||
def _get_invocation_params(
|
||||
self,
|
||||
stop: Optional[list[str]] = None,
|
||||
@ -1010,7 +1026,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
prompt = dumps(messages)
|
||||
cache_val = llm_cache.lookup(prompt, llm_string)
|
||||
if isinstance(cache_val, list):
|
||||
return ChatResult(generations=cache_val)
|
||||
converted_generations = self._convert_cached_generations(cache_val)
|
||||
return ChatResult(generations=converted_generations)
|
||||
elif self.cache is None:
|
||||
pass
|
||||
else:
|
||||
@ -1082,7 +1099,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
prompt = dumps(messages)
|
||||
cache_val = await llm_cache.alookup(prompt, llm_string)
|
||||
if isinstance(cache_val, list):
|
||||
return ChatResult(generations=cache_val)
|
||||
converted_generations = self._convert_cached_generations(cache_val)
|
||||
return ChatResult(generations=converted_generations)
|
||||
elif self.cache is None:
|
||||
pass
|
||||
else:
|
||||
|
@ -108,6 +108,39 @@ def merge_lists(left: Optional[list], *others: Optional[list]) -> Optional[list]
|
||||
else e
|
||||
)
|
||||
merged[to_merge[0]] = merge_dicts(merged[to_merge[0]], new_e)
|
||||
# Special handling for tool call chunks: if this chunk appears to be
|
||||
# a continuation of a prior chunk (has None name/id) and no matching
|
||||
# index was found, try to merge with the most recent tool call chunk
|
||||
# that has a name/id.
|
||||
# Fixes issues with models that send inconsistent indices.
|
||||
# See #31511 for more.
|
||||
elif (
|
||||
e.get("type") == "tool_call_chunk"
|
||||
and e.get("name") is None
|
||||
and e.get("id") is None
|
||||
and merged
|
||||
):
|
||||
# Find the most recent tool call chunk with a valid name or id
|
||||
for i in reversed(range(len(merged))):
|
||||
if (
|
||||
isinstance(merged[i], dict)
|
||||
and merged[i].get("type") == "tool_call_chunk"
|
||||
and (
|
||||
merged[i].get("name") is not None
|
||||
or merged[i].get("id") is not None
|
||||
)
|
||||
):
|
||||
# Merge with this chunk
|
||||
new_e = (
|
||||
{k: v for k, v in e.items() if k != "type"}
|
||||
if "type" in e
|
||||
else e
|
||||
)
|
||||
merged[i] = merge_dicts(merged[i], new_e)
|
||||
break
|
||||
else:
|
||||
# No suitable chunk found, append as new
|
||||
merged.append(e)
|
||||
else:
|
||||
merged.append(e)
|
||||
else:
|
||||
|
@ -13,7 +13,8 @@ from langchain_core.language_models.fake_chat_models import (
|
||||
GenericFakeChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_core.outputs import ChatGeneration
|
||||
from langchain_core.outputs import ChatGeneration, Generation
|
||||
from langchain_core.outputs.chat_result import ChatResult
|
||||
|
||||
|
||||
class InMemoryCache(BaseCache):
|
||||
@ -305,6 +306,93 @@ def test_llm_representation_for_serializable() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_cache_with_generation_objects() -> None:
|
||||
"""Test that cache can handle Generation objects instead of ChatGeneration objects.
|
||||
|
||||
This test reproduces a bug where cache returns Generation objects
|
||||
but ChatResult expects ChatGeneration objects, causing validation errors.
|
||||
|
||||
See #22389 for more info.
|
||||
|
||||
"""
|
||||
cache = InMemoryCache()
|
||||
|
||||
# Create a simple fake chat model that we can control
|
||||
from langchain_core.messages import AIMessage
|
||||
|
||||
class SimpleFakeChat:
|
||||
"""Simple fake chat model for testing."""
|
||||
|
||||
def __init__(self, cache: BaseCache) -> None:
|
||||
self.cache = cache
|
||||
self.response = "hello"
|
||||
|
||||
def _get_llm_string(self) -> str:
|
||||
return "test_llm_string"
|
||||
|
||||
def generate_response(self, prompt: str) -> ChatResult:
|
||||
"""Simulate the cache lookup and generation logic."""
|
||||
from langchain_core.load import dumps
|
||||
|
||||
llm_string = self._get_llm_string()
|
||||
prompt_str = dumps([prompt])
|
||||
|
||||
# Check cache first
|
||||
cache_val = self.cache.lookup(prompt_str, llm_string)
|
||||
if cache_val:
|
||||
# This is where our fix should work
|
||||
converted_generations = []
|
||||
for gen in cache_val:
|
||||
if isinstance(gen, Generation) and not isinstance(
|
||||
gen, ChatGeneration
|
||||
):
|
||||
# Convert Generation to ChatGeneration by creating an AIMessage
|
||||
chat_gen = ChatGeneration(
|
||||
message=AIMessage(content=gen.text),
|
||||
generation_info=gen.generation_info,
|
||||
)
|
||||
converted_generations.append(chat_gen)
|
||||
else:
|
||||
converted_generations.append(gen)
|
||||
return ChatResult(generations=converted_generations)
|
||||
|
||||
# Generate new response
|
||||
chat_gen = ChatGeneration(
|
||||
message=AIMessage(content=self.response), generation_info={}
|
||||
)
|
||||
result = ChatResult(generations=[chat_gen])
|
||||
|
||||
# Store in cache
|
||||
self.cache.update(prompt_str, llm_string, result.generations)
|
||||
return result
|
||||
|
||||
model = SimpleFakeChat(cache)
|
||||
|
||||
# First call - normal operation
|
||||
result1 = model.generate_response("test prompt")
|
||||
assert result1.generations[0].message.content == "hello"
|
||||
|
||||
# Manually corrupt the cache by replacing ChatGeneration with Generation
|
||||
cache_key = next(iter(cache._cache.keys()))
|
||||
cached_chat_generations = cache._cache[cache_key]
|
||||
|
||||
# Replace with Generation objects (missing message field)
|
||||
corrupted_generations = [
|
||||
Generation(
|
||||
text=gen.text,
|
||||
generation_info=gen.generation_info,
|
||||
type="Generation", # This is the key - wrong type
|
||||
)
|
||||
for gen in cached_chat_generations
|
||||
]
|
||||
cache._cache[cache_key] = corrupted_generations
|
||||
|
||||
# Second call should handle the Generation objects gracefully
|
||||
result2 = model.generate_response("test prompt")
|
||||
assert result2.generations[0].message.content == "hello"
|
||||
assert isinstance(result2.generations[0], ChatGeneration)
|
||||
|
||||
|
||||
def test_cleanup_serialized() -> None:
|
||||
cleanup_serialized = {
|
||||
"lc": 1,
|
||||
|
@ -1197,3 +1197,65 @@ def test_convert_to_openai_image_block() -> None:
|
||||
}
|
||||
result = convert_to_openai_image_block(input_block)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_tool_call_streaming_different_indices() -> None:
|
||||
"""Test that tool call chunks with different indices but logically part of the same
|
||||
tool call are merged correctly. This addresses issues with models like Qwen3 that
|
||||
send inconsistent indices during streaming.
|
||||
|
||||
See #31511.
|
||||
|
||||
""" # noqa: D205
|
||||
# Create chunks that simulate Qwen3 behavior:
|
||||
# First chunk has index=1, subsequent chunks have index=0 with name=None, id=None
|
||||
chunk1 = AIMessageChunk(
|
||||
content="",
|
||||
tool_call_chunks=[
|
||||
create_tool_call_chunk(
|
||||
name="search_function",
|
||||
args='{"query": "langchain',
|
||||
id="call_123",
|
||||
index=1, # Initial index
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
chunk2 = AIMessageChunk(
|
||||
content="",
|
||||
tool_call_chunks=[
|
||||
create_tool_call_chunk(
|
||||
name=None, # Continuation chunk
|
||||
args=' tutorial"}',
|
||||
id=None, # Continuation chunk
|
||||
index=0, # Different index
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Merge chunks as happens during streaming
|
||||
merged_chunk: AIMessageChunk = chunk1 + chunk2 # type: ignore[assignment]
|
||||
|
||||
# Should result in a single merged tool call chunk
|
||||
assert len(merged_chunk.tool_call_chunks) == 1
|
||||
assert merged_chunk.tool_call_chunks[0]["name"] == "search_function"
|
||||
assert merged_chunk.tool_call_chunks[0]["args"] == '{"query": "langchain tutorial"}'
|
||||
assert merged_chunk.tool_call_chunks[0]["id"] == "call_123"
|
||||
|
||||
# Should result in a single valid tool call
|
||||
assert len(merged_chunk.tool_calls) == 1
|
||||
assert len(merged_chunk.invalid_tool_calls) == 0
|
||||
|
||||
# Verify the final tool call is correct
|
||||
tool_call = merged_chunk.tool_calls[0]
|
||||
assert tool_call["name"] == "search_function"
|
||||
assert tool_call["args"] == {"query": "langchain tutorial"}
|
||||
assert tool_call["id"] == "call_123"
|
||||
|
||||
# Test with message_chunk_to_message
|
||||
message: AIMessage = message_chunk_to_message(merged_chunk) # type: ignore[assignment]
|
||||
|
||||
assert len(message.tool_calls) == 1
|
||||
assert len(message.invalid_tool_calls) == 0
|
||||
assert message.tool_calls[0]["name"] == "search_function"
|
||||
assert message.tool_calls[0]["args"] == {"query": "langchain tutorial"}
|
||||
|
@ -3,15 +3,7 @@ from __future__ import annotations
|
||||
import warnings
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from importlib import util
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Literal,
|
||||
Optional,
|
||||
Union,
|
||||
cast,
|
||||
overload,
|
||||
)
|
||||
from typing import Any, Callable, Literal, Optional, Union, cast, overload
|
||||
|
||||
from langchain_core.language_models import (
|
||||
BaseChatModel,
|
||||
@ -188,7 +180,7 @@ def init_chat_model(
|
||||
|
||||
o3_mini = init_chat_model("openai:o3-mini", temperature=0)
|
||||
claude_sonnet = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0)
|
||||
gemini_2_flash = init_chat_model("google_vertexai:gemini-2.0-flash", temperature=0)
|
||||
gemini_2_flash = init_chat_model("google_vertexai:gemini-2.5-flash", temperature=0)
|
||||
|
||||
o3_mini.invoke("what's your name")
|
||||
claude_sonnet.invoke("what's your name")
|
||||
|
@ -13,10 +13,7 @@ from typing import (
|
||||
overload,
|
||||
)
|
||||
|
||||
from langchain_core.language_models import (
|
||||
BaseChatModel,
|
||||
LanguageModelInput,
|
||||
)
|
||||
from langchain_core.language_models import BaseChatModel, LanguageModelInput
|
||||
from langchain_core.messages import AnyMessage, BaseMessage
|
||||
from langchain_core.runnables import Runnable, RunnableConfig, ensure_config
|
||||
from typing_extensions import TypeAlias, override
|
||||
@ -177,7 +174,7 @@ def init_chat_model(
|
||||
|
||||
o3_mini = init_chat_model("openai:o3-mini", temperature=0)
|
||||
claude_sonnet = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0)
|
||||
gemini_2_flash = init_chat_model("google_vertexai:gemini-2.0-flash", temperature=0)
|
||||
gemini_2_flash = init_chat_model("google_vertexai:gemini-2.5-flash", temperature=0)
|
||||
|
||||
o3_mini.invoke("what's your name")
|
||||
claude_sonnet.invoke("what's your name")
|
||||
|
@ -3,11 +3,7 @@ from __future__ import annotations
|
||||
import re
|
||||
import warnings
|
||||
from collections.abc import AsyncIterator, Iterator, Mapping
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Optional,
|
||||
)
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import anthropic
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
@ -19,14 +15,8 @@ from langchain_core.language_models import BaseLanguageModel, LangSmithParams
|
||||
from langchain_core.language_models.llms import LLM
|
||||
from langchain_core.outputs import GenerationChunk
|
||||
from langchain_core.prompt_values import PromptValue
|
||||
from langchain_core.utils import (
|
||||
get_pydantic_field_names,
|
||||
)
|
||||
from langchain_core.utils.utils import (
|
||||
_build_model_kwargs,
|
||||
from_env,
|
||||
secret_from_env,
|
||||
)
|
||||
from langchain_core.utils import get_pydantic_field_names
|
||||
from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
|
||||
from pydantic import ConfigDict, Field, SecretStr, model_validator
|
||||
from typing_extensions import Self
|
||||
|
||||
@ -34,10 +24,10 @@ from typing_extensions import Self
|
||||
class _AnthropicCommon(BaseLanguageModel):
|
||||
client: Any = None #: :meta private:
|
||||
async_client: Any = None #: :meta private:
|
||||
model: str = Field(default="claude-2", alias="model_name")
|
||||
model: str = Field(default="claude-3-5-sonnet-latest", alias="model_name")
|
||||
"""Model name to use."""
|
||||
|
||||
max_tokens_to_sample: int = Field(default=1024, alias="max_tokens")
|
||||
max_tokens: int = Field(default=1024, alias="max_tokens_to_sample")
|
||||
"""Denotes the number of tokens to predict per generation."""
|
||||
|
||||
temperature: Optional[float] = None
|
||||
@ -104,15 +94,16 @@ class _AnthropicCommon(BaseLanguageModel):
|
||||
timeout=self.default_request_timeout,
|
||||
max_retries=self.max_retries,
|
||||
)
|
||||
self.HUMAN_PROMPT = anthropic.HUMAN_PROMPT
|
||||
self.AI_PROMPT = anthropic.AI_PROMPT
|
||||
# Keep for backward compatibility but not used in Messages API
|
||||
self.HUMAN_PROMPT = getattr(anthropic, "HUMAN_PROMPT", None)
|
||||
self.AI_PROMPT = getattr(anthropic, "AI_PROMPT", None)
|
||||
return self
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Mapping[str, Any]:
|
||||
"""Get the default parameters for calling Anthropic API."""
|
||||
d = {
|
||||
"max_tokens_to_sample": self.max_tokens_to_sample,
|
||||
"max_tokens": self.max_tokens,
|
||||
"model": self.model,
|
||||
}
|
||||
if self.temperature is not None:
|
||||
@ -129,16 +120,8 @@ class _AnthropicCommon(BaseLanguageModel):
|
||||
return {**self._default_params}
|
||||
|
||||
def _get_anthropic_stop(self, stop: Optional[list[str]] = None) -> list[str]:
|
||||
if not self.HUMAN_PROMPT or not self.AI_PROMPT:
|
||||
msg = "Please ensure the anthropic package is loaded"
|
||||
raise NameError(msg)
|
||||
|
||||
if stop is None:
|
||||
stop = []
|
||||
|
||||
# Never want model to invent new turns of Human / Assistant dialog.
|
||||
stop.extend([self.HUMAN_PROMPT])
|
||||
|
||||
return stop
|
||||
|
||||
|
||||
@ -192,7 +175,7 @@ class AnthropicLLM(LLM, _AnthropicCommon):
|
||||
"""Get the identifying parameters."""
|
||||
return {
|
||||
"model": self.model,
|
||||
"max_tokens": self.max_tokens_to_sample,
|
||||
"max_tokens": self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"top_k": self.top_k,
|
||||
"top_p": self.top_p,
|
||||
@ -211,27 +194,51 @@ class AnthropicLLM(LLM, _AnthropicCommon):
|
||||
params = super()._get_ls_params(stop=stop, **kwargs)
|
||||
identifying_params = self._identifying_params
|
||||
if max_tokens := kwargs.get(
|
||||
"max_tokens_to_sample",
|
||||
"max_tokens",
|
||||
identifying_params.get("max_tokens"),
|
||||
):
|
||||
params["ls_max_tokens"] = max_tokens
|
||||
return params
|
||||
|
||||
def _wrap_prompt(self, prompt: str) -> str:
|
||||
if not self.HUMAN_PROMPT or not self.AI_PROMPT:
|
||||
msg = "Please ensure the anthropic package is loaded"
|
||||
raise NameError(msg)
|
||||
def _format_messages(self, prompt: str) -> list[dict[str, str]]:
|
||||
"""Convert prompt to Messages API format."""
|
||||
messages = []
|
||||
|
||||
if prompt.startswith(self.HUMAN_PROMPT):
|
||||
return prompt # Already wrapped.
|
||||
# Handle legacy prompts that might have HUMAN_PROMPT/AI_PROMPT markers
|
||||
if self.HUMAN_PROMPT and self.HUMAN_PROMPT in prompt:
|
||||
# Split on human/assistant turns
|
||||
parts = prompt.split(self.HUMAN_PROMPT)
|
||||
|
||||
# Guard against common errors in specifying wrong number of newlines.
|
||||
corrected_prompt, n_subs = re.subn(r"^\n*Human:", self.HUMAN_PROMPT, prompt)
|
||||
if n_subs == 1:
|
||||
return corrected_prompt
|
||||
for _, part in enumerate(parts):
|
||||
if not part.strip():
|
||||
continue
|
||||
|
||||
# As a last resort, wrap the prompt ourselves to emulate instruct-style.
|
||||
return f"{self.HUMAN_PROMPT} {prompt}{self.AI_PROMPT} Sure, here you go:\n"
|
||||
if self.AI_PROMPT and self.AI_PROMPT in part:
|
||||
# Split human and assistant parts
|
||||
human_part, assistant_part = part.split(self.AI_PROMPT, 1)
|
||||
if human_part.strip():
|
||||
messages.append({"role": "user", "content": human_part.strip()})
|
||||
if assistant_part.strip():
|
||||
messages.append(
|
||||
{"role": "assistant", "content": assistant_part.strip()}
|
||||
)
|
||||
else:
|
||||
# Just human content
|
||||
if part.strip():
|
||||
messages.append({"role": "user", "content": part.strip()})
|
||||
else:
|
||||
# Handle modern format or plain text
|
||||
# Clean prompt for Messages API
|
||||
content = re.sub(r"^\n*Human:\s*", "", prompt)
|
||||
content = re.sub(r"\n*Assistant:\s*.*$", "", content)
|
||||
if content.strip():
|
||||
messages.append({"role": "user", "content": content.strip()})
|
||||
|
||||
# Ensure we have at least one message
|
||||
if not messages:
|
||||
messages = [{"role": "user", "content": prompt.strip() or "Hello"}]
|
||||
|
||||
return messages
|
||||
|
||||
def _call(
|
||||
self,
|
||||
@ -272,15 +279,19 @@ class AnthropicLLM(LLM, _AnthropicCommon):
|
||||
|
||||
stop = self._get_anthropic_stop(stop)
|
||||
params = {**self._default_params, **kwargs}
|
||||
response = self.client.completions.create(
|
||||
prompt=self._wrap_prompt(prompt),
|
||||
stop_sequences=stop,
|
||||
|
||||
# Remove parameters not supported by Messages API
|
||||
params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
|
||||
|
||||
response = self.client.messages.create(
|
||||
messages=self._format_messages(prompt),
|
||||
stop_sequences=stop if stop else None,
|
||||
**params,
|
||||
)
|
||||
return response.completion
|
||||
return response.content[0].text
|
||||
|
||||
def convert_prompt(self, prompt: PromptValue) -> str:
|
||||
return self._wrap_prompt(prompt.to_string())
|
||||
return prompt.to_string()
|
||||
|
||||
async def _acall(
|
||||
self,
|
||||
@ -304,12 +315,15 @@ class AnthropicLLM(LLM, _AnthropicCommon):
|
||||
stop = self._get_anthropic_stop(stop)
|
||||
params = {**self._default_params, **kwargs}
|
||||
|
||||
response = await self.async_client.completions.create(
|
||||
prompt=self._wrap_prompt(prompt),
|
||||
stop_sequences=stop,
|
||||
# Remove parameters not supported by Messages API
|
||||
params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
|
||||
|
||||
response = await self.async_client.messages.create(
|
||||
messages=self._format_messages(prompt),
|
||||
stop_sequences=stop if stop else None,
|
||||
**params,
|
||||
)
|
||||
return response.completion
|
||||
return response.content[0].text
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
@ -343,17 +357,20 @@ class AnthropicLLM(LLM, _AnthropicCommon):
|
||||
stop = self._get_anthropic_stop(stop)
|
||||
params = {**self._default_params, **kwargs}
|
||||
|
||||
for token in self.client.completions.create(
|
||||
prompt=self._wrap_prompt(prompt),
|
||||
stop_sequences=stop,
|
||||
stream=True,
|
||||
**params,
|
||||
):
|
||||
chunk = GenerationChunk(text=token.completion)
|
||||
# Remove parameters not supported by Messages API
|
||||
params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
|
||||
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
yield chunk
|
||||
with self.client.messages.stream(
|
||||
messages=self._format_messages(prompt),
|
||||
stop_sequences=stop if stop else None,
|
||||
**params,
|
||||
) as stream:
|
||||
for event in stream:
|
||||
if event.type == "content_block_delta" and hasattr(event.delta, "text"):
|
||||
chunk = GenerationChunk(text=event.delta.text)
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
yield chunk
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
@ -386,17 +403,20 @@ class AnthropicLLM(LLM, _AnthropicCommon):
|
||||
stop = self._get_anthropic_stop(stop)
|
||||
params = {**self._default_params, **kwargs}
|
||||
|
||||
async for token in await self.async_client.completions.create(
|
||||
prompt=self._wrap_prompt(prompt),
|
||||
stop_sequences=stop,
|
||||
stream=True,
|
||||
**params,
|
||||
):
|
||||
chunk = GenerationChunk(text=token.completion)
|
||||
# Remove parameters not supported by Messages API
|
||||
params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
|
||||
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
yield chunk
|
||||
async with self.async_client.messages.stream(
|
||||
messages=self._format_messages(prompt),
|
||||
stop_sequences=stop if stop else None,
|
||||
**params,
|
||||
) as stream:
|
||||
async for event in stream:
|
||||
if event.type == "content_block_delta" and hasattr(event.delta, "text"):
|
||||
chunk = GenerationChunk(text=event.delta.text)
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
yield chunk
|
||||
|
||||
def get_num_tokens(self, text: str) -> int:
|
||||
"""Calculate number of tokens."""
|
||||
|
@ -7,12 +7,12 @@ authors = []
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"anthropic<1,>=0.57.0",
|
||||
"langchain-core<1.0.0,>=0.3.68",
|
||||
"anthropic<1,>=0.60.0",
|
||||
"langchain-core<1.0.0,>=0.3.72",
|
||||
"pydantic<3.0.0,>=2.7.4",
|
||||
]
|
||||
name = "langchain-anthropic"
|
||||
version = "0.3.17"
|
||||
version = "0.3.18"
|
||||
description = "An integration package connecting AnthropicMessages and LangChain"
|
||||
readme = "README.md"
|
||||
|
||||
|
@ -31,8 +31,8 @@ from pydantic import BaseModel, Field
|
||||
from langchain_anthropic import ChatAnthropic, ChatAnthropicMessages
|
||||
from tests.unit_tests._utils import FakeCallbackHandler
|
||||
|
||||
MODEL_NAME = "claude-3-5-haiku-latest"
|
||||
IMAGE_MODEL_NAME = "claude-3-5-sonnet-latest"
|
||||
MODEL_NAME = "claude-3-5-haiku-20241022"
|
||||
IMAGE_MODEL_NAME = "claude-3-5-sonnet-20241022"
|
||||
|
||||
|
||||
def test_stream() -> None:
|
||||
@ -178,7 +178,7 @@ async def test_abatch_tags() -> None:
|
||||
|
||||
async def test_async_tool_use() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model=MODEL_NAME,
|
||||
model=MODEL_NAME, # type: ignore[call-arg]
|
||||
)
|
||||
|
||||
llm_with_tools = llm.bind_tools(
|
||||
@ -274,7 +274,7 @@ def test_system_invoke() -> None:
|
||||
|
||||
def test_anthropic_call() -> None:
|
||||
"""Test valid call to anthropic."""
|
||||
chat = ChatAnthropic(model=MODEL_NAME)
|
||||
chat = ChatAnthropic(model=MODEL_NAME) # type: ignore[call-arg]
|
||||
message = HumanMessage(content="Hello")
|
||||
response = chat.invoke([message])
|
||||
assert isinstance(response, AIMessage)
|
||||
@ -283,7 +283,7 @@ def test_anthropic_call() -> None:
|
||||
|
||||
def test_anthropic_generate() -> None:
|
||||
"""Test generate method of anthropic."""
|
||||
chat = ChatAnthropic(model=MODEL_NAME)
|
||||
chat = ChatAnthropic(model=MODEL_NAME) # type: ignore[call-arg]
|
||||
chat_messages: list[list[BaseMessage]] = [
|
||||
[HumanMessage(content="How many toes do dogs have?")],
|
||||
]
|
||||
@ -299,7 +299,7 @@ def test_anthropic_generate() -> None:
|
||||
|
||||
def test_anthropic_streaming() -> None:
|
||||
"""Test streaming tokens from anthropic."""
|
||||
chat = ChatAnthropic(model=MODEL_NAME)
|
||||
chat = ChatAnthropic(model=MODEL_NAME) # type: ignore[call-arg]
|
||||
message = HumanMessage(content="Hello")
|
||||
response = chat.stream([message])
|
||||
for token in response:
|
||||
@ -312,7 +312,7 @@ def test_anthropic_streaming_callback() -> None:
|
||||
callback_handler = FakeCallbackHandler()
|
||||
callback_manager = CallbackManager([callback_handler])
|
||||
chat = ChatAnthropic(
|
||||
model=MODEL_NAME,
|
||||
model=MODEL_NAME, # type: ignore[call-arg]
|
||||
callback_manager=callback_manager,
|
||||
verbose=True,
|
||||
)
|
||||
@ -328,7 +328,7 @@ async def test_anthropic_async_streaming_callback() -> None:
|
||||
callback_handler = FakeCallbackHandler()
|
||||
callback_manager = CallbackManager([callback_handler])
|
||||
chat = ChatAnthropic(
|
||||
model=MODEL_NAME,
|
||||
model=MODEL_NAME, # type: ignore[call-arg]
|
||||
callback_manager=callback_manager,
|
||||
verbose=True,
|
||||
)
|
||||
@ -343,7 +343,7 @@ async def test_anthropic_async_streaming_callback() -> None:
|
||||
|
||||
def test_anthropic_multimodal() -> None:
|
||||
"""Test that multimodal inputs are handled correctly."""
|
||||
chat = ChatAnthropic(model=IMAGE_MODEL_NAME)
|
||||
chat = ChatAnthropic(model=IMAGE_MODEL_NAME) # type: ignore[call-arg]
|
||||
messages: list[BaseMessage] = [
|
||||
HumanMessage(
|
||||
content=[
|
||||
@ -399,7 +399,7 @@ async def test_astreaming() -> None:
|
||||
|
||||
def test_tool_use() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-20250219",
|
||||
model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
|
||||
temperature=0,
|
||||
)
|
||||
tool_definition = {
|
||||
@ -424,7 +424,7 @@ def test_tool_use() -> None:
|
||||
|
||||
# Test streaming
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-20250219",
|
||||
model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
|
||||
temperature=0,
|
||||
# Add extra headers to also test token-efficient tools
|
||||
model_kwargs={
|
||||
@ -492,7 +492,7 @@ def test_tool_use() -> None:
|
||||
|
||||
|
||||
def test_builtin_tools() -> None:
|
||||
llm = ChatAnthropic(model="claude-3-7-sonnet-20250219")
|
||||
llm = ChatAnthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
|
||||
tool = {"type": "text_editor_20250124", "name": "str_replace_editor"}
|
||||
llm_with_tools = llm.bind_tools([tool])
|
||||
response = llm_with_tools.invoke(
|
||||
@ -510,7 +510,7 @@ class GenerateUsername(BaseModel):
|
||||
|
||||
|
||||
def test_disable_parallel_tool_calling() -> None:
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") # type: ignore[call-arg]
|
||||
llm_with_tools = llm.bind_tools([GenerateUsername], parallel_tool_calls=False)
|
||||
result = llm_with_tools.invoke(
|
||||
"Use the GenerateUsername tool to generate user names for:\n\n"
|
||||
@ -529,7 +529,7 @@ def test_anthropic_with_empty_text_block() -> None:
|
||||
"""Type the given letter."""
|
||||
return "OK"
|
||||
|
||||
model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0).bind_tools(
|
||||
model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0).bind_tools( # type: ignore[call-arg]
|
||||
[type_letter],
|
||||
)
|
||||
|
||||
@ -568,7 +568,7 @@ def test_anthropic_with_empty_text_block() -> None:
|
||||
|
||||
def test_with_structured_output() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-opus-20240229",
|
||||
model="claude-3-opus-20240229", # type: ignore[call-arg]
|
||||
)
|
||||
|
||||
structured_llm = llm.with_structured_output(
|
||||
@ -587,7 +587,7 @@ def test_with_structured_output() -> None:
|
||||
|
||||
|
||||
def test_get_num_tokens_from_messages() -> None:
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") # type: ignore[call-arg]
|
||||
|
||||
# Test simple case
|
||||
messages = [
|
||||
@ -650,7 +650,7 @@ class GetWeather(BaseModel):
|
||||
@pytest.mark.parametrize("tool_choice", ["GetWeather", "auto", "any"])
|
||||
def test_anthropic_bind_tools_tool_choice(tool_choice: str) -> None:
|
||||
chat_model = ChatAnthropic(
|
||||
model=MODEL_NAME,
|
||||
model=MODEL_NAME, # type: ignore[call-arg]
|
||||
)
|
||||
chat_model_with_tools = chat_model.bind_tools([GetWeather], tool_choice=tool_choice)
|
||||
response = chat_model_with_tools.invoke("what's the weather in ny and la")
|
||||
@ -661,7 +661,7 @@ def test_pdf_document_input() -> None:
|
||||
url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
|
||||
data = b64encode(requests.get(url, timeout=10).content).decode()
|
||||
|
||||
result = ChatAnthropic(model=IMAGE_MODEL_NAME).invoke(
|
||||
result = ChatAnthropic(model=IMAGE_MODEL_NAME).invoke( # type: ignore[call-arg]
|
||||
[
|
||||
HumanMessage(
|
||||
[
|
||||
@ -684,7 +684,7 @@ def test_pdf_document_input() -> None:
|
||||
|
||||
|
||||
def test_citations() -> None:
|
||||
llm = ChatAnthropic(model="claude-3-5-haiku-latest")
|
||||
llm = ChatAnthropic(model="claude-3-5-haiku-latest") # type: ignore[call-arg]
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
@ -729,8 +729,8 @@ def test_citations() -> None:
|
||||
@pytest.mark.vcr
|
||||
def test_thinking() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-latest",
|
||||
max_tokens=5_000,
|
||||
model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
|
||||
max_tokens=5_000, # type: ignore[call-arg]
|
||||
thinking={"type": "enabled", "budget_tokens": 2_000},
|
||||
)
|
||||
|
||||
@ -766,8 +766,8 @@ def test_thinking() -> None:
|
||||
@pytest.mark.vcr
|
||||
def test_redacted_thinking() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-latest",
|
||||
max_tokens=5_000,
|
||||
model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
|
||||
max_tokens=5_000, # type: ignore[call-arg]
|
||||
thinking={"type": "enabled", "budget_tokens": 2_000},
|
||||
)
|
||||
query = "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB" # noqa: E501
|
||||
@ -805,8 +805,8 @@ def test_redacted_thinking() -> None:
|
||||
|
||||
def test_structured_output_thinking_enabled() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-latest",
|
||||
max_tokens=5_000,
|
||||
model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
|
||||
max_tokens=5_000, # type: ignore[call-arg]
|
||||
thinking={"type": "enabled", "budget_tokens": 2_000},
|
||||
)
|
||||
with pytest.warns(match="structured output"):
|
||||
@ -828,8 +828,8 @@ def test_structured_output_thinking_force_tool_use() -> None:
|
||||
# when `thinking` is enabled. When this test fails, it means that the feature
|
||||
# is supported and the workarounds in `with_structured_output` should be removed.
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-7-sonnet-latest",
|
||||
max_tokens=5_000,
|
||||
model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
|
||||
max_tokens=5_000, # type: ignore[call-arg]
|
||||
thinking={"type": "enabled", "budget_tokens": 2_000},
|
||||
).bind_tools(
|
||||
[GenerateUsername],
|
||||
@ -896,13 +896,13 @@ def test_image_tool_calling() -> None:
|
||||
],
|
||||
),
|
||||
]
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-latest")
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-latest") # type: ignore[call-arg]
|
||||
llm.bind_tools([color_picker]).invoke(messages)
|
||||
|
||||
|
||||
@pytest.mark.vcr
|
||||
def test_web_search() -> None:
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-latest")
|
||||
llm = ChatAnthropic(model="claude-3-5-sonnet-latest") # type: ignore[call-arg]
|
||||
|
||||
tool = {"type": "web_search_20250305", "name": "web_search", "max_uses": 1}
|
||||
llm_with_tools = llm.bind_tools([tool])
|
||||
@ -944,9 +944,9 @@ def test_web_search() -> None:
|
||||
@pytest.mark.vcr
|
||||
def test_code_execution() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-sonnet-4-20250514",
|
||||
model="claude-sonnet-4-20250514", # type: ignore[call-arg]
|
||||
betas=["code-execution-2025-05-22"],
|
||||
max_tokens=10_000,
|
||||
max_tokens=10_000, # type: ignore[call-arg]
|
||||
)
|
||||
|
||||
tool = {"type": "code_execution_20250522", "name": "code_execution"}
|
||||
@ -1002,10 +1002,10 @@ def test_remote_mcp() -> None:
|
||||
]
|
||||
|
||||
llm = ChatAnthropic(
|
||||
model="claude-sonnet-4-20250514",
|
||||
model="claude-sonnet-4-20250514", # type: ignore[call-arg]
|
||||
betas=["mcp-client-2025-04-04"],
|
||||
mcp_servers=mcp_servers,
|
||||
max_tokens=10_000,
|
||||
max_tokens=10_000, # type: ignore[call-arg]
|
||||
)
|
||||
|
||||
input_message = {
|
||||
@ -1052,7 +1052,7 @@ def test_files_api_image(block_format: str) -> None:
|
||||
if not image_file_id:
|
||||
pytest.skip()
|
||||
llm = ChatAnthropic(
|
||||
model="claude-sonnet-4-20250514",
|
||||
model="claude-sonnet-4-20250514", # type: ignore[call-arg]
|
||||
betas=["files-api-2025-04-14"],
|
||||
)
|
||||
if block_format == "anthropic":
|
||||
@ -1086,7 +1086,7 @@ def test_files_api_pdf(block_format: str) -> None:
|
||||
if not pdf_file_id:
|
||||
pytest.skip()
|
||||
llm = ChatAnthropic(
|
||||
model="claude-sonnet-4-20250514",
|
||||
model="claude-sonnet-4-20250514", # type: ignore[call-arg]
|
||||
betas=["files-api-2025-04-14"],
|
||||
)
|
||||
if block_format == "anthropic":
|
||||
@ -1111,7 +1111,7 @@ def test_files_api_pdf(block_format: str) -> None:
|
||||
def test_search_result_tool_message() -> None:
|
||||
"""Test that we can pass a search result tool message to the model."""
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-5-haiku-latest",
|
||||
model="claude-3-5-haiku-latest", # type: ignore[call-arg]
|
||||
betas=["search-results-2025-06-09"],
|
||||
)
|
||||
|
||||
@ -1164,7 +1164,7 @@ def test_search_result_tool_message() -> None:
|
||||
|
||||
def test_search_result_top_level() -> None:
|
||||
llm = ChatAnthropic(
|
||||
model="claude-3-5-haiku-latest",
|
||||
model="claude-3-5-haiku-latest", # type: ignore[call-arg]
|
||||
betas=["search-results-2025-06-09"],
|
||||
)
|
||||
input_message = HumanMessage(
|
||||
@ -1209,6 +1209,6 @@ def test_search_result_top_level() -> None:
|
||||
|
||||
|
||||
def test_async_shared_client() -> None:
|
||||
llm = ChatAnthropic(model="claude-3-5-haiku-latest")
|
||||
llm = ChatAnthropic(model="claude-3-5-haiku-latest") # type: ignore[call-arg]
|
||||
_ = asyncio.run(llm.ainvoke("Hello"))
|
||||
_ = asyncio.run(llm.ainvoke("Hello"))
|
||||
|
@ -24,14 +24,14 @@ def test_anthropic_model_param() -> None:
|
||||
|
||||
def test_anthropic_call() -> None:
|
||||
"""Test valid call to anthropic."""
|
||||
llm = Anthropic(model="claude-2.1") # type: ignore[call-arg]
|
||||
llm = Anthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
|
||||
output = llm.invoke("Say foo:")
|
||||
assert isinstance(output, str)
|
||||
|
||||
|
||||
def test_anthropic_streaming() -> None:
|
||||
"""Test streaming tokens from anthropic."""
|
||||
llm = Anthropic(model="claude-2.1") # type: ignore[call-arg]
|
||||
llm = Anthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
|
||||
generator = llm.stream("I'm Pickle Rick")
|
||||
|
||||
assert isinstance(generator, Generator)
|
||||
@ -45,6 +45,7 @@ def test_anthropic_streaming_callback() -> None:
|
||||
callback_handler = FakeCallbackHandler()
|
||||
callback_manager = CallbackManager([callback_handler])
|
||||
llm = Anthropic(
|
||||
model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
|
||||
streaming=True,
|
||||
callback_manager=callback_manager,
|
||||
verbose=True,
|
||||
@ -55,7 +56,7 @@ def test_anthropic_streaming_callback() -> None:
|
||||
|
||||
async def test_anthropic_async_generate() -> None:
|
||||
"""Test async generate."""
|
||||
llm = Anthropic()
|
||||
llm = Anthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
|
||||
output = await llm.agenerate(["How many toes do dogs have?"])
|
||||
assert isinstance(output, LLMResult)
|
||||
|
||||
@ -65,6 +66,7 @@ async def test_anthropic_async_streaming_callback() -> None:
|
||||
callback_handler = FakeCallbackHandler()
|
||||
callback_manager = CallbackManager([callback_handler])
|
||||
llm = Anthropic(
|
||||
model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
|
||||
streaming=True,
|
||||
callback_manager=callback_manager,
|
||||
verbose=True,
|
||||
|
@ -23,7 +23,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "anthropic"
|
||||
version = "0.57.0"
|
||||
version = "0.60.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@ -34,9 +34,9 @@ dependencies = [
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7f/58/21e725231c2d36025384a4764e528009a93728039d81a295d28c1e396448/anthropic-0.57.0.tar.gz", hash = "sha256:2073ed4c8b75bb25dff53da0a3336232cd54f475392ea47150d0ccfa634bdc42", size = 423683, upload-time = "2025-07-03T16:25:40.926Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4e/03/3334921dc54ed822b3dd993ae72d823a7402588521bbba3e024b3333a1fd/anthropic-0.60.0.tar.gz", hash = "sha256:a22ba187c6f4fd5afecb2fc913b960feccf72bc0d25c1b7ce0345e87caede577", size = 425983, upload-time = "2025-07-28T19:53:47.685Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/1f/00f8b30c6d3ad02dc37423c3cb0344289a566c38fb0322c8532751e43ecf/anthropic-0.57.0-py3-none-any.whl", hash = "sha256:18672e4d1f7734df58371ff19b796e630faf0732ce64167b55d4a455de75bbf6", size = 292827, upload-time = "2025-07-03T16:25:39.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/bb/d84f287fb1c217b30c328af987cf8bbe3897edf0518dcc5fa39412f794ec/anthropic-0.60.0-py3-none-any.whl", hash = "sha256:65ad1f088a960217aaf82ba91ff743d6c89e9d811c6d64275b9a7c59ee9ac3c6", size = 293116, upload-time = "2025-07-28T19:53:45.944Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -424,7 +424,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-anthropic"
|
||||
version = "0.3.17"
|
||||
version = "0.3.18"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "anthropic" },
|
||||
@ -469,7 +469,7 @@ typing = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "anthropic", specifier = ">=0.57.0,<1" },
|
||||
{ name = "anthropic", specifier = ">=0.60.0,<1" },
|
||||
{ name = "langchain-core", editable = "../../core" },
|
||||
{ name = "pydantic", specifier = ">=2.7.4,<3.0.0" },
|
||||
]
|
||||
@ -505,7 +505,7 @@ typing = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.3.68"
|
||||
version = "0.3.72"
|
||||
source = { editable = "../../core" }
|
||||
dependencies = [
|
||||
{ name = "jsonpatch" },
|
||||
@ -521,7 +521,7 @@ dependencies = [
|
||||
requires-dist = [
|
||||
{ name = "jsonpatch", specifier = ">=1.33,<2.0" },
|
||||
{ name = "langsmith", specifier = ">=0.3.45" },
|
||||
{ name = "packaging", specifier = ">=23.2,<25" },
|
||||
{ name = "packaging", specifier = ">=23.2" },
|
||||
{ name = "pydantic", specifier = ">=2.7.4" },
|
||||
{ name = "pyyaml", specifier = ">=5.3" },
|
||||
{ name = "tenacity", specifier = ">=8.1.0,!=8.4.0,<10.0.0" },
|
||||
|
@ -3,11 +3,13 @@ python scripts/release_branch.py anthropic bagatur
|
||||
"""
|
||||
|
||||
import glob
|
||||
import tomllib
|
||||
import toml
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Ignoring errors since this script is run in a controlled environment
|
||||
import toml # type: ignore # pyright: ignore[reportMissingModuleSource]
|
||||
import tomllib # type: ignore # pyright: ignore[reportMissingImports]
|
||||
|
||||
|
||||
def main(*args):
|
||||
pkg = args[1]
|
||||
|
@ -1,12 +1,13 @@
|
||||
"""python scripts/update_mypy_ruff.py"""
|
||||
|
||||
import glob
|
||||
import tomllib
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import toml
|
||||
import subprocess
|
||||
import re
|
||||
# Ignoring errors since this script is run in a controlled environment
|
||||
import toml # type: ignore # pyright: ignore[reportMissingModuleSource]
|
||||
import tomllib # type: ignore # pyright: ignore[reportMissingImports]
|
||||
|
||||
ROOT_DIR = Path(__file__).parents[1]
|
||||
|
||||
@ -50,10 +51,9 @@ def main():
|
||||
|
||||
to_ignore = {}
|
||||
for l in logs:
|
||||
if re.match("^(.*)\:(\d+)\: error:.*\[(.*)\]", l):
|
||||
path, line_no, error_type = re.match(
|
||||
"^(.*)\:(\d+)\: error:.*\[(.*)\]", l
|
||||
).groups()
|
||||
match = re.match(r"^(.*):(\d+): error:.*\[(.*)\]", l)
|
||||
if match:
|
||||
path, line_no, error_type = match.groups()
|
||||
if (path, line_no) in to_ignore:
|
||||
to_ignore[(path, line_no)].append(error_type)
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user