Merge branch 'langchain-ai:master' into master

This commit is contained in:
Dhiru Pandey 2025-07-28 16:03:44 -07:00 committed by GitHub
commit 0bf2dba44a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 707 additions and 524 deletions

View File

@ -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()

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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 * *"

View File

@ -1,5 +1,5 @@
name: '📝 Run Documentation Notebooks'
name: '📓 Validate Documentation Notebooks'
run-name: 'Test notebooks in ${{ inputs.working-directory }}'
on:
workflow_dispatch:
inputs:

View File

@ -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 \

View File

@ -15,7 +15,7 @@
[![GitHub star chart](https://img.shields.io/github/stars/langchain-ai/langchain?style=flat-square)](https://star-history.com/#langchain-ai/langchain)
[![Open Issues](https://img.shields.io/github/issues-raw/langchain-ai/langchain?style=flat-square)](https://github.com/langchain-ai/langchain/issues)
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode&style=flat-square)](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)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/langchainai.svg?style=social&label=Follow%20%40LangChainAI)](https://twitter.com/langchainai)
[![CodSpeed Badge](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/langchain-ai/langchain)

View File

@ -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:

View File

@ -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",

View File

@ -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",

View File

@ -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"
}

View File

@ -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)."
]
}
],

View File

@ -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?\")"
]

View File

@ -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)."
]

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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>
);
}
}

View File

@ -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>
);
}

View File

@ -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:

View File

@ -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:

View File

@ -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,

View File

@ -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"}

View File

@ -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")

View File

@ -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")

View File

@ -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."""

View File

@ -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"

View File

@ -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"))

View File

@ -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,

View File

@ -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" },

View File

@ -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]

View File

@ -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: