Giter VIP home page Giter VIP logo

langchain-aws's Introduction

๐Ÿฆœ๏ธ๐Ÿ”— LangChain ๐Ÿค Amazon Web Services (AWS)

This repository provides LangChain components for various AWS services. It aims to replace and expand upon the existing LangChain AWS components found in the langchain-community package in the LangChain repository.

Features

  • LLMs: Includes LLM classes for AWS services like Bedrock and SageMaker Endpoints, allowing you to leverage their language models within LangChain.
  • Retrievers: Supports retrievers for services like Amazon Kendra and KnowledgeBases for Amazon Bedrock, enabling efficient retrieval of relevant information in your RAG applications.
  • Graphs: Provides components for working with AWS Neptune graphs within LangChain.
  • More to come: This repository will continue to expand and offer additional components for various AWS services as development progresses.

Note: This repository will replace all AWS integrations currently present in the langchain-community package. Users are encouraged to migrate to this repository as soon as possible.

Installation

You can install the langchain-aws package from PyPI.

pip install langchain-aws

Usage

Here's a simple example of how to use the langchain-aws package.

from langchain_aws import BedrockLLM

# Initialize the Bedrock LLM
llm = BedrockLLM(
    model_id="anthropic.claude-v2:1"
)

# Invoke the llm
response = llm.invoke("Hello! How are you today?")
print(response)

For more detailed usage examples and documentation, please refer to the LangChain docs.

Contributing

We welcome contributions to this project! Please follow the contribution guide for instructions to setup the project for development and guidance on how to contribute effectively.

License

This project is licensed under the MIT License.

langchain-aws's People

Contributors

3coins avatar baskaryan avatar bigbernnn avatar brnaba-aws avatar ccurme avatar efriis avatar fedor-intercom avatar ihmaws avatar joehu21 avatar laithalsaadoon avatar lakshmiperi23 avatar naptlme avatar ravediamond avatar rozerarenu avatar thiagotps avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

langchain-aws's Issues

Exposing more boto3 client options to ChatBedrock and doc example?

With the addition of the opus model support on Bedrock, which generally tends to always hit the default timeout options, would it be okay to add the different retry/timeout options from boto3 as common options to ChatBedrock?

Currently we have support for a few common keys here: https://github.com/langchain-ai/langchain-aws/blob/main/libs/aws/langchain_aws/llms/bedrock.py#L300.

However just changing from Claude 2/2.1 or Claude 3 Sonnet to Opus increases the runtime mostly by 10x (mine are consistently around 20-35 seconds on old models to 200+ seconds with Opus)..
UPDATE:
Later it also turns out many of the timeouts reported by boto3 was actually general throttling (as seen in bedrock logs), however there was no logs or errors indicating any throttling happening, just reported as a timeout.
Changing the timeout settings + upping the max_attempts was necessary to even get it to retry properly it seems.

I would assume that once more people start using this new library, and depending on the popularity of Opus, a few changes are recommended:

  1. Adding a higher default timeout, to prevent common issues to appear.
  2. Adding more boto3 client options available as arguments to ChatBedrock/BedrockBase, so that developers can instantly see them as a supported list of arguments when developing using this chat model.
  3. A small documentation example that links to the available boto3 options that would work with Bedrock, and a small example on how to create a custom Botocore.config.Config() object that can be passed to the existing config argument would be greatly appreciated. I did get it working through trial and error, but only from boto3 examples directly and not from langchain issues or docs.

Adding amazon-bedrock-guardrailConfig for Guardrails to support different streaming types

Guardrails supports Asynchronous and Synchronous streaming that determine how guardrails are applied to the model response. This feature is currently not supported by the Bedrock classes. Feature is highlighted here in the Bedrock documentation and requires amazon-bedrock-guardrailConfig to be passed in the API call while using streaming and using InvokeModelWithResponseStream.

boto3 supports this in the invoke_model_with_response_stream method as a part of the body.

Streaming tool calling with ChatBedrockConverse throws an error with some models

Screenshot 2024-08-03 at 17 13 13
Hi,

I have a LangGraph agent which is using an LLM via langchain_aws.ChatBedrockConverse. When I run my project with LangGraph Studio and submit a HUMAN message I receive the error below. However, when I use, for example, ChatBedrock instead of ChatBedrockConverse, the issue does not reproduce.

I installed LangGraph Studio for the first time on Friday and really loved it. I would appreciate it if you could help me resolve this issue. Is it a configuration issue on my side?

Below is the error message I am receiving:

langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_core/runnables/base.py", line 5060, in invoke
langgraph-api-1 | return self.bound.invoke(
langgraph-api-1 | ^^^^^^^^^^^^^^^^^^
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 274, in invoke
langgraph-api-1 | self.generate_prompt(
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 714, in generate_prompt
langgraph-api-1 | return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs)
langgraph-api-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 571, in generate
langgraph-api-1 | raise e
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 561, in generate
langgraph-api-1 | self._generate_with_cache(
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 781, in _generate_with_cache
langgraph-api-1 | for chunk in self._stream(messages, stop=stop, **kwargs):
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/langchain_aws/chat_models/bedrock_converse.py", line 427, in _stream
langgraph-api-1 | response = self.client.converse_stream(
langgraph-api-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/botocore/client.py", line 565, in _api_call
langgraph-api-1 | return self._make_api_call(operation_name, kwargs)
langgraph-api-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
langgraph-api-1 | File "/usr/local/lib/python3.12/site-packages/botocore/client.py", line 1017, in _make_api_call
langgraph-api-1 | raise error_class(parsed_response, operation_name)
langgraph-api-1 | botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: This model doesn't support tool use in streaming mode.
langgraph-api-1 | 2024-08-03T14:01:03.326001Z [info ] 192.168.65.1:52399 - "POST /threads/2b6d9e7d-83f9-48e4-ae81-5f074a05a9fa/history HTTP/1.1" 200 [uvicorn.access] filename=httptools_impl.py func_name=send lineno=466
langgraph-api-1 | 2024-08-03T14:01:03.355357Z [info ] 192.168.65.1:52399 - "GET /assistants/f69d7c70-ea79-56d5-9773-67796ece2bec/schemas HTTP/1.1" 200 [uvicorn.access] filename=httptools_impl.py func_name=send lineno=466

My current LangGraph Studio version is 0.0.11.
Thank you for your assistance.

Best Regards,
Daniela

Implementation of BlobLoader with S3

An implementation of BlobLoader with S3 would be fantastic. It'll allow users to easily hook their s3 data to langchain parsers and indexing code.

Here's an explanation about the abstractions:
https://python.langchain.com/docs/modules/data_connection/document_loaders/custom/#blob-loaders

The minimal feature set for this to be useful:

  1. filters by keys matching a given prefix
  2. filter for last_modified_date
  3. filter for max file size
  4. filter for file extension
  5. filter for min file size

Additional features:
7. if available filter for file mimetype

ChatBedrock calls incorrect tool in a multi-agent system

When using ChatBedrock in the context of a collaborative multi-agent system and a proposed fix to the tool_calls attribute being set, the stated node and expected tool call do not align. The entry node is apparently honored but the attempted function call is incorrect. As seen in the output below, the "Researcher" node is apparently calling the "python_repl" tool which should not be the case, "tavily_tool" is the expected function name.

The fact this is the case for two different ways of attempting to handle tool calling leads me to believe there is a separate issue here.

bigbernnn#1
#50

[0:tasks] Starting step 0 with 1 task:
- __start__ -> {'messages': [HumanMessage(content="Fetch the UK's GDP over the past 5 years, then draw a line graph of it. Once you code it up, finish.")]}
[0:writes] Finished step 0 with writes to 1 channel:
- messages -> [HumanMessage(content="Fetch the UK's GDP over the past 5 years, then draw a line graph of it. Once you code it up, finish.")]
[1:tasks] Starting step 1 with 1 task:
- Researcher -> {'messages': [HumanMessage(content="Fetch the UK's GDP over the past 5 years, then draw a line graph of it. Once you code it up, finish.")],
 'sender': None}
{'Researcher': {'messages': [AIMessage(content='Okay, let\'s get started on fetching the UK\'s GDP data for the past 5 years and drawing a line graph.\n\n<function_calls>\n<invoke>\n<tool_name>python_repl</tool_name>\n<parameters>\n<code>\nimport requests\nimport json\nfrom datetime import datetime, timedelta\n\n# Function to fetch GDP data for the UK from the past 5 years\ndef fetch_uk_gdp_data():\n    url = "https://ec.europa.eu/eurostat/wdds/rest/data/v2.1/json/en/naida_10_gdp"\n    params = {\n        "na_item": "B1GQ",\n        "s_adj": "SNA_SAM",\n        "precision": "1",\n        "unit": "CP_MEUR",\n        "geo": "UK",\n        "startPeriod": (datetime.now() - timedelta(days=365*5)).strftime("%Y"),\n        "endPeriod": datetime.now().year\n    }\n    response = requests.get(url, params=params)\n    data = json.loads(response.text)\n    \n    gdp_data = []\n    for value in data["value"]["orderedValue"]:\n        year = int(value["period"])\n        gdp = float(value["value"])\n        gdp_data.append((year, gdp))\n        \n    return gdp_data\n\n# Fetch and print the GDP data\nuk_gdp_data = fetch_uk_gdp_data()\nprint("UK GDP data for the past 5 years:")\nfor year, gdp in uk_gdp_data:\n    print(f"{year}: {gdp:.2f} billion Euros")\n</code>\n</parameters>\n</invoke>\n</function_calls>\n\nThe code above fetches the UK\'s GDP data for the past 5 years from the Eurostat API and prints it out.\n\nTo draw a line graph, we can use a plotting library like matplotlib:\n\n<function_calls>\n<invoke>\n<tool_name>python_repl</tool_name>\n<parameters>\n<code>\nimport matplotlib.pyplot as plt\n\n# Fetch GDP data if not already fetched\nif \'uk_gdp_data\' not in locals():\n    uk_gdp_data = fetch_uk_gdp_data()\n\n# Extract years and GDP values from data\nyears, gdp_values = zip(*uk_gdp_data)\n\n# Create line plot\nplt.figure(figsize=(8, 6))\nplt.plot(years, gdp_values)\nplt.title("UK GDP for the Past 5 Years")\nplt.xlabel("Year")\nplt.ylabel("GDP (billion Euros)")\nplt.xticks(years)\nplt.show()\n</code>\n</parameters>\n</invoke>\n</function_calls>\n\nThis code will generate a line graph showing the UK\'s GDP over the past 5 years, with the years on the x-axis and the GDP values on the y-axis.\n\nFINAL ANSWER: The code above fetches the UK\'s GDP data for the past 5 years from the Eurostat API, prints out the data, and generates a line graph visualizing the GDP values over time using matplotlib.', additional_kwargs={'usage': {'prompt_tokens': 381, 'completion_tokens': 775, 'total_tokens': 1156}, 'stop_reason': 'tool_use', 'tool_calls': [{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}], 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, response_metadata={'usage': {'prompt_tokens': 381, 'completion_tokens': 775, 'total_tokens': 1156}, 'stop_reason': 'tool_use', 'tool_calls': [{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}], 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, name='Researcher', id='run-6b95de2b-a61e-4bfe-a5f1-7bc11405fe02-0', tool_calls=[{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}])], 'sender': 'Researcher'}}
----
[1:writes] Finished step 1 with writes to 2 channels:
- messages -> [AIMessage(content='Okay, let\'s get started on fetching the UK\'s GDP data for the past 5 years and drawing a line graph.\n\n<function_calls>\n<invoke>\n<tool_name>python_repl</tool_name>\n<parameters>\n<code>\nimport requests\nimport json\nfrom datetime import datetime, timedelta\n\n# Function to fetch GDP data for the UK from the past 5 years\ndef fetch_uk_gdp_data():\n    url = "https://ec.europa.eu/eurostat/wdds/rest/data/v2.1/json/en/naida_10_gdp"\n    params = {\n        "na_item": "B1GQ",\n        "s_adj": "SNA_SAM",\n        "precision": "1",\n        "unit": "CP_MEUR",\n        "geo": "UK",\n        "startPeriod": (datetime.now() - timedelta(days=365*5)).strftime("%Y"),\n        "endPeriod": datetime.now().year\n    }\n    response = requests.get(url, params=params)\n    data = json.loads(response.text)\n    \n    gdp_data = []\n    for value in data["value"]["orderedValue"]:\n        year = int(value["period"])\n        gdp = float(value["value"])\n        gdp_data.append((year, gdp))\n        \n    return gdp_data\n\n# Fetch and print the GDP data\nuk_gdp_data = fetch_uk_gdp_data()\nprint("UK GDP data for the past 5 years:")\nfor year, gdp in uk_gdp_data:\n    print(f"{year}: {gdp:.2f} billion Euros")\n</code>\n</parameters>\n</invoke>\n</function_calls>\n\nThe code above fetches the UK\'s GDP data for the past 5 years from the Eurostat API and prints it out.\n\nTo draw a line graph, we can use a plotting library like matplotlib:\n\n<function_calls>\n<invoke>\n<tool_name>python_repl</tool_name>\n<parameters>\n<code>\nimport matplotlib.pyplot as plt\n\n# Fetch GDP data if not already fetched\nif \'uk_gdp_data\' not in locals():\n    uk_gdp_data = fetch_uk_gdp_data()\n\n# Extract years and GDP values from data\nyears, gdp_values = zip(*uk_gdp_data)\n\n# Create line plot\nplt.figure(figsize=(8, 6))\nplt.plot(years, gdp_values)\nplt.title("UK GDP for the Past 5 Years")\nplt.xlabel("Year")\nplt.ylabel("GDP (billion Euros)")\nplt.xticks(years)\nplt.show()\n</code>\n</parameters>\n</invoke>\n</function_calls>\n\nThis code will generate a line graph showing the UK\'s GDP over the past 5 years, with the years on the x-axis and the GDP values on the y-axis.\n\nFINAL ANSWER: The code above fetches the UK\'s GDP data for the past 5 years from the Eurostat API, prints out the data, and generates a line graph visualizing the GDP values over time using matplotlib.', additional_kwargs={'usage': {'prompt_tokens': 381, 'completion_tokens': 775, 'total_tokens': 1156}, 'stop_reason': 'tool_use', 'tool_calls': [{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}], 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, response_metadata={'usage': {'prompt_tokens': 381, 'completion_tokens': 775, 'total_tokens': 1156}, 'stop_reason': 'tool_use', 'tool_calls': [{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}], 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, name='Researcher', id='run-6b95de2b-a61e-4bfe-a5f1-7bc11405fe02-0', tool_calls=[{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}])]
- sender -> 'Researcher'
[2:tasks] Starting step 2 with 1 task:
- call_tool -> {'messages': [HumanMessage(content="Fetch the UK's GDP over the past 5 years, then draw a line graph of it. Once you code it up, finish."),
              AIMessage(content='Okay, let\'s get started on fetching the UK\'s GDP data for the past 5 years and drawing a line graph.\n\n<function_calls>\n<invoke>\n<tool_name>python_repl</tool_name>\n<parameters>\n<code>\nimport requests\nimport json\nfrom datetime import datetime, timedelta\n\n# Function to fetch GDP data for the UK from the past 5 years\ndef fetch_uk_gdp_data():\n    url = "https://ec.europa.eu/eurostat/wdds/rest/data/v2.1/json/en/naida_10_gdp"\n    params = {\n        "na_item": "B1GQ",\n        "s_adj": "SNA_SAM",\n        "precision": "1",\n        "unit": "CP_MEUR",\n        "geo": "UK",\n        "startPeriod": (datetime.now() - timedelta(days=365*5)).strftime("%Y"),\n        "endPeriod": datetime.now().year\n    }\n    response = requests.get(url, params=params)\n    data = json.loads(response.text)\n    \n    gdp_data = []\n    for value in data["value"]["orderedValue"]:\n        year = int(value["period"])\n        gdp = float(value["value"])\n        gdp_data.append((year, gdp))\n        \n    return gdp_data\n\n# Fetch and print the GDP data\nuk_gdp_data = fetch_uk_gdp_data()\nprint("UK GDP data for the past 5 years:")\nfor year, gdp in uk_gdp_data:\n    print(f"{year}: {gdp:.2f} billion Euros")\n</code>\n</parameters>\n</invoke>\n</function_calls>\n\nThe code above fetches the UK\'s GDP data for the past 5 years from the Eurostat API and prints it out.\n\nTo draw a line graph, we can use a plotting library like matplotlib:\n\n<function_calls>\n<invoke>\n<tool_name>python_repl</tool_name>\n<parameters>\n<code>\nimport matplotlib.pyplot as plt\n\n# Fetch GDP data if not already fetched\nif \'uk_gdp_data\' not in locals():\n    uk_gdp_data = fetch_uk_gdp_data()\n\n# Extract years and GDP values from data\nyears, gdp_values = zip(*uk_gdp_data)\n\n# Create line plot\nplt.figure(figsize=(8, 6))\nplt.plot(years, gdp_values)\nplt.title("UK GDP for the Past 5 Years")\nplt.xlabel("Year")\nplt.ylabel("GDP (billion Euros)")\nplt.xticks(years)\nplt.show()\n</code>\n</parameters>\n</invoke>\n</function_calls>\n\nThis code will generate a line graph showing the UK\'s GDP over the past 5 years, with the years on the x-axis and the GDP values on the y-axis.\n\nFINAL ANSWER: The code above fetches the UK\'s GDP data for the past 5 years from the Eurostat API, prints out the data, and generates a line graph visualizing the GDP values over time using matplotlib.', additional_kwargs={'usage': {'prompt_tokens': 381, 'completion_tokens': 775, 'total_tokens': 1156}, 'stop_reason': 'tool_use', 'tool_calls': [{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}], 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, response_metadata={'usage': {'prompt_tokens': 381, 'completion_tokens': 775, 'total_tokens': 1156}, 'stop_reason': 'tool_use', 'tool_calls': [{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}], 'model_id': 'anthropic.claude-3-sonnet-20240229-v1:0'}, name='Researcher', id='run-6b95de2b-a61e-4bfe-a5f1-7bc11405fe02-0', tool_calls=[{'name': 'python_repl', 'args': {}, 'id': 'call_MEMWS5G7T1iOyTFHj5S6gg'}, {'name': 'python_repl', 'args': {}, 'id': 'call_jevlNPPIT_2oxzHFx8U_oA'}])],
 'sender': 'Researcher'}

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[22], line 14
      1 events = graph.stream(
      2     {
      3         "messages": [
   (...)
     12     {"recursion_limit": 150},
     13 )
---> 14 for s in events:
     15     print(s)
     16     print("----")

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langgraph/pregel/__init__.py:963, in Pregel.stream(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug)
    960         del fut, task
    962 # panic on failure or timeout
--> 963 _panic_or_proceed(done, inflight, step)
    964 # don't keep futures around in memory longer than needed
    965 del done, inflight, futures

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1489, in _panic_or_proceed(done, inflight, step)
   1487             inflight.pop().cancel()
   1488         # raise the exception
-> 1489         raise exc
   1491 if inflight:
   1492     # if we got here means we timed out
   1493     while inflight:
   1494         # cancel all pending tasks

File [/usr/lib/python3.10/concurrent/futures/thread.py:58](http://localhost:8888/usr/lib/python3.10/concurrent/futures/thread.py#line=57), in _WorkItem.run(self)
     55     return
     57 try:
---> 58     result = self.fn(*self.args, **self.kwargs)
     59 except BaseException as exc:
     60     self.future.set_exception(exc)

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langgraph/pregel/retry.py:66, in run_with_retry(task, retry_policy)
     64 task.writes.clear()
     65 # run the task
---> 66 task.proc.invoke(task.input, task.config)
     67 # if successful, end
     68 break

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langchain_core/runnables/base.py:2493, in RunnableSequence.invoke(self, input, config, **kwargs)
   2489 config = patch_config(
   2490     config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
   2491 )
   2492 if i == 0:
-> 2493     input = step.invoke(input, config, **kwargs)
   2494 else:
   2495     input = step.invoke(input, config)

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langgraph/utils.py:95, in RunnableCallable.invoke(self, input, config, **kwargs)
     93     if accepts_config(self.func):
     94         kwargs["config"] = config
---> 95     ret = context.run(self.func, input, **kwargs)
     96 if isinstance(ret, Runnable) and self.recurse:
     97     return ret.invoke(input, config)

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langgraph/prebuilt/tool_node.py:68, in ToolNode._func(self, input, config)
     63     return ToolMessage(
     64         content=str_output(output), name=call["name"], tool_call_id=call["id"]
     65     )
     67 with get_executor_for_config(config) as executor:
---> 68     outputs = [*executor.map(run_one, message.tool_calls)]
     69     if output_type == "list":
     70         return outputs

File [/usr/lib/python3.10/concurrent/futures/_base.py:621](http://localhost:8888/usr/lib/python3.10/concurrent/futures/_base.py#line=620), in Executor.map.<locals>.result_iterator()
    618 while fs:
    619     # Careful not to keep a reference to the popped future
    620     if timeout is None:
--> 621         yield _result_or_cancel(fs.pop())
    622     else:
    623         yield _result_or_cancel(fs.pop(), end_time - time.monotonic())

File [/usr/lib/python3.10/concurrent/futures/_base.py:319](http://localhost:8888/usr/lib/python3.10/concurrent/futures/_base.py#line=318), in _result_or_cancel(***failed resolving arguments***)
    317 try:
    318     try:
--> 319         return fut.result(timeout)
    320     finally:
    321         fut.cancel()

File [/usr/lib/python3.10/concurrent/futures/_base.py:451](http://localhost:8888/usr/lib/python3.10/concurrent/futures/_base.py#line=450), in Future.result(self, timeout)
    449     raise CancelledError()
    450 elif self._state == FINISHED:
--> 451     return self.__get_result()
    453 self._condition.wait(timeout)
    455 if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:

File [/usr/lib/python3.10/concurrent/futures/_base.py:403](http://localhost:8888/usr/lib/python3.10/concurrent/futures/_base.py#line=402), in Future.__get_result(self)
    401 if self._exception:
    402     try:
--> 403         raise self._exception
    404     finally:
    405         # Break a reference cycle with the exception in self._exception
    406         self = None

File [/usr/lib/python3.10/concurrent/futures/thread.py:58](http://localhost:8888/usr/lib/python3.10/concurrent/futures/thread.py#line=57), in _WorkItem.run(self)
     55     return
     57 try:
---> 58     result = self.fn(*self.args, **self.kwargs)
     59 except BaseException as exc:
     60     self.future.set_exception(exc)

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langchain_core/runnables/config.py:499, in ContextThreadPoolExecutor.map.<locals>._wrapped_fn(*args)
    498 def _wrapped_fn(*args: Any) -> T:
--> 499     return contexts.pop().run(fn, *args)

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langgraph/prebuilt/tool_node.py:62, in ToolNode._func.<locals>.run_one(call)
     61 def run_one(call: ToolCall):
---> 62     output = self.tools_by_name[call["name"]].invoke(call["args"], config)
     63     return ToolMessage(
     64         content=str_output(output), name=call["name"], tool_call_id=call["id"]
     65     )

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langchain_core/tools.py:260, in BaseTool.invoke(self, input, config, **kwargs)
    253 def invoke(
    254     self,
    255     input: Union[str, Dict],
    256     config: Optional[RunnableConfig] = None,
    257     **kwargs: Any,
    258 ) -> Any:
    259     config = ensure_config(config)
--> 260     return self.run(
    261         input,
    262         callbacks=config.get("callbacks"),
    263         tags=config.get("tags"),
    264         metadata=config.get("metadata"),
    265         run_name=config.get("run_name"),
    266         run_id=config.pop("run_id", None),
    267         config=config,
    268         **kwargs,
    269     )

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langchain_core/tools.py:417, in BaseTool.run(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, **kwargs)
    415 except ValidationError as e:
    416     if not self.handle_validation_error:
--> 417         raise e
    418     elif isinstance(self.handle_validation_error, bool):
    419         observation = "Tool input validation error"

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langchain_core/tools.py:406, in BaseTool.run(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, **kwargs)
    404 context = copy_context()
    405 context.run(_set_config_context, child_config)
--> 406 parsed_input = self._parse_input(tool_input)
    407 tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
    408 observation = (
    409     context.run(
    410         self._run, *tool_args, run_manager=run_manager, **tool_kwargs
   (...)
    413     else context.run(self._run, *tool_args, **tool_kwargs)
    414 )

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/langchain_core/tools.py:304, in BaseTool._parse_input(self, tool_input)
    302 else:
    303     if input_args is not None:
--> 304         result = input_args.parse_obj(tool_input)
    305         return {
    306             k: getattr(result, k)
    307             for k, v in result.dict().items()
    308             if k in tool_input
    309         }
    310 return tool_input

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/pydantic/v1/main.py:526, in BaseModel.parse_obj(cls, obj)
    524         exc = TypeError(f'{cls.__name__} expected dict not {obj.__class__.__name__}')
    525         raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls) from e
--> 526 return cls(**obj)

File ~/Documents/Projects/jupyter/.venv/lib/python3.10/site-packages/pydantic/v1/main.py:341, in BaseModel.__init__(__pydantic_self__, **data)
    339 values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data)
    340 if validation_error:
--> 341     raise validation_error
    342 try:
    343     object_setattr(__pydantic_self__, '__dict__', values)

ValidationError: 1 validation error for python_replSchema
code
  field required (type=value_error.missing)

Question: Does Bedrock LLMs API support `.with_structured_output` method?

I'm trying to use bedrock to extract information from documents, and I'd like to use Structured Output since there are bedrock's available models that support it.
Either I'm doing something wrong or it is not supported operation by Bedrock, my code is failing with NotImplemented error.
I was planning to consolidate API, costs, etc in Bedrock but at the moment I'm forced to use other providers.

Any hint would be greatly appreciated.

Error "stop_reason": response_body["stop_reason"] when running 0.1.5

I have run this code example with langchain-aws v 0.1.4 and get no errors:
https://catalog.workshops.aws/building-with-amazon-bedrock/en-US/bonus/bedrock-templates

However, running the same code with: v0.1.5, I get the error:
in: /site-packages/langchain_aws/llms/bedrock.py", line 260, in prepare_output
"stop_reason": response_body["stop_reason"],
~~~~~~~~~~~~~^^^^^^^^^^^^^^^
KeyError: 'stop_reason'

Uninstalling v0.15 and installing v0.14 resolved the error

Converse operation: messages.2.content: Conversation blocks and tool result blocks cannot be provided in the same turn.

With the ChatBedrock class, the following code works as expected:

from langchain_core.prompts import ChatPromptTemplate
from langchain_aws import ChatBedrock
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.messages import AIMessage, ToolMessage


def rag_query(query: str) -> str:
    """Do a RAG search in a internal knowledge base. """
    return "No results found !!!"

def web_query(query: str) -> str:
    """Search the web for the query."""
    return "Connection error"

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant with tools."),
    ("human", "Tell me who was Mozart ?"),
    AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Mozart"}, "id": 1}]),
    ToolMessage("You correctly called the tool", tool_call_id="1"),
    ("human", "Who was Bethoveen ?"),
    AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Bethoveen"}, "id": 2}]),
    ToolMessage("You correctly called the tool", tool_call_id="2"),
    ("human", "How to install linux ?"),
    AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "Tutorial on how to install Linux"}, "id": 3}]),
    ToolMessage("You correctly called the tool", tool_call_id="3"),
    ("human", "What is nmap"),
    AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "nmap documentation"}, "id": 4}]),
    ToolMessage("You correctly called the tool", tool_call_id="4"),
    ("human", "{question}")
])

bedrock = boto3.client('bedrock-runtime')
llm = ChatBedrock(model_id="anthropic.claude-3-haiku-20240307-v1:0", client=bedrock)

chain = {"question": RunnablePassthrough()}|  prompt | llm.bind_tools([rag_query, web_query])
chain.invoke("How to use docker ?")

Result:

AIMessage(content='', additional_kwargs={'usage': {'prompt_tokens': 741, 'completion_tokens': 54, 'total_tokens': 795}, 'stop_reason': 'tool_use', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, response_metadata={'usage': {'prompt_tokens': 741, 'completion_tokens': 54, 'total_tokens': 795}, 'stop_reason': 'tool_use', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, id='run-f7e8b75a-ceeb-4021-952d-39a74c880203-0', tool_calls=[{'name': 'web_query', 'args': {'query': 'docker tutorial'}, 'id': 'toolu_bdrk_01KGdyLm9JyGVVZQ4VQF1ZPR', 'type': 'tool_call'}], usage_metadata={'input_tokens': 741, 'output_tokens': 54, 'total_tokens': 795})

But when using the ChatBedrockConverse class:

from langchain_core.prompts import ChatPromptTemplate
from langchain_aws import ChatBedrockConverse
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.messages import AIMessage, ToolMessage


def rag_query(query: str) -> str:
    """Do a RAG search in a internal knowledge base. """
    return "No results found !!!"

def web_query(query: str) -> str:
    """Search the web for the query."""
    return "Connection error"

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant with tools."),
    ("human", "Tell me who was Mozart ?"),
    AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Mozart"}, "id": 1}]),
    ToolMessage("You correctly called the tool", tool_call_id="1"),
    ("human", "Who was Bethoveen ?"),
    AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Bethoveen"}, "id": 2}]),
    ToolMessage("You correctly called the tool", tool_call_id="2"),
    ("human", "How to install linux ?"),
    AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "Tutorial on how to install Linux"}, "id": 3}]),
    ToolMessage("You correctly called the tool", tool_call_id="3"),
    ("human", "What is nmap"),
    AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "nmap documentation"}, "id": 4}]),
    ToolMessage("You correctly called the tool", tool_call_id="4"),
    ("human", "{question}")
])

bedrock = boto3.client('bedrock-runtime')
llm = ChatBedrockConverse(model_id="anthropic.claude-3-haiku-20240307-v1:0", client=bedrock)

chain = {"question": RunnablePassthrough()}|  prompt | llm.bind_tools([rag_query, web_query])
chain.invoke("How to use docker ?")

I receive the following exception:

ValidationException: An error occurred (ValidationException) when calling the Converse operation: messages.2.content: Conversation blocks and tool result blocks cannot be provided in the same turn.

@baskaryan

BedrockAnthropicTokenUsageCallbackHandler does not function with langchain_aws

langchain_community.callbacks.bedrock_anthropic_callback.BedrockAnthropicTokenUsageCallbackHandler appears to be broken with langchain_aws models.

Example:

# Latest versions
#%pip install -U langchain_community==0.2.0 langchain_core==0.2.0 langchain_aws==0.1.4

from langchain_community.callbacks.bedrock_anthropic_callback import BedrockAnthropicTokenUsageCallbackHandler
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_aws import ChatBedrock

region = "us-east-1"

model = ChatBedrock(
    model_id="anthropic.claude-3-sonnet-20240229-v1:0",
    region_name="us-east-1",
)

# Create an instance of the callback handler
token_usage_callback = BedrockAnthropicTokenUsageCallbackHandler()

# Pass the callback handler to the underlying LLM model
model.callbacks = [token_usage_callback]

prompt = PromptTemplate(
    template="List 5 colors",
    input_variables=[],
)

# Create an instance of the callback handler
token_usage_callback = BedrockAnthropicTokenUsageCallbackHandler()

# Pass the callback handler to the underlying LLM model
model.callbacks = [token_usage_callback]

# Create the processing chain
chain = prompt | model | StrOutputParser()

response = chain.invoke({})
print(response)

print(token_usage_callback)

Output:

Here are 5 colors:

1. Red
2. Blue
3. Yellow
4. Green
5. Purple
Tokens Used: 0
	Prompt Tokens: 0
	Completion Tokens: 0
Successful Requests: 1
Total Cost (USD): $0.0

Side note: It might be a good idea for langchain_aws to take over the BedrockAnthropicTokenUsageCallbackHandler callback instead in order to keep it more up to date?

test

Privileged issue

  • I am a LangChain maintainer, or was asked directly by a LangChain maintainer to create an issue here.

Issue Content

test issue

ChatBedrock doesn't work with Cohere Command R and Command R+ models

Running the ChatBedrock model with Cohere's new Command R and Command R+ models will throw a ValidationException.

When you invoke the following model:

llm = ChatBedrock(
    model_id="cohere.command-r-v1:0",
    model_kwargs={
        "temperature": 0.3,
        "max_tokens": 4096
    },
)

You will get the following error:

ERROR: Error raised by bedrock service: An error occurred (ValidationException) when calling the InvokeModel operation: Malformed input request: #: extraneous key [prompt] is not permitted, please reformat your input and try again.

This is likely because the new Command R models do not accept "prompt" as an input. It accepts "message". (Source).
This should be refactored to pass in a message key instead of prompt if Command R or R+ is used.

ChatBedrockConverse Pydantic example working with Haiku but not with Sonnet?

Pydantic WorkoutProgram Class Generation Fails with Claude 3 Sonnet but Works with Haiku

Description

When using the ChatBedrockConverse class from LangChain to generate a workout program, the code works fine with the Claude 3 Haiku model but fails with the Claude 3 Sonnet model. The error suggests that the Sonnet model is not returning the expected structure for the WorkoutProgram. Using langchain-aws v0.1.12.

Steps to Reproduce

Run the following self-contained script. Uncomment Haiku model_id to see it run successfully.

Code

import json
from typing import Any
import boto3
from botocore.config import Config
from langchain.pydantic_v1 import BaseModel, Field
from langchain_aws.chat_models.bedrock import ChatBedrockConverse

# AWS Bedrock setup
REGION = "us-east-1"
session = boto3.Session(region_name=REGION)
bedrock_client = session.client(
    "bedrock-runtime", 
)

# Model definitions
class Day(BaseModel):
    day_of_week: str = Field(..., description="Day of the week")
    activities: list[str] = Field(..., description="List of 1-3 high-level workout descriptions or rest day activities")

class Week(BaseModel):
    monday: Day
    tuesday: Day
    wednesday: Day
    thursday: Day
    friday: Day
    saturday: Day
    sunday: Day

class WorkoutProgram(BaseModel):
    """Generate a 4-week workout program for a beginner"""
    week1: Week = Field(..., description="Workout plan for Week 1")
    week2: Week = Field(..., description="Workout plan for Week 2")
    week3: Week = Field(..., description="Workout plan for Week 3")
    week4: Week = Field(..., description="Workout plan for Week 4")

# LLM setup
llm = ChatBedrockConverse(
    client=bedrock_client,
    model_id="anthropic.claude-3-sonnet-20240229-v1:0",  # Change to Haiku to see it work
    #model_id="anthropic.claude-3-haiku-20240307-v1:0",
    max_tokens=4096,
)

llm_with_tools = llm.bind_tools([WorkoutProgram])

# Workout program generation
def generate_initial_program(user_profile: dict) -> WorkoutProgram:
    initial_prompt = f"""
    You are a professional fitness trainer tasked with creating a 4-week workout program for a beginner with the following characteristics:
    {json.dumps(user_profile, indent=2)}

    Create a program that is tailored to this individual's needs and goals. The program should focus on full-body workouts 3 days a week, but adjust the intensity and types of exercises based on the user's profile. 

    Include all 7 days in each week, with the non-workout days being rest days or light activity days.
    For each day, provide 1-3 high-level descriptions of the workouts or activities, such as "30 min HIIT cardio" or "Chest and Back training".
    Rest days can include suggestions like "Light stretching" or "20 min walk".

    Please create this personalized 4-week workout program in a single response, providing a complete 4-week plan.
    """
    response: Any = llm_with_tools.invoke(initial_prompt, tool_choice={"tool": {"name": "WorkoutProgram"}})

    print("Raw response content:")
    print(json.dumps(response.content, indent=2))

    workout_program_data: dict[str, Any] = response.content[0]['input']
    print("\nExtracted workout program data:")
    print(json.dumps(workout_program_data, indent=2))

    return WorkoutProgram.parse_obj(workout_program_data)


user_profile = {
    "height": "5'10\"",
    "weight": "180 lbs",
    "age": 30,
    "goals": "Lose weight and improve overall fitness"
}

initial_workout_program = generate_initial_program(user_profile)
print("\nSuccessfully generated workout program:")
print(initial_workout_program.json(indent=2))

Error Message

When run with the Sonnet model, you should see an error like this:

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[1], [line 80](vscode-notebook-cell:?execution_count=1&line=80)
     [70](vscode-notebook-cell:?execution_count=1&line=70)     return WorkoutProgram.parse_obj(workout_program_data)
     [73](vscode-notebook-cell:?execution_count=1&line=73) user_profile = {
     [74](vscode-notebook-cell:?execution_count=1&line=74)     "height": "5'10\"",
     [75](vscode-notebook-cell:?execution_count=1&line=75)     "weight": "180 lbs",
     [76](vscode-notebook-cell:?execution_count=1&line=76)     "age": 30,
     [77](vscode-notebook-cell:?execution_count=1&line=77)     "goals": "Lose weight and improve overall fitness"
     [78](vscode-notebook-cell:?execution_count=1&line=78) }
---> [80](vscode-notebook-cell:?execution_count=1&line=80) initial_workout_program = generate_initial_program(user_profile)
     [81](vscode-notebook-cell:?execution_count=1&line=81) print("\nSuccessfully generated workout program:")
     [82](vscode-notebook-cell:?execution_count=1&line=82) print(initial_workout_program.json(indent=2))

Cell In[1], [line 70](vscode-notebook-cell:?execution_count=1&line=70)
     [67](vscode-notebook-cell:?execution_count=1&line=67) print("\nExtracted workout program data:")
     [68](vscode-notebook-cell:?execution_count=1&line=68) print(json.dumps(workout_program_data, indent=2))
---> [70](vscode-notebook-cell:?execution_count=1&line=70) return WorkoutProgram.parse_obj(workout_program_data)

File ~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:526, in BaseModel.parse_obj(cls, obj)
    [524](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:524)         exc = TypeError(f'{cls.__name__} expected dict not {obj.__class__.__name__}')
    [525](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:525)         raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls) from e
--> [526](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:526) return cls(**obj)

File ~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:341, in BaseModel.__init__(__pydantic_self__, **data)
    [339](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:339) values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data)
    [340](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:340) if validation_error:
--> [341](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:341)     raise validation_error
    [342](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:342) try:
    [343](https://file+.vscode-resource.vscode-cdn.net/Users/austinwelch/Desktop/lg-example/~/mambaforge/envs/py311/lib/python3.11/site-packages/pydantic/v1/main.py:343)     object_setattr(__pydantic_self__, '__dict__', values)

ValidationError: 4 validation errors for WorkoutProgram
week1
  field required (type=value_error.missing)
week2
  field required (type=value_error.missing)
week3
  field required (type=value_error.missing)
week4
  field required (type=value_error.missing)

Any idea why pydantic validation fails for this code when I switch to Sonnet, but it works fine with Haiku?

Refactor ChatBedrock._combine_llm_outputs() to match other Langchain model output

_combine_llm_outputs() of different supported models hardcodes different keys.
In this example, the token_usage key is different in

https://github.com/langchain-ai/langchain/blob/acaf214a4516a2ffbd2817f553f4d48e6a908695/libs/community/langchain_community/chat_models/bedrock.py#L321

and

https://github.com/langchain-ai/langchain/blob/acaf214a4516a2ffbd2817f553f4d48e6a908695/libs/partners/openai/langchain_openai/chat_models/base.py#L457

The outcome is that replacing one model with another is not transparent and can lead to issues, such as breaking monitoring

Implement tool calling for ChatBedrock

Currently ChatBedrock cannot easily be used for tool calling when using langchain agents as it does not implement the bind_tools method. An example would be this langchain example using Anthropic: https://python.langchain.com/docs/use_cases/tool_use/agents/ and the create_tool_calling_agent function.

Are there any plans to support this common use case, at least when using Bedrock Anthropic models?
It might also be nice to add the BedrockChat to the langchain integrations page here: https://python.langchain.com/docs/integrations/chat/ so that the supported features can easily be understood.

ChatBedrockConverse.with_structured_output does not allow for snake case parameters

I recently converted from ChatBedrock to ChatBedrockConverse and hit issues when using with_structured_output. The result was that I kept receiving this:

pydantic.v1.error_wrappers.ValidationError: 1 validation error for ChooseRoute
question_type
  field required (type=value_error.missing)

ChooseRoute is the Pydantic model that was provide as input to the with_structured_output call.

class ChooseRoute(BaseModel):
    """Classifies student's question type for routing."""

    question_type: str = Field(
        description="Classify the student's question as, 'concept', 'homework' or 'video'"
    )

If I change the name of question_type to questionType everything runs fine.

It appears to be an issue in _converse_params: 421. The _snake_to_camel_keys function also changes the parameters in the tool schema which eventually causes an pydantic validation error on the construction of the pydantic model that's input to the with_structured_output call.

    def _stream(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> Iterator[ChatGenerationChunk]:
        bedrock_messages, system = _messages_to_bedrock(messages)
        params = self._converse_params(stop=stop, **_snake_to_camel_keys(kwargs)) # <------
        response = self.client.converse_stream(
            messages=bedrock_messages, system=system, **params
        )
        for event in response["stream"]:
            if message_chunk := _parse_stream_event(event):
                yield ChatGenerationChunk(message=message_chunk)

Official Anthropic function calling support?

Anthropic announced GA support for tools today (5/30): https://www.anthropic.com/news/tool-use-ga

Is this meant to be supported? I saw some PRs mentioned related to tool use, but the following code returns an empty list:

from langchain_aws import ChatBedrock  # langchain-aws v0.1.4 (latest)
from langchain_core.messages import HumanMessage
from langchain_community.tools import DuckDuckGoSearchRun

llm = ChatBedrock(
    model_id="anthropic.claude-3-sonnet-20240229-v1:0",
    region_name="us-east-1",
)

search = DuckDuckGoSearchRun()

tools = [search]

# llm with tools
llm_with_tools = llm.bind_tools(tools)

messages = [
    ("system", "You are a helpful assistant."),
    ("human", "What is LangChain Tool Calling?")
]

llm_with_tools.invoke(messages).tool_calls

Output:

[]

SageMaker streaming not working

When trying to use SageMakerEndpoint, here is a sample piece of code:

import json
from typing import Dict

import boto3
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain_aws.llms.sagemaker_endpoint import LLMContentHandler
from langchain_aws.llms.sagemaker_endpoint import SagemakerEndpoint
from langchain_core.prompts import PromptTemplate


class ContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompt: str, model_kwargs: Dict) -> bytes:
        input_str = json.dumps({"inputs": prompt, "parameters": model_kwargs})
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        return response_json[0]["generated_text"]


region = "<<region>>"
session = boto3.session.Session()
sagemaker_client = boto3.client("sagemaker-runtime", region_name=region)
endpoint_name = "<<endpoint-name>>"
conversation_memory = ConversationBufferMemory(
    memory_key="history", input_key="input", human_prefix="Human", ai_prefix="AI"
)
content_handler = ContentHandler()

llm = SagemakerEndpoint(
    endpoint_name=endpoint_name,
    client=sagemaker_client,
    content_handler=content_handler,
    model_kwargs={"temperature": 0.1, "max_new_tokens": 1000},
    streaming=False,  # changing this to True is an issue right now.
)
prompt_data = "{history} <s>[INST] {input} [/INST]"
question = "what is the recipe of mayonnaise?"
prompt_template = PromptTemplate(template=prompt_data)
conversation = ConversationChain(llm=llm, verbose=True, memory=conversation_memory, prompt=prompt_template)

response = conversation.invoke({"input": question, "history": conversation_memory.chat_memory.messages})
print("Model response::", response["response"])

With streaming set to False, I get a response, but with streaming set to True, this goes into an inifinite loop. Here is the stack trace:

---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
File [.../site-packages/langchain_aws/llms/sagemaker_endpoint.py:68](.../site-packages/langchain_aws/llms/sagemaker_endpoint.py#line=67), in LineIterator.__next__(self)
     67 try:
---> 68     chunk = next(self.byte_iterator)
     69 except StopIteration:

StopIteration: 

During handling of the above exception, another exception occurred:

KeyboardInterrupt                         Traceback (most recent call last)
Cell In[7], line 48
     45 prompt_template = PromptTemplate(template=prompt_data)
     46 conversation = ConversationChain(llm=llm, verbose=True, memory=conversation_memory, prompt=prompt_template)
---> 48 response = conversation.invoke({"input": question, "history": conversation_memory.chat_memory.messages})
     49 print("Model response::", response["response"])

File [.../site-packages/langchain/chains/base.py:166](.../site-packages/langchain/chains/base.py#line=165), in Chain.invoke(self, input, config, **kwargs)
    164 except BaseException as e:
    165     run_manager.on_chain_error(e)
--> 166     raise e
    167 run_manager.on_chain_end(outputs)
    169 if include_run_info:

File [.../site-packages/langchain/chains/base.py:156](.../site-packages/langchain/chains/base.py#line=155), in Chain.invoke(self, input, config, **kwargs)
    153 try:
    154     self._validate_inputs(inputs)
    155     outputs = (
--> 156         self._call(inputs, run_manager=run_manager)
    157         if new_arg_supported
    158         else self._call(inputs)
    159     )
    161     final_outputs: Dict[str, Any] = self.prep_outputs(
    162         inputs, outputs, return_only_outputs
    163     )
    164 except BaseException as e:

File [.../site-packages/langchain/chains/llm.py:126](.../site-packages/langchain/chains/llm.py#line=125), in LLMChain._call(self, inputs, run_manager)
    121 def _call(
    122     self,
    123     inputs: Dict[str, Any],
    124     run_manager: Optional[CallbackManagerForChainRun] = None,
    125 ) -> Dict[str, str]:
--> 126     response = self.generate([inputs], run_manager=run_manager)
    127     return self.create_outputs(response)[0]

File [.../site-packages/langchain/chains/llm.py:138](.../site-packages/langchain/chains/llm.py#line=137), in LLMChain.generate(self, input_list, run_manager)
    136 callbacks = run_manager.get_child() if run_manager else None
    137 if isinstance(self.llm, BaseLanguageModel):
--> 138     return self.llm.generate_prompt(
    139         prompts,
    140         stop,
    141         callbacks=callbacks,
    142         **self.llm_kwargs,
    143     )
    144 else:
    145     results = self.llm.bind(stop=stop, **self.llm_kwargs).batch(
    146         cast(List, prompts), {"callbacks": callbacks}
    147     )

File [.../site-packages/langchain_core/language_models/llms.py:633](.../site-packages/langchain_core/language_models/llms.py#line=632), in BaseLLM.generate_prompt(self, prompts, stop, callbacks, **kwargs)
    625 def generate_prompt(
    626     self,
    627     prompts: List[PromptValue],
   (...)
    630     **kwargs: Any,
    631 ) -> LLMResult:
    632     prompt_strings = [p.to_string() for p in prompts]
--> 633     return self.generate(prompt_strings, stop=stop, callbacks=callbacks, **kwargs)

File [.../site-packages/langchain_core/language_models/llms.py:803](.../site-packages/langchain_core/language_models/llms.py#line=802), in BaseLLM.generate(self, prompts, stop, callbacks, tags, metadata, run_name, run_id, **kwargs)
    788 if (self.cache is None and get_llm_cache() is None) or self.cache is False:
    789     run_managers = [
    790         callback_manager.on_llm_start(
    791             dumpd(self),
   (...)
    801         )
    802     ]
--> 803     output = self._generate_helper(
    804         prompts, stop, run_managers, bool(new_arg_supported), **kwargs
    805     )
    806     return output
    807 if len(missing_prompts) > 0:

File [.../site-packages/langchain_core/language_models/llms.py:670](.../site-packages/langchain_core/language_models/llms.py#line=669), in BaseLLM._generate_helper(self, prompts, stop, run_managers, new_arg_supported, **kwargs)
    668     for run_manager in run_managers:
    669         run_manager.on_llm_error(e, response=LLMResult(generations=[]))
--> 670     raise e
    671 flattened_outputs = output.flatten()
    672 for manager, flattened_output in zip(run_managers, flattened_outputs):

File [.../site-packages/langchain_core/language_models/llms.py:657](.../site-packages/langchain_core/language_models/llms.py#line=656), in BaseLLM._generate_helper(self, prompts, stop, run_managers, new_arg_supported, **kwargs)
    647 def _generate_helper(
    648     self,
    649     prompts: List[str],
   (...)
    653     **kwargs: Any,
    654 ) -> LLMResult:
    655     try:
    656         output = (
--> 657             self._generate(
    658                 prompts,
    659                 stop=stop,
    660                 # TODO: support multiple run managers
    661                 run_manager=run_managers[0] if run_managers else None,
    662                 **kwargs,
    663             )
    664             if new_arg_supported
    665             else self._generate(prompts, stop=stop)
    666         )
    667     except BaseException as e:
    668         for run_manager in run_managers:

File [.../site-packages/langchain_core/language_models/llms.py:1317](.../site-packages/langchain_core/language_models/llms.py#line=1316), in LLM._generate(self, prompts, stop, run_manager, **kwargs)
   1314 new_arg_supported = inspect.signature(self._call).parameters.get("run_manager")
   1315 for prompt in prompts:
   1316     text = (
-> 1317         self._call(prompt, stop=stop, run_manager=run_manager, **kwargs)
   1318         if new_arg_supported
   1319         else self._call(prompt, stop=stop, **kwargs)
   1320     )
   1321     generations.append([Generation(text=text)])
   1322 return LLMResult(generations=generations)

File [.../site-packages/langchain_aws/llms/sagemaker_endpoint.py:373](.../site-packages/langchain_aws/llms/sagemaker_endpoint.py#line=372), in SagemakerEndpoint._call(self, prompt, stop, run_manager, **kwargs)
    371 if self.streaming and run_manager:
    372     completion: str = ""
--> 373     for chunk in self._stream(prompt, stop, run_manager, **kwargs):
    374         completion += chunk.text
    375     return completion

File [.../site-packages/langchain_aws/llms/sagemaker_endpoint.py:327](.../site-packages/langchain_aws/llms/sagemaker_endpoint.py#line=326), in SagemakerEndpoint._stream(self, prompt, stop, run_manager, **kwargs)
    319 resp = self.client.invoke_endpoint_with_response_stream(
    320     EndpointName=self.endpoint_name,
    321     Body=self.content_handler.transform_input(prompt, _model_kwargs),
    322     ContentType=self.content_handler.content_type,
    323     **_endpoint_kwargs,
    324 )
    325 iterator = LineIterator(resp["Body"])
--> 327 for line in iterator:
    328     text = self.content_handler.transform_output(line)
    330     if stop is not None:

File [.../site-packages/langchain_aws/llms/sagemaker_endpoint.py:70](.../site-packages/langchain_aws/llms/sagemaker_endpoint.py#line=69), in LineIterator.__next__(self)
     68     chunk = next(self.byte_iterator)
     69 except StopIteration:
---> 70     if self.read_pos < self.buffer.getbuffer().nbytes:
     71         continue
     72     raise

KeyboardInterrupt:

On printing out the values of self.read_pos and self.buffer.getbuffer().nbytes:, I can see self.read_pos is always at 0 and this is stuck in an infinite loop with this if statement.

Recursion depth error: Tool calling with streaming model

When setting the Bedrock model to streaming=True and tool calls enabled, it causes a recursion depth error due to stream and generate constantly pointing back to each other.

  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 432, in _stream
    result = self._generate(
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 501, in _generate
    for chunk in self._stream(messages, stop, run_manager, **kwargs):
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 432, in _stream
    result = self._generate(
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 501, in _generate
    for chunk in self._stream(messages, stop, run_manager, **kwargs):
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 432, in _stream
    result = self._generate(
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 501, in _generate
    for chunk in self._stream(messages, stop, run_manager, **kwargs):
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\chat_models\bedrock.py", line 427, in _stream
    provider = self._get_provider()
  File "C:\Users\..\AppData\Local\pypoetry\Cache\virtualenvs\..\lib\site-packages\langchain_aws\llms\bedrock.py", line 570, in _get_provider
    if self.model_id.startswith("arn"):
RecursionError: maximum recursion depth exceeded while calling a Python object

llm = ChatBedrock(client=bedrock_client, model_id=model, model_kwargs=model_kwargs, streaming=True)

def _stream(
...

        if "claude-3" in self._get_model():
            if _tools_in_params({**kwargs}):
                result = self._generate(
                    messages, stop=stop, run_manager=run_manager, **kwargs
                )

_stream points back to self._generate if model is claude 3 and tools exist;

    def _generate(
...
        if self.streaming:
            response_metadata: List[Dict[str, Any]] = []
            for chunk in self._stream(messages, stop, run_manager, **kwargs):
                completion += chunk.text
                response_metadata.append(chunk.message.response_metadata)
                if "tool_calls" in chunk.message.additional_kwargs.keys():
                    tool_calls = chunk.message.additional_kwargs["tool_calls"]
            llm_output = _combine_generation_info_for_llm_result(
                response_metadata, provider_stop_reason_code
            )

_generate points back to _stream() if self.streaming is enabled.

Token Counts not used from Bedrock response.

I would like to track the token counts (input/output) for my Bedrock calls in a similar fashion to this example for langchain-openai using callbacks.

These token counts are present in the header (or chunk data for streaming), but they are not captured by langchain-aws.

This PR addresses this issue.

Thanks.

Request for Pydantic v2 support

Enterprise AWS customer here. Just wanted to say thank you for implementing structured output parsing for this adapter and getting it on Structured Output table - Langchain.

Now there's a new table of support at Langchain I'd like to get langchain-aws on. Versions of langchain-core after 0.2.23 require pydantic v2. There is a list of langchain adapters that support the pydantic v2 input to API calls here: How to use LangChain with different Pydantic versions | ๐Ÿฆœ๏ธ๐Ÿ”— LangChain - this is a feature request to get langchain-aws on that list. See also this tweet for an example of what I'd love to be able to do with langchain-aws in order to use it as well as my other providers.

Note that langchain is planning a full upgrade to pydantic v2 in September of this year: Pydantic Migration Feedback Request ยท langchain-ai/langchain ยท Discussion #24405

Redundant Tool Usage output being returned together with text outputs in the content field of AIMessage in ChatBedrockConverse.

When running the following code

from langchain_core.prompts import ChatPromptTemplate
from langchain_aws import ChatBedrockConverse, ChatBedrock
from langchain_core.runnables import RunnableAssign,RunnablePassthrough
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.output_parsers import StrOutputParser
import uuid
from langchain_core.tools import tool
from typing import Annotated, Literal

@tool
def math_tool(op: Annotated[Literal["sum" , "sub" , "mul" , "div"], "The math operation to be perfomerd"], x: float, y: float) -> float:
    """Perform mathematical operations."""
    match op:
        case "sum":
            return x + y
        case "sub":
            return x - y
        case "mul":
            return x * y
        case "div":
            return x/y
        case _:
            raise ToolException(f"Operation {op} not recognized.")

model = ChatBedrockConverse(model_id="anthropic.claude-3-haiku-20240307-v1:0", client=client)

prompt = ChatPromptTemplate.from_messages([
("system", """Answer the user's request using relevant tools (if they are available). Before calling a tool, do some analysis within <thinking></thinking> tags. First, think about which of the provided tools is the relevant tool to answer the user's request. Second, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool call. BUT, if one of the values for a required parameter is missing, DO NOT invoke the function (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters. DO NOT ask for more information on optional parameters if it is not provided."""),
("placeholder", "{messages}")])

chain = {"messages": RunnablePassthrough()} | prompt | model.bind_tools([math_tool])
msg = chain.invoke([HumanMessage(content="How much is 3*3 ?")])
msg.content

The content field of the AIMessage returned is:

[{'type': 'text',
  'text': '<thinking>\nThe relevant tool for this request is the "math_tool" which can perform various mathematical operations. The required parameters for this tool are "op" (the operation to perform), "x" (the first operand), and "y" (the second operand).\n\nThe user has requested to find the result of 3 * 3, which corresponds to the "mul" (multiplication) operation with "x" = 3 and "y" = 3. All the required parameters are provided, so I can proceed with invoking the math_tool.\n</thinking>'},
  {'type': 'tool_use',
   'name': 'math_tool',
   'input': {'op': 'mul', 'x': 3, 'y': 3},
   'id': 'tooluse_DOvtVdkMSECOFKJ5usGsgA'}]

A list containing both the text block and the tool call.

But the tool_call field of the AIMessage class is already populate with that tool information:

msg.tool_calls
 [{'name': 'math_tool',
   'args': {'op': 'mul', 'x': 3, 'y': 3},
   'id': 'tooluse_DOvtVdkMSECOFKJ5usGsgA'}]

Is that the expected behavior ? Isn't it redundant to return tool calling information both in the content and tool_calls fields ?

Error: Mistral Models are not working for ChatBedrock & BedrockLLM

This is an example with ChatBedrock.

from langchain_core.messages import HumanMessage
from langchain_aws import ChatBedrock, BedrockLLM

chat = ChatBedrock(
    model_id="mistral.mistral-large-2402-v1:0",
    model_kwargs={"temperature": 0.1},
)
messages = [
    HumanMessage(
        content="Translate this sentence from English to French. I love programming."
    )
]
chat.invoke(messages)

Error below:

ValueError: Error raised by bedrock service: 'stop_reason'

The error is present for all mistral models. It's been a few days already since we detected this error.

raise TypeError( TypeError: Additional kwargs key generation_token_count already exists in left dict and value has unsupported type <class 'int'>

from langchain.agents import AgentExecutor, create_tool_calling_agent, tool
from langchain_aws import ChatBedrock
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful assistant"),
("placeholder", "{chat_history}"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
]
)
model = ChatBedrock(
model_id="meta.llama3-70b-instruct-v1:0"
)

@tool
def magic_function(input: int) -> int:
"""Applies a magic function to an input."""
return input + 2

tools = [magic_function]

agent = create_tool_calling_agent(model, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

agent_executor.invoke({"input": "what is the value of magic_function(3)?"})

Error Message and Stack Trace (if applicable)

File "/MVP/mvp/lib/python3.12/site-packages/langchain_core/messages/ai.py", line 243, in add
response_metadata = merge_dicts(
^^^^^^^^^^^^
File "/MVP/mvp/lib/python3.12/site-packages/langchain_core/utils/_merge.py", line 40, in merge_dicts
raise TypeError(
TypeError: Additional kwargs key generation_token_count already exists in left dict and value has unsupported type <class 'int'>.

Description

I am trying out the example from the langchain website and it is giving me error as TypeError: Additional kwargs key generation_token_count already exists in left dict and value has unsupported type <class 'int'>.

I cannot solve the error, and don't understand it. can you please resolve it?

System Info

langchain==0.2.6
langchain-aws==0.1.9
langchain-community==0.2.6
langchain-core==0.2.11
langchain-text-splitters==0.2.2
Mac
Python 3.12

ChatBedrock model doesn't work with llama 2 models when using the stop sequence in non-streaming mode

When using ChatBedrock with llama 2 models and generating texts in non-streaming mode, setting stop sequence will cause malformed input request error.

e.g. running the following code

model_id = "meta.llama2-70b-chat-v1"
model_kwargs = {
    "temperature": 0,
    "max_gen_len": 2048,
}

llm = BedrockChat(
    model_id=model_id,
    model_kwargs=model_kwargs
)

llm.invoke("show me the weather in sf", stop=["Humidity"])

will result in the following error message

ValueError: Error raised by bedrock service: An error occurred (ValidationException) when calling the InvokeModel operation: Malformed input request: extraneous key [stop_sequences] is not permitted, please reformat your input and try again.

Querying Llama3 70b using BedrockChat returns empty response if prompt is long

Hello,

I have posted this bug on the main repo. However it seems that from langchain_community.chat_models import BedrockChat has been deprecated.

I have tried with from langchain_aws.chat_models import BedrockChat and I am facing the same issue. So cross posting as it might be more relevant here.

TL;DR: Querying Llama3 with BedrockChat returns an empty string if long query. Short queries like "What is the capital of China?" does return the expected answer. If I pass the long query directly in the AWS Console, it works fine.

Example:

from langchain.chains import LLMChain
from langchain.prompts import HumanMessagePromptTemplate
from langchain.prompts.chat import ChatPromptTemplate
from langchain_aws.chat_models import BedrockChat 
import langchain

langchain.debug = True

def get_llama3_bedrock(
    model_id="meta.llama3-70b-instruct-v1:0",
    max_gen_len=2048,
    top_p=0.0,
    temperature=0.0,
):
    model_kwargs = {
        "top_p": top_p,
        "max_gen_len": max_gen_len,
        "temperature": temperature,
    }
    return BedrockChat(model_id=model_id, model_kwargs=model_kwargs)

prompt_poem = """
This is a poem by William Blake

============
Never seek to tell thy love
Love that never told can be 
For the gentle wind does move
Silently invisibly

I told my love I told my love 
I told her all my heart 
Trembling cold in ghastly fears
Ah she doth depart

Soon as she was gone from me
A traveller came by
Silently invisibly 
O was no deny 
============

What did the lady do?
"""
langchain_prompt = ChatPromptTemplate.from_messages([
        HumanMessagePromptTemplate.from_template(prompt_poem)
        ]
)
print("Response 1:", LLMChain(llm=get_llama3_bedrock(), prompt=langchain_prompt).run(dict()))
#Responds: ''

prompt_simple_question = """What is the capital of China?"""
langchain_prompt = ChatPromptTemplate.from_messages([
        HumanMessagePromptTemplate.from_template(prompt_simple_question)
        ]
)
print("Response 2:", LLMChain(llm=get_llama3_bedrock(), prompt=langchain_prompt).run(dict()))
#Responds: 'Beijing.'

Versions:
python 3.11.7

langchain==0.1.16
langchain-aws==0.1.2
langchain-core==0.1.46
langchain-text-splitters==0.0.1

Problems using agent type "Tool calling", with Bedrock LLMs "Claude 3.5 Sonnet" and "Llama 3 70b"

Good morning,

my goal is to implement Bedrock models "meta.llama3-70b-instruct-v1:0" and "anthropic.claude-3-5-sonnet-20240620-v1:0", with Langchain agent type Tool Calling

I implemented the models in this way:

        bedrock_session = boto3.Session(
                            region_name="us-east-1"
                      )

        bedrock_client = bedrock_session.client("bedrock-runtime")
            
        llm = ChatBedrockConverse(
                client=bedrock_client,
                model_id=llm_model, #"meta.llama3-70b-instruct-v1:0" or "anthropic.claude-3-5-sonnet-20240620-v1:0"
                verbose=verbose,
                callbacks=callbacks
            )
       tools = [...] #A list of BaseTool

        agent = create_tool_calling_agent(llm, tools, prompt)

        langchain_agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

        user_message = {
            "message": message,
        }

        if new_files:
            user_message["files"] = str(new_files)

        response = langchain_agent_executor.invoke({
            "input": json.dumps(user_message),
            "chat_history": self.memory.buffer_as_messages,
        })

Claude 3.5 Sonnet responses, but the output format of the response and tokens is not a string but:

[{'type': 'text', 'text': '[...]', 'index': 0}]

Llama 3 70b doesn't response, I had the following error:

Arguments: (ValidationException("An error occurred (ValidationException) when calling the ConverseStream operation: This model doesn't support tool use in streaming mode."),)

Bedrock invoke_model expects alternating roles

I am attempting to use ChatBedrock with an Anthropic Claude Sonnet model (anthropic.claude-3-sonnet-20240229-v1:0) in an implementation of LangGraph's collaborative multi-agent system. I am receiving an error as shown in a below stack trace where there is an expectation that roles must alternate. Messages passed in to _prepare_input_and_invoke are structured as so (with the message content redacted):

[
  {
    "role": "user",
    "content": "REDACTED"
  },
  {
    "role": "assistant",
    "content": "REDACTED"
  },
  {
    "role": "assistant",
    "content": "REDACTED"
  }
]

I assume this is expected behavior in regards to the multi-agent system and see similar message structure when using ChatOpenAI in place of ChatBedrock where there are multiple messages of type "ai" in succession.

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/langchain_aws/llms/bedrock.py", line 628, in _prepare_input_and_invoke
    response = self.client.invoke_model(**request_options)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/botocore/client.py", line 565, in _api_call
    return self._make_api_call(operation_name, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/botocore/client.py", line 1021, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the InvokeModel operation: messages: roles must alternate between "user" and "assistant", but found multiple "assistant" roles in a row

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 399, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 70, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 278, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/routers/spel.py", line 28, in spel_invoke
    content = chain.handle_query(message.input)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/services/ai_components.py", line 289, in handle_query
    return self.graph.invoke(
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langgraph/pregel/__init__.py", line 1333, in invoke
    for chunk in self.stream(
  File "/usr/local/lib/python3.11/site-packages/langgraph/pregel/__init__.py", line 876, in stream
    _panic_or_proceed(done, inflight, step)
  File "/usr/local/lib/python3.11/site-packages/langgraph/pregel/__init__.py", line 1422, in _panic_or_proceed
    raise exc
  File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langgraph/pregel/retry.py", line 66, in run_with_retry
    task.proc.invoke(task.input, task.config)
  File "/usr/local/lib/python3.11/site-packages/langchain_core/runnables/base.py", line 2399, in invoke
    input = step.invoke(
            ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langgraph/utils.py", line 95, in invoke
    ret = context.run(self.func, input, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/services/ai_components.py", line 315, in agent_node
    result = agent.invoke(state)
             ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain_core/runnables/base.py", line 2399, in invoke
    input = step.invoke(
            ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py", line 170, in invoke
    self.generate_prompt(
  File "/usr/local/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py", line 599, in generate_prompt
    return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py", line 456, in generate
    raise e
  File "/usr/local/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py", line 446, in generate
    self._generate_with_cache(
  File "/usr/local/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py", line 671, in _generate_with_cache
    result = self._generate(
             ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain_aws/chat_models/bedrock.py", line 440, in _generate
    completion, llm_output = self._prepare_input_and_invoke(
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain_aws/llms/bedrock.py", line 635, in _prepare_input_and_invoke
    raise ValueError(f"Error raised by bedrock service: {e}")
ValueError: Error raised by bedrock service: An error occurred (ValidationException) when calling the InvokeModel operation: messages: roles must alternate between "user" and "assistant", but found multiple "assistant" roles in a row

fix Bedrock Guardrails intervention detection

in llms/bedrock.py, line 682 the function _is_guardrails_intervention uses body.get(GUARDRAILS_BODY_KEY) == "GUARDRAIL_INTERVENED" to detect intervention, where GUARDRAILS_BODY_KEY is defined as "amazon-bedrock-guardrailAction" on line 35.
However the payload from Bedrock actually uses 'amazon-bedrock-guardrailAction': 'INTERVENED' to indicate intervention.

Fix:
line 35: GUARDRAILS_BODY_KEY=amazon-bedrock-guardrailAction
line 682: body.get(GUARDRAILS_BODY_KEY) == "INTERVENED"

Bedrock Mistral prompts need to be updated to match AWS docs

Hello! I am using Mistral through Bedrock with LangChain and noticed, after using the model for a while with ChatMessageHistory, it would begin to hallucinate and have trouble stopping. I dug into it and it seems similar to langchain-aws#31 that the prompt getting sent to the model doesn't quite match the AWS documentation. Notably, there is no use of <s> or </s>, and the system prompt isn't put inside the first human message.

I updated langchain_aws/chat_models/bedrock.py as follows:

def convert_messages_to_prompt_mistral(messages: List[BaseMessage]) -> str:
    """
    Convert a list of messages to a prompt for mistral. The format is:
    <s>[INST] Instruction [/INST] Model answer</s>[INST] Follow-up instruction [/INST]
    Any system messages are simply prepended to the first HumanMessage.
    """
    system_messages = [msg for msg in messages if isinstance(msg, SystemMessage)]
    non_system_messages = [msg for msg in messages if not isinstance(msg, SystemMessage)]
    prompt = "<s>"
    for idx, message in enumerate(non_system_messages):
        if isinstance(message, HumanMessage):
            prompt += "[INST] "
            if idx == 0 and len(system_messages) > 0:
                system_prompt = "\n".join([msg.content for msg in system_messages])
                prompt += f"{system_prompt}\n\n"
            prompt += f"{message.content.strip()} [/INST]"
        elif isinstance(message, AIMessage):
            prompt += f"{message.content.strip()}</s>"
        else:
            raise ValueError(f"Got unknown type {message}")
    return prompt

Here is a simple program:

from langchain_aws import ChatBedrock
from langchain_core.messages import HumanMessage, SystemMessage


messages = [
    SystemMessage(content="You're a helpful assistant that answers with poems."),
    HumanMessage(content="What is the purpose of model regularization?"),
]

chat = ChatBedrock(
    model_id="mistral.mixtral-8x7b-instruct-v0:1",
    model_kwargs={"max_tokens": 1000},
    region_name="us-west-2",
)

for chunk in chat.stream(messages):
    print(chunk.content, end="", flush=True)

Prompt before (obtained by adding some print statements to the _stream function here):

{"max_tokens": 1000, "prompt": "<<SYS>> You're a helpful assistant that answers with poems. <</SYS>>\n[INST] What is the purpose of model regularization? [/INST]"}

After:

{"max_tokens": 1000, "prompt": "<s>[INST] You're a helpful assistant that answers with poems.\n\nWhat is the purpose of model regularization? [/INST]"}

Sorry, this simple program doesn't demonstrate the hallucinating, the program I have is much longer and for work and unfortunately I can't share it. But it does demonstrate the prompt updates.

With this update to the prompt, the model seems to be working great and I no longer have the issues with it hallucinating or not stopping.

Note about wrapping the system prompt in <<SYS>>/<</SYS>>: I reviewed the mistral docs, llama_index's implementation, as well as chujiezheng's chat_templates. Note that Mistral doesn't seem to advertise any special handling for system prompts. The llama_index implementation just follows Llama's use of <<SYS>>, but chujiezheng doesn't. I tried both ways and it didn't seem to matter, so for the above, I left it out.

I hope this is helpful. If my understanding of how <s> and </s> works isn't correct, I apologize, but it really seems necessary according to the docs, this other really helpful read (How to Prompt Mistral AI models, and Why) and I'm pretty sure nothing is adding it. Thank you for your consideration addressing this issue.

llama3-70b chat no longer functioning with Bedrock

With the most recent version it appears that ChatBedrock does not function when I call using a llama3 model using chain.ainvoke({}).

\langchain_aws\llms\bedrock.py", line 867, in _aprepare_input_and_invoke_stream
    body = json.dumps(input_body)
                      ^^^^^^^^^^
UnboundLocalError: cannot access local variable 'input_body' where it is not associated with a value

It appears that the code does not set the input_body value if the model is not claude-3:

if "claude-3" in self._get_model():
            if _tools_in_params(params):
                input_body = LLMInputOutputAdapter.prepare_input(
                    provider=provider,
                    model_kwargs=params,
                    prompt=prompt,
                    system=system,
                    messages=messages,
                    tools=params["tools"],
                )
            else:
                input_body = LLMInputOutputAdapter.prepare_input(
                    provider=provider,
                    prompt=prompt,
                    system=system,
                    messages=messages,
                    model_kwargs=params,
                )
body = json.dumps(input_body)

This is seemingly a result of the following commit, perhaps the else is at the wrong level?
018ed89

Streaming error: "Additional kwargs key generation_token_count already exists"

Hello,

I'm trying to replicate the Streamlit GPT-like app using Bedrock with Langchain, and seems that the stream operation doesn't work. As for the error, seems like the API is returning the generation_token_count with a different type than what is expected on the local side.

Could this be a library error?

Traceback (most recent call last):
  File "/Users/<REDACTED>/test/genai/.venv/lib/python3.12/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 600, in _run_script
    exec(code, module.__dict__)
  File "/Users/<REDACTED>/test/genai/chat.py", line 40, in <module>
    for chunk in chat.stream(messages):
  File "/Users/<REDACTED>/test/genai/.venv/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 265, in stream
    raise e
  File "/Users/<REDACTED>/test/genai/.venv/lib/python3.12/site-packages/langchain_core/language_models/chat_models.py", line 256, in stream
    generation += chunk
  File "/Users/<REDACTED>/test/genai/.venv/lib/python3.12/site-packages/langchain_core/outputs/chat_generation.py", line 79, in __add__
    message=self.message + other.message,
            ~~~~~~~~~~~~~^~~~~~~~~~~~~~~
  File "/Users/<REDACTED>/test/genai/.venv/lib/python3.12/site-packages/langchain_core/messages/ai.py", line 224, in __add__
    response_metadata = merge_dicts(
                        ^^^^^^^^^^^^
  File "/Users/<REDACTED>/test/genai/.venv/lib/python3.12/site-packages/langchain_core/utils/_merge.py", line 40, in merge_dicts
    raise TypeError(
TypeError: Additional kwargs key generation_token_count already exists in left dict and value has unsupported type <class 'int'>.

Here is how I'm calling the model (I removed al streamlit interaction):

from langchain_aws import ChatBedrock
from langchain_core.messages import HumanMessage

prompt = "Tell me a joke"

chat = ChatBedrock(
    region_name="us-east-1",
    model_id="meta.llama3-8b-instruct-v1:0",
    model_kwargs={"temperature": 0.8},
    streaming=True,
)

messages = [HumanMessage(content=prompt)]

for chunk in chat.stream(messages):
    print(chunk.content, end="", flush=True)

Libraries

I'm using the following libraries:

# requirements.txt

langchain==0.2.6
langchain_aws==0.1.8
langchain_community==0.2.6
langchain_core==0.2.10
streamlit==1.35.0
unstructured==0.14.7

BedrockEmbeddings BUG: Default values for dim and norm in embed_documents() overwrite model_kwargs for Titan Embedding v2 model

The changes introduced in PR #39 make impossible for one to set dimensions or normalize parameters in the model_kwargs of BedrockEmbeddings class.

Example below:

from langchain_aws import BedrockEmbeddings

# The options set here are overwritten
model_kwargs = {
    "dimensions": 256,
    "normalize": False
}

embeddings = BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0",
                               region_name="eu-central-1", model_kwargs=model_kwargs)

text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
embedded = embeddings.embed_documents(texts=[text])

print(embedded[0])         # Vector is normalized because of the norm = True default in embed_documents()
print(len(embedded[0]))    # Prints a length of 1024 because of the dim = 1024 default in embed_documents()

This is problematic when using the BedrockEmbeddings with a vector store:

from langchain_aws import BedrockEmbeddings
from langchain_postgres.vectorstores import PGVector
import os

DB_USER = os.getenv('PGUSER')
DB_PASSWORD = os.getenv('PGPASSWORD')
DB_HOST = os.getenv('PGHOST')
DB_PORT = os.getenv('PGPORT')
DB_NAME = os.getenv('PGDATABASE')

model_kwargs = {
    "dimensions": 256,
    "normalize": False
}

connection = f"postgresql+psycopg://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"

text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."

embeddings = BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0",
                               region_name="eu-central-1", model_kwargs=model_kwargs)

pgvector_store = PGVector(
    connection=connection, embeddings=embeddings)

pgvector_store.add_texts(texts=[text])   # add_texts() calls embed_documents() and model_kwargs are overwritten again

Expected behavior is model_kwargs not to be overwritten by any method unless explicit values are provided.

EDIT: Even if the embeddings are created separately from the vector store e.g:

embedded_texts = embeddings.embed_documents(
        texts=texts, dim=512)

vector_store.add_embeddings(
        texts=texts,
        embeddings=embedded_texts
)

When the chain is invoked with the vector store retriever it throws an error for different dimension vectors since it creates a query vector with the default 1024 dimensions:

retriever = vector_store.as_retriever()
    chain = (
        {
            "context": RunnableLambda(get_retriever_query_dict) | retriever,
            "question": itemgetter("question")
        }
        | prompt
        | model
        | StrOutputParser()
    )
chain.invoke({"question": question})
[SQL: SELECT langchain_pg_embedding.id AS langchain_pg_embedding_id, langchain_pg_embedding.collection_id AS langchain_pg_embedding_collection_id, langchain_pg_embedding.embedding AS langchain_pg_embedding_embedding, langchain_pg_embedding.document AS langchain_pg_embedding_document, langchain_pg_embedding.cmetadata AS langchain_pg_embedding_cmetadata, langchain_pg_embedding.embedding <=> %(embedding_1)s AS distance

sqlalchemy.exc.DataError: (psycopg.errors.DataException) different vector dimensions 512 and 1024

v0.1.14 breaks streaming response rendering from Claude 3.5 Sonnet

We picked up v0.1.14 in our project today and noticed streaming responses from Claude via Bedrock are no longer rendered as chunks are delivered. The message from the model remains empty until a new reply is entered, then the message is rendered.
Reverting to v0.1.13 fixes the issue.

After entering a reply
step1

After entering one more reply
step2

The message not being renedered would be a response to:

ConversationChain(
        llm=ss.llm,
        verbose=True,
        memory=ConversationBufferWindowMemory(
            k=ss.configs['models']['llm'][model]['memory_window'],
            ai_prefix="Assistant",
            chat_memory=StreamlitChatMessageHistory(),
        ),
        prompt=llm_prompt,
    ).predict(
        input=input_text, callbacks=[StreamHandler(st.empty())]
    )

Ai Messages with Tool Calls do not include the text of Anthropic LLM responses.

The text portion of AI responses that contain tool calls are not included in the AIMessage object. This was using the Claude 3 models. I was able to confirm that the response from the server included two pieces of content as per the Anthropic documentation - a text object and a tool_use object. Additionally, the LLMInputOutputAdapter.prepare_output() which parses the server responses throws out the text content if there is more than one content object in the content list. This greatly impedes the ability to use chain-of-thoughts and agent architectures that rely on it (e.g. ReAct).

# langchain_aws/llms/bedrock.py
class LLMInputOutputAdapter:
    ...
    @classmethod
    def prepare_output(cls, provider: str, response: Any) -> dict:
        text = ""
        tool_calls = []
        response_body = json.loads(response.get("body").read().decode())

        if provider == "anthropic":
            if "completion" in response_body:
                text = response_body.get("completion")
            elif "content" in response_body:
                content = response_body.get("content")
                if len(content) == 1 and content[0]["type"] == "text":
                    text = content[0]["text"]
                elif any(block["type"] == "tool_use" for block in content):
                    tool_calls = extract_tool_calls(content)
        ...

Link to the line (at time of writing)

LLMInputOutputAdapter.prepare_output - List out of Index error

Hi, I'm currently using ChatBedrock(model_id = "anthropic.claude-3-sonnet-20240229-v1:0"). I'm implementing a reflection workflow for PII extraction, with 1 prompt for the extractor and 1 prompt for the reflector.
There are 3 invocations where the first invocation extracts PII from a resume, the second invocation critiques the output, followed by the third invocation refining the output.

Currently, I'm noticing a List out of Index error for several cases:

  • When I include pydantic format in the prompt
  • When I include this line in the 2nd prompt: If no modifications are necessary, respond with "Output looks correct. Please return the original output in the same format."
Screenshot 2024-04-26 at 3 59 29 PM

This error did not appear when I was using langchain.llms.Bedrock. I presume this error happens only for chat models, when the library is trying to prepare the output in the form of an AIMessage but failed to do so. Does anyone know how to resolve this issue?

Support for Claude v3 models

When I try to use this package with the model set to Claude v3 I get the following ValidationError exception

Claude v3 models are not supported by this LLM.Please use `from langchain_community.chat_models import BedrockChat` instead. (type=value_error)

My understanding is that the langchain-community bedrock integrations are deprecated and that the current implementation for bedrock is langchain-aws.

Are there any plans to support Claude v3?

botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the Converse operation: The model returned the following errors: Your API request included an `assistant` message in the final position, which would pre-fill the `assistant` response. When using tools, pre-filling the `assistant` response is not supported.

from langchain_core.messages import (
BaseMessage,
HumanMessage,
ToolMessage,
)
from inputs import description,links
import operator
from typing import Annotated, Sequence, TypedDict
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from tools.uber_suggest import search_seo_keywords
from tools.scraper import scrape
from tools.serper import get_serper_results
from langgraph.graph import END, StateGraph, START
from langgraph.prebuilt import ToolNode
from langchain_aws import ChatBedrock,ChatBedrockConverse
import os

llm = ChatBedrockConverse(
model_id="anthropic.claude-3-sonnet-20240229-v1:0",
temperature=0,
credentials_profile_name="bedrock",
region_name='us-west-2'
)

def create__generation_agent(llm, tools, system_message: str):
"""Create an agent."""
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful AI assistant, collaborating with other assistants."
" Use the provided tools to progress towards answering the question."
" If you are unable to fully answer, that's OK, another assistant with different tools "
" will help where you left off. Execute what you can to make progress."
" If you or any of the other assistants have the final answer or deliverable,"
" prefix your response with FINAL ANSWER so the team knows to stop."
" You have access to the following tools: {tool_names}.\n{system_message}",
),
MessagesPlaceholder(variable_name="messages"),
]
)
prompt = prompt.partial(system_message=system_message)
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
return prompt | llm.bind_tools(tools)

def create_reviewer_agent(llm, tools, system_message: str):
"""Create an agent."""
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful AI assistant, collaborating with other assistants."
" Use the provided tools to progress towards answering the question."
" If you are unable to fully answer, that's OK, another assistant with different tools "
" will help where you left off. Execute what you can to make progress."
" prefix your response with FINAL REVIEW or CONTINUE so the team knows to stop or continue."
" prefix will be FINAL REVIEW if nothing major to review."
" prefix will be CONTINUE if you have a critique"
" You have access to the following tools: {tool_names}.\n{system_message}",
),
MessagesPlaceholder(variable_name="messages"),
]
)
prompt = prompt.partial(system_message=system_message)
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
return prompt | llm.bind_tools(tools)

This defines the object that is passed between each node
in the graph. We will create different nodes for each agent and tool
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
sender: str

import functools

from langchain_core.messages import AIMessage

Helper function to create a node for a given agent
def agent_node(state, agent, name):
result = agent.invoke(state)

if result.tool_calls:
pass
else:
result = AIMessage(**result.dict(exclude={"type", "name"}), name=name)
return {
"messages": [result],
"sender": name,
}

Research agent and node
generator_agent = create__generation_agent(
llm,
tools,
system_message="""
""",
)
generator_node = functools.partial(agent_node, agent=generator_agent, name="generator")

chart_generator
reviewer_agent = create_reviewer_agent(
llm,
tools,
system_message="""""",
)
reviewer_node = functools.partial(agent_node, agent=reviewer_agent, name="reviewer")
tool_node = ToolNode(tools)

from typing import Literal

def router(state) -> Literal["call_tool", "end", "continue"]:

messages = state["messages"]
last_message = messages[-1]
if last_message.tool_calls:

return "call_tool"
if "FINAL ANSWER" in last_message.content:
return "continue"
elif "FINAL REVIEW" in last_message.content:
return "end"
else:
return "continue"

workflow = StateGraph(AgentState)

workflow.add_node("generator", generator_node)
workflow.add_node("reviewer", reviewer_node)
workflow.add_node("call_tool", tool_node)

workflow.add_conditional_edges(
"generator",
router,
{"continue": "reviewer", "call_tool": "call_tool", "end": END},
)
workflow.add_conditional_edges(
"reviewer",
router,
{"continue": "generator", "call_tool": "call_tool", "end": END},
)

workflow.add_conditional_edges(
"call_tool",

lambda x: x["sender"],
{
"generator": "generator",
"reviewer": "reviewer",
},
)
workflow.add_edge(START, "generator")
graph = workflow.compile()

events = graph.stream(
{
"description":description,
"links":links,
"messages": [
HumanMessage(
content=f"""""
)
],
},

{"recursion_limit": 150},
)
for s in events:
print(s)
print("----")

working for gpt models, gives error for Bedrock anthropic models.

Does ChatBedrock(claude-3) support returning images in ToolMessage?

Hi,
I am working on a project that is highly multimodal dependent.
Some of the implemented tools return images which are then fed into the chat model.
Here is the error I am getting when the image is added into the content using method described (with small changes described below) here

Exception has occurred: ValueError
Error raised by bedrock service: An error occurred (ValidationException) when calling the InvokeModel operation: messages.2.content.0.tool_result.content.1.image.source: Field required
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the InvokeModel operation: messages.2.content.0.tool_result.content.1.image.source: Field required

Here, the last message is the one causing problems

[HumanMultimodalMessage(content=[{'type': 'text', 'text': 'Hello. Can you please descr...? Remember to use the available tools.'}]), AIMessage(content='', additional_kwargs={'usage': {'prompt_tokens': 347, 'completion_...: 'toolu_bdrk_01Du8uhp4iKzvJNJc9BYyjgA'}]), ToolMessage(content=[{'type': 'text', 'text': 'Here is the image test.png'}, {'type':..._id='toolu_bdrk_01Du8uhp4iKzvJNJc9BYyjgA')]
0: HumanMultimodalMessage(content=[{'type': 'text', 'text': 'Hello. Can you please describe the contents of test.png image? Remember to use the available tools.'}])
1: AIMessage(content='', additional_kwargs={'usage': {'prompt_tokens': 347, 'completion_tokens': 56, 'total_tokens': 403}, 'stop_reason': 'tool_use', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, response_metadata={'usage': {'prompt_tokens': 347, 'completion_tokens': 56, 'total_tokens': 403}, 'stop_reason': 'tool_use', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, id='run-04b9ef27-1d6c-4ed8-9eb1-dc47cd3787ad-0', tool_calls=[{'name': 'GetImageTool', 'args': {'name': 'test.png'}, 'id': 'toolu_bdrk_01Du8uhp4iKzvJNJc9BYyjgA'}])
2: ToolMessage(content=[{'type': 'text', 'text': 'Here is the image test.png'}, {'type': 'image', 'image': {'source': 'data:image/png;base64,............')

I've changed the standard image dictionary from

{
    'type':'image_url',
    'image_url':'....'
}

to

{
    'type':'image',
    'source':'....'
}

as the previous errors hinted

Exception has occurred: ValueError
Error raised by bedrock service: An error occurred (ValidationException) when calling the InvokeModel operation: messages.2.content.0.tool_result.content.1: Input tag 'image_url' found using 'type' does not match any of the expected tags: 'text', 'image'

I've run into similar problem with ChatOpenAI:
OpenAI (or at least langchain_openai) explicitely does not support images returned in ToolMessage, so I am currently splitting the tool output into two messages (ToolMessage with a text content, and HumanMessage with image) with success.

The same workaround works with bedrock

[HumanMultimodalMessage(content=[{'type': 'text', 'text': 'Hello. Can you please descr...? Remember to use the available tools.'}]), AIMessage(content='', additional_kwargs={'usage': {'prompt_tokens': 347, 'completion_...: 'toolu_bdrk_01SLKQ2dznQy2Ac93WTZ8AFn'}]), ToolMultimodalMessage(content=[{'type': 'text', 'text': 'Here is the image test.png '..._id='toolu_bdrk_01SLKQ2dznQy2Ac93WTZ8AFn'), HumanMultimodalMessage(content=[{'type': 'text', 'text': 'Image returned by a tool ca...+QyuUwuk8sGqJ8AZeonWEsAAAAASUVORK5CYII=']), AIMessage(content='The image appears to show a simple black background with no other ...n-b580351e-a370-4f27-bfbc-7bfbbd5becc7-0')]
special variables:
function variables:
0: HumanMultimodalMessage(content=[{'type': 'text', 'text': 'Hello. Can you please describe the contents of test.png image? Remember to use the available tools.'}])
1: AIMessage(content='', additional_kwargs={'usage': {'prompt_tokens': 347, 'completion_tokens': 56, 'total_tokens': 403}, 'stop_reason': 'tool_use', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, response_metadata={'usage': {'prompt_tokens': 347, 'completion_tokens': 56, 'total_tokens': 403}, 'stop_reason': 'tool_use', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, id='run-433616ec-c1b6-4dee-b5d6-476bf8aa1072-0', tool_calls=[{'name': 'GetImageTool', 'args': {'name': 'test.png'}, 'id': 'toolu_bdrk_01SLKQ2dznQy2Ac93WTZ8AFn'}])
2: ToolMultimodalMessage(content=[{'type': 'text', 'text': 'Here is the image test.png '}], tool_call_id='toolu_bdrk_01SLKQ2dznQy2Ac93WTZ8AFn')
3: HumanMultimodalMessage(content=[{'type': 'text', 'text': 'Image returned by a tool call toolu_bdrk_01SLKQ2dznQy2Ac93WTZ8AFn'}, {'type': 'image_url', 'image_url': {'url': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAxUlEQVR4Ae3BAQEAAACCIP1/ugsOCOQyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8sGqJ8AZeonWEsAAAAASUVORK5CYII='}}], images=['iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAxUlEQVR4Ae3BAQEAAACCIP1/ugsOCOQyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8vkMrlMLpPL5DK5TC6Ty+QyuUwuk8sGqJ8AZeonWEsAAAAASUVORK5CYII='])
4: AIMessage(content='The image appears to show a simple black background with no other visible elements. It is a completely black square or rectangular image.', additional_kwargs={'usage': {'prompt_tokens': 479, 'completion_tokens': 28, 'total_tokens': 507}, 'stop_reason': 'end_turn', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, response_metadata={'usage': {'prompt_tokens': 479, 'completion_tokens': 28, 'total_tokens': 507}, 'stop_reason': 'end_turn', 'model_id': 'anthropic.claude-3-haiku-20240307-v1:0'}, id='run-b580351e-a370-4f27-bfbc-7bfbbd5becc7-0')

Support for Guardrails for Amazon Bedrock

Guardrails for Amazon Bedrock since GA has changed the payload when using boto3.
This is the new payload content:

response = client.invoke_model(
    body=b'bytes'|file,
    contentType='string',
    accept='string',
    modelId='string',
    trace='ENABLED'|'DISABLED',
    guardrailIdentifier='string',
    guardrailVersion='string'
)

Changes in BedrockBase are needed. I'll also create a PR

Wrong input key for Cohere models in BedrockLLM

I'm using the cohere.command-r-v1:0 model to test some prompts. After noticing that Cohere doesn't support Chat #33, I changed to BedrockLLM. However I keep getting the following message:

ValueError: Error raised by bedrock service: An error occurred (ValidationException) when calling the InvokeModel operation: Malformed input request: #: required key [message] not found#: extraneous key [prompt] is not permitted, please reformat your input and try again.

I have tried several methods including:

import boto3 

session = boto3.Session(region_name=region)
boto3_bedrock = session.client(service_name="bedrock-runtime")

llm = BedrockLLM(region_name="us-east-1", model_id="cohere.command-r-v1:0" client=boto3_bedrock, verbose=verbose)
llm.invoke("Hey") # Fails

# ----------------------------------

prompt = ChatPromptTemplate.from_messages(
        [
            ("system", "You are a greeting chatbot, greet users only"),
            ("human", "{greeting}"),
        ]
    )
chain = (prompt | llm)

chain.invoke({"greeting": "Hey"}) # Fails

After validating the error message and line 134 in libs/aws/langchain_aws/llm/bedrock I think it should be rewritten as:

@classmethod
def prepare_input(
    cls,
    provider: str,
    model_kwargs: Dict[str, Any],
    prompt: Optional[str] = None,
    system: Optional[str] = None,
    messages: Optional[List[Dict]] = None,
) -> Dict[str, Any]:
    input_body = {**model_kwargs}
    if provider == "anthropic":
        if messages:
            input_body["anthropic_version"] = "bedrock-2023-05-31"
            input_body["messages"] = messages
            if system:
                input_body["system"] = system
            if "max_tokens" not in input_body:
                input_body["max_tokens"] = 1024
        if prompt:
            input_body["prompt"] = _human_assistant_format(prompt)
            if "max_tokens_to_sample" not in input_body:
                input_body["max_tokens_to_sample"] = 1024
    elif provider == "cohere"):
        input_body["message"] = prompt
    elif provider in ("ai21", "meta", "mistral"):
        input_body["prompt"] = prompt
    elif provider == "amazon":
        input_body = dict()
        input_body["inputText"] = prompt
        input_body["textGenerationConfig"] = {**model_kwargs}
    else:
        input_body["inputText"] = prompt

    return input_body

Here are my dependencies:

langchain==0.1.19
langchain-aws==0.1.3

I'm using Python 3.11

ChatBedrock with Converse enabled returns consecutive user messages

When leveraging ChatBedrock with beta_use_converse_api and Langgraph, I am getting the following error:

ValidationException: An error occurred (ValidationException) when calling the Converse operation: A conversation must alternate between user and assistant roles. Make sure the conversation alternates between user and assistant roles and try again.

The tool nodes are defined using ToolNode from langgraph.prebuilt. The setup is otherwise similar to the one at the bottom of this example.

The setup runs without error with beta_use_converse_api=False. The only difference seems to be that consecutive user messages are merged.

Facing Issues While Adding Guardrail parameter to ChatbBedrock initializer.

langchain-aws version used: 0.1.4

Have tried all the below scenarios:

  1.  guardrails={ "trace": False,
                 "accept": "application/json",
                 "contentType": "application/json",
                 "modelId": model_id,
     "guardrailIdentifier": "identifier", --> "id" of the Guardrail I created using Bedrock Console.
     "guardrailVersion": "5"}
    
  2. guardrail_kwargs={"guardrailIdentifier": "identifier", "guardrailVersion": "5"}

  3. guardrails={ "trace": False,
    "guardrailIdentifier": "xxxx" --> the ARN of the guardrail I created using Bedrock Console.
    "guardrailVersion": "5"}

  4. guardrails={ "trace": False,
    "guardrailIdentifier": "identifier", --> "id" of the Guardrail I created using Bedrock Console.
    "guardrailVersion": "5"}

Adding the exception below:

  1. 2024-05-19 15:27:29,346 ERROR root ScriptRunner.scriptThread : Error Occured: Error raised by bedrock service: Parameter validation failed:
    Unknown parameter in input: "guardrailIdentifier", must be one of: body, contentType, accept, modelId
    Unknown parameter in input: "guardrailVersion", must be one of: body, contentType, accept, modelId

Tried the alternative as below:
guardrails={ "trace": False,
"id": "identifier", --> "id" of the Guardrail I created using Bedrock Console.
"version": "5"}

Got the exception as below:

Error Occured: Guardrails must be a dictionary with 'guardrailIdentifier' and 'guardrailVersion' keys.

The below option doesn't give any error but eventually has no relevance as the identifier value is None:

guardrails={ "trace": False,
"guardrailIdentifier": None
"guardrailVersion": "5"}

Please suggest the correct way of passing the guardrail parameter for it to work.

PS: I have tested the Guardrail from Bedrock Console and it works like a charm.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.