From 75663f2cae07ce9abc8627bec87fdd9783130bce Mon Sep 17 00:00:00 2001 From: Luis Lopez <144131610+LuisGoCity@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:19:26 +0100 Subject: [PATCH 01/19] community: Add cost per 1K tokens for fine-tuned model cached input (#29248) ### Description - Since there is no cost per 1k input tokens for a fine-tuned cached version of `gpt-4o-mini-2024-07-18` is not available when using the `OpenAICallbackHandler`, it raises an error when trying to make calls with such model. - To add the price in the `MODEL_COST_PER_1K_TOKENS` dictionary cc. @efriis --- libs/community/langchain_community/callbacks/openai_info.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/community/langchain_community/callbacks/openai_info.py b/libs/community/langchain_community/callbacks/openai_info.py index 5796065f1ee2c..5dee1e22ba3ee 100644 --- a/libs/community/langchain_community/callbacks/openai_info.py +++ b/libs/community/langchain_community/callbacks/openai_info.py @@ -128,6 +128,7 @@ "gpt-3.5-turbo-1106-finetuned": 0.003, "gpt-3.5-turbo-0125-finetuned": 0.003, "gpt-4o-mini-2024-07-18-finetuned": 0.0003, + "gpt-4o-mini-2024-07-18-finetuned-cached": 0.00015, # Fine Tuned output "babbage-002-finetuned-completion": 0.0016, "davinci-002-finetuned-completion": 0.012, From 595297e2e58967b4f083e3ad387f2c0628e42538 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Thu, 16 Jan 2025 14:43:42 -0800 Subject: [PATCH 02/19] core: Add support for calls in get_function_nonlocals (#29255) Thank you for contributing to LangChain! - [ ] **PR title**: "package: description" - Where "package" is whichever of langchain, community, core, etc. is being modified. Use "docs: ..." for purely docs changes, "infra: ..." for CI changes. - Example: "community: add foobar LLM" - [ ] **PR message**: ***Delete this entire checklist*** and replace with - **Description:** a description of the change - **Issue:** the issue # it fixes, if applicable - **Dependencies:** any dependencies required for this change - **Twitter handle:** if your PR gets announced, and you'd like a mention, we'll gladly shout you out! - [ ] **Add tests and docs**: If you're adding a new integration, please include 1. a test for the integration, preferably unit tests that do not rely on network access, 2. an example notebook showing its use. It lives in `docs/docs/integrations` directory. - [ ] **Lint and test**: Run `make format`, `make lint` and `make test` from the root of the package(s) you've modified. See contribution guidelines for more: https://python.langchain.com/docs/contributing/ Additional guidelines: - Make sure optional dependencies are imported within a function. - Please do not add dependencies to pyproject.toml files (even optional ones) unless they are required for unit tests. - Most PRs should not touch more than one package. - Changes should be backwards compatible. - If you are adding something to community, do not re-import it in langchain. If no one reviews your PR within a few days, please @-mention one of baskaryan, efriis, eyurtsev, ccurme, vbarda, hwchase17. --- libs/core/langchain_core/runnables/utils.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libs/core/langchain_core/runnables/utils.py b/libs/core/langchain_core/runnables/utils.py index a81ca7615a66f..89e3651dcce4c 100644 --- a/libs/core/langchain_core/runnables/utils.py +++ b/libs/core/langchain_core/runnables/utils.py @@ -271,6 +271,20 @@ def visit_Attribute(self, node: ast.Attribute) -> Any: if isinstance(parent, ast.Name): self.loads.add(parent.id + "." + attr_expr) self.loads.discard(parent.id) + elif isinstance(parent, ast.Call): + if isinstance(parent.func, ast.Name): + self.loads.add(parent.func.id) + else: + parent = parent.func + attr_expr = "" + while isinstance(parent, ast.Attribute): + if attr_expr: + attr_expr = parent.attr + "." + attr_expr + else: + attr_expr = parent.attr + parent = parent.value + if isinstance(parent, ast.Name): + self.loads.add(parent.id + "." + attr_expr) class FunctionNonLocals(ast.NodeVisitor): From d5360b9bd6bdcee4160e38ee668c53630ab3a509 Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 16 Jan 2025 17:52:37 -0500 Subject: [PATCH 03/19] core[patch]: release 0.3.30 (#29256) --- libs/core/poetry.lock | 6 +++--- libs/core/pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/core/poetry.lock b/libs/core/poetry.lock index eadfb8d460c70..17cafbf8ed334 100644 --- a/libs/core/poetry.lock +++ b/libs/core/poetry.lock @@ -1200,7 +1200,7 @@ files = [ [[package]] name = "langchain-tests" -version = "0.3.7" +version = "0.3.8" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -1225,7 +1225,7 @@ url = "../standard-tests" [[package]] name = "langchain-text-splitters" -version = "0.3.4" +version = "0.3.5" description = "LangChain text splitting utilities" optional = false python-versions = ">=3.9,<4.0" @@ -1233,7 +1233,7 @@ files = [] develop = true [package.dependencies] -langchain-core = "^0.3.26" +langchain-core = "^0.3.29" [package.source] type = "directory" diff --git a/libs/core/pyproject.toml b/libs/core/pyproject.toml index 65e40fafb09f8..489bda0580214 100644 --- a/libs/core/pyproject.toml +++ b/libs/core/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-core" -version = "0.3.29" +version = "0.3.30" description = "Building applications with LLMs through composability" authors = [] license = "MIT" From 36ff83a0b57ca6ea11d2ec80bf974b6c7323bb35 Mon Sep 17 00:00:00 2001 From: Michael Chin Date: Fri, 17 Jan 2025 06:06:17 -0800 Subject: [PATCH 04/19] docs: Message history for Neptune chains (#29260) Expanded the Amazon Neptune documentation with new sections detailing usage of chat message history with the `create_neptune_opencypher_qa_chain` and `create_neptune_sparql_qa_chain` functions. --- .../graphs/amazon_neptune_open_cypher.ipynb | 176 +++++++++++++++- .../graphs/amazon_neptune_sparql.ipynb | 197 ++++++++++++++++-- 2 files changed, 342 insertions(+), 31 deletions(-) diff --git a/docs/docs/integrations/graphs/amazon_neptune_open_cypher.ipynb b/docs/docs/integrations/graphs/amazon_neptune_open_cypher.ipynb index e25719a1b63c2..ba3084e160dc0 100644 --- a/docs/docs/integrations/graphs/amazon_neptune_open_cypher.ipynb +++ b/docs/docs/integrations/graphs/amazon_neptune_open_cypher.ipynb @@ -70,9 +70,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Austin airport has 98 outgoing routes.\n" + ] + } + ], "source": [ "from langchain_aws import ChatBedrockConverse\n", "from langchain_aws.chains import create_neptune_opencypher_qa_chain\n", @@ -83,13 +91,161 @@ " temperature=0,\n", ")\n", "\n", - "chain = create_neptune_opencypher_qa_chain(\n", - " llm=llm,\n", - " graph=graph,\n", - ")\n", + "chain = create_neptune_opencypher_qa_chain(llm=llm, graph=graph)\n", + "\n", + "result = chain.invoke(\"How many outgoing routes does the Austin airport have?\")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding Message History\n", + "\n", + "The Neptune openCypher QA chain has the ability to be wrapped by [`RunnableWithMessageHistory`](https://python.langchain.com/v0.2/api_reference/core/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html#langchain_core.runnables.history.RunnableWithMessageHistory). This adds message history to the chain, allowing us to create a chatbot that retains conversation state across multiple invocations.\n", + "\n", + "To start, we need a way to store and load the message history. For this purpose, each thread will be created as an instance of [`InMemoryChatMessageHistory`](https://python.langchain.com/api_reference/core/chat_history/langchain_core.chat_history.InMemoryChatMessageHistory.html), and stored into a dictionary for repeated access.\n", + "\n", + "(Also see: https://python.langchain.com/docs/versions/migrating_memory/chat_history/#chatmessagehistory)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.chat_history import InMemoryChatMessageHistory\n", + "\n", + "chats_by_session_id = {}\n", + "\n", + "\n", + "def get_chat_history(session_id: str) -> InMemoryChatMessageHistory:\n", + " chat_history = chats_by_session_id.get(session_id)\n", + " if chat_history is None:\n", + " chat_history = InMemoryChatMessageHistory()\n", + " chats_by_session_id[session_id] = chat_history\n", + " return chat_history" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, the QA chain and message history storage can be used to create the new `RunnableWithMessageHistory`. Note that we must set `query` as the input key to match the format expected by the base chain." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.runnables.history import RunnableWithMessageHistory\n", + "\n", + "runnable_with_history = RunnableWithMessageHistory(\n", + " chain,\n", + " get_chat_history,\n", + " input_messages_key=\"query\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before invoking the chain, a unique `session_id` needs to be generated for the conversation that the new `InMemoryChatMessageHistory` will remember." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", "\n", - "result = chain.invoke(\n", - " {\"query\": \"How many outgoing routes does the Austin airport have?\"}\n", + "session_id = uuid.uuid4()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, invoke the message history enabled chain with the `session_id`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You can fly directly to 98 destinations from Austin airport.\n" + ] + } + ], + "source": [ + "result = runnable_with_history.invoke(\n", + " {\"query\": \"How many destinations can I fly to directly from Austin airport?\"},\n", + " config={\"configurable\": {\"session_id\": session_id}},\n", + ")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As the chain continues to be invoked with the same `session_id`, responses will be returned in the context of previous queries in the conversation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You can fly directly to 4 destinations in Europe from Austin airport.\n" + ] + } + ], + "source": [ + "result = runnable_with_history.invoke(\n", + " {\"query\": \"Out of those destinations, how many are in Europe?\"},\n", + " config={\"configurable\": {\"session_id\": session_id}},\n", + ")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The four European destinations you can fly to directly from Austin airport are:\n", + "- AMS (Amsterdam Airport Schiphol)\n", + "- FRA (Frankfurt am Main)\n", + "- LGW (London Gatwick)\n", + "- LHR (London Heathrow)\n" + ] + } + ], + "source": [ + "result = runnable_with_history.invoke(\n", + " {\"query\": \"Give me the codes and names of those airports.\"},\n", + " config={\"configurable\": {\"session_id\": session_id}},\n", ")\n", "print(result[\"result\"].content)" ] @@ -97,7 +253,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -111,7 +267,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/docs/docs/integrations/graphs/amazon_neptune_sparql.ipynb b/docs/docs/integrations/graphs/amazon_neptune_sparql.ipynb index 3fb1811239895..17dd065067455 100644 --- a/docs/docs/integrations/graphs/amazon_neptune_sparql.ipynb +++ b/docs/docs/integrations/graphs/amazon_neptune_sparql.ipynb @@ -48,7 +48,7 @@ "\n", "Seed the W3C organizational data, W3C org ontology plus some instances. \n", " \n", - "You will need an S3 bucket in the same region and account. Set `STAGE_BUCKET`as the name of that bucket." + "You will need an S3 bucket in the same region and account as the Neptune cluster. Set `STAGE_BUCKET`as the name of that bucket." ] }, { @@ -60,11 +60,6 @@ "STAGE_BUCKET = \"\"" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": "" - }, { "cell_type": "code", "execution_count": null, @@ -89,7 +84,50 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Bulk-load the org ttl - both ontology and instances" + "We will use the `%load` magic command from the `graph-notebook` package to insert the W3C data into the Neptune graph. Before running `%load`, use `%%graph_notebook_config` to set the graph connection parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --upgrade --quiet graph-notebook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext graph_notebook.magics" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%graph_notebook_config\n", + "{\n", + " \"host\": \"\",\n", + " \"neptune_service\": \"neptune-db\",\n", + " \"port\": 8182,\n", + " \"auth_mode\": \"<[DEFAULT|IAM]>\",\n", + " \"load_from_s3_arn\": \"\",\n", + " \"ssl\": true,\n", + " \"aws_region\": \"\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Bulk-load the org ttl - both ontology and instances." ] }, { @@ -246,7 +284,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "### Create the Neptune Database RDF Graph" + "source": [ + "### Create the Neptune Database RDF Graph" + ] }, { "cell_type": "code", @@ -297,7 +337,7 @@ " examples=EXAMPLES,\n", ")\n", "\n", - "result = chain.invoke({\"query\": \"How many organizations are in the graph?\"})\n", + "result = chain.invoke(\"How many organizations are in the graph?\")\n", "print(result[\"result\"].content)" ] }, @@ -305,7 +345,6 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Extra questions\n", "Here are a few more prompts to try on the graph data that was ingested.\n" ] }, @@ -315,7 +354,8 @@ "metadata": {}, "outputs": [], "source": [ - "chain.invoke({\"query\": \"Are there any mergers or acquisitions?\"})" + "result = chain.invoke(\"Are there any mergers or acquisitions?\")\n", + "print(result[\"result\"].content)" ] }, { @@ -324,7 +364,8 @@ "metadata": {}, "outputs": [], "source": [ - "chain.invoke({\"query\": \"Find organizations.\"})" + "result = chain.invoke(\"Find organizations.\")\n", + "print(result[\"result\"].content)" ] }, { @@ -333,7 +374,8 @@ "metadata": {}, "outputs": [], "source": [ - "chain.invoke({\"query\": \"Find sites of MegaSystems or MegaFinancial.\"})" + "result = chain.invoke(\"Find sites of MegaSystems or MegaFinancial.\")\n", + "print(result[\"result\"].content)" ] }, { @@ -342,7 +384,43 @@ "metadata": {}, "outputs": [], "source": [ - "chain.invoke({\"query\": \"Find a member who is a manager of one or more members.\"})" + "result = chain.invoke(\"Find a member who is a manager of one or more members.\")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = chain.invoke(\"Find five members and their managers.\")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = chain.invoke(\n", + " \"Find org units or suborganizations of The Mega Group. What are the sites of those units?\"\n", + ")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding Message History\n", + "\n", + "The Neptune SPARQL QA chain has the ability to be wrapped by [`RunnableWithMessageHistory`](https://python.langchain.com/v0.2/api_reference/core/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html#langchain_core.runnables.history.RunnableWithMessageHistory). This adds message history to the chain, allowing us to create a chatbot that retains conversation state across multiple invocations.\n", + "\n", + "To start, we need a way to store and load the message history. For this purpose, each thread will be created as an instance of [`InMemoryChatMessageHistory`](https://python.langchain.com/api_reference/core/chat_history/langchain_core.chat_history.InMemoryChatMessageHistory.html), and stored into a dictionary for repeated access.\n", + "\n", + "(Also see: https://python.langchain.com/docs/versions/migrating_memory/chat_history/#chatmessagehistory)" ] }, { @@ -351,7 +429,24 @@ "metadata": {}, "outputs": [], "source": [ - "chain.invoke({\"query\": \"Find five members and their managers.\"})" + "from langchain_core.chat_history import InMemoryChatMessageHistory\n", + "\n", + "chats_by_session_id = {}\n", + "\n", + "\n", + "def get_chat_history(session_id: str) -> InMemoryChatMessageHistory:\n", + " chat_history = chats_by_session_id.get(session_id)\n", + " if chat_history is None:\n", + " chat_history = InMemoryChatMessageHistory()\n", + " chats_by_session_id[session_id] = chat_history\n", + " return chat_history" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, the QA chain and message history storage can be used to create the new `RunnableWithMessageHistory`. Note that we must set `query` as the input key to match the format expected by the base chain." ] }, { @@ -360,17 +455,77 @@ "metadata": {}, "outputs": [], "source": [ - "chain.invoke(\n", - " {\n", - " \"query\": \"Find org units or suborganizations of The Mega Group. What are the sites of those units?\"\n", - " }\n", + "from langchain_core.runnables.history import RunnableWithMessageHistory\n", + "\n", + "runnable_with_history = RunnableWithMessageHistory(\n", + " chain,\n", + " get_chat_history,\n", + " input_messages_key=\"query\",\n", ")" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before invoking the chain, a unique `session_id` needs to be generated for the conversation that the new `InMemoryChatMessageHistory` will remember.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "\n", + "session_id = uuid.uuid4()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, invoke the message history enabled chain with the `session_id`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = runnable_with_history.invoke(\n", + " {\"query\": \"How many org units or suborganizations does the The Mega Group have?\"},\n", + " config={\"configurable\": {\"session_id\": session_id}},\n", + ")\n", + "print(result[\"result\"].content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As the chain continues to be invoked with the same `session_id`, responses will be returned in the context of previous queries in the conversation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = runnable_with_history.invoke(\n", + " {\"query\": \"List the sites for each of the units.\"},\n", + " config={\"configurable\": {\"session_id\": session_id}},\n", + ")\n", + "print(result[\"result\"].content)" + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -384,7 +539,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.10.13" } }, "nbformat": 4, From f0226135e5632b4d29364159bc386009b16dbc91 Mon Sep 17 00:00:00 2001 From: Jun He Date: Fri, 17 Jan 2025 06:30:58 -0800 Subject: [PATCH 05/19] docs: Remove redundant "%" (#29205) Before this commit, the copied command can't be used directly. --- docs/docs/tutorials/classification.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tutorials/classification.ipynb b/docs/docs/tutorials/classification.ipynb index c869efc1524e5..b61ab56e3c38e 100644 --- a/docs/docs/tutorials/classification.ipynb +++ b/docs/docs/tutorials/classification.ipynb @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "%pip install --upgrade --quiet langchain-core" + "pip install --upgrade --quiet langchain-core" ] }, { From 97a5bc7fc72324d2ad9fee42ff7e0e522d0acd49 Mon Sep 17 00:00:00 2001 From: Zapiron <125368863+DangerousPotential@users.noreply.github.com> Date: Sat, 18 Jan 2025 00:17:40 +0800 Subject: [PATCH 06/19] docs: Fixed typos and improve metadata explanation (#29266) Fix mini typos and made the explanation of metadata filtering clearer --- docs/docs/concepts/vectorstores.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/concepts/vectorstores.mdx b/docs/docs/concepts/vectorstores.mdx index 1a909453744a9..3493d6b0d2bf1 100644 --- a/docs/docs/concepts/vectorstores.mdx +++ b/docs/docs/concepts/vectorstores.mdx @@ -151,10 +151,10 @@ Many vectorstores support [the `k`](/docs/integrations/vectorstores/pinecone/#qu ### Metadata filtering While vectorstore implement a search algorithm to efficiently search over *all* the embedded documents to find the most similar ones, many also support filtering on metadata. -This allows structured filters to reduce the size of the similarity search space. These two concepts work well together: +Metadata filtering helps narrow down the search by applying specific conditions such as retrieving documents from a particular source or date range. These two concepts work well together: -1. **Semantic search**: Query the unstructured data directly, often using via embedding or keyword similarity. -2. **Metadata search**: Apply structured query to the metadata, filering specific documents. +1. **Semantic search**: Query the unstructured data directly, often via embedding or keyword similarity. +2. **Metadata search**: Apply structured query to the metadata, filtering specific documents. Vector store support for metadata filtering is typically dependent on the underlying vector store implementation. From 628145b172216f944d9344c9c91b80c2d7475c81 Mon Sep 17 00:00:00 2001 From: Erick Friis Date: Fri, 17 Jan 2025 10:41:59 -0800 Subject: [PATCH 07/19] infra: fix api build (#29274) --- .github/scripts/prep_api_docs_build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/scripts/prep_api_docs_build.py b/.github/scripts/prep_api_docs_build.py index acc0c257680fb..0d17449a9a2ad 100644 --- a/.github/scripts/prep_api_docs_build.py +++ b/.github/scripts/prep_api_docs_build.py @@ -82,6 +82,12 @@ def main(): and p["repo"] != "langchain-ai/langchain" ]) + # Delete ones without a pyproject.toml + for partner in Path("langchain/libs/partners").iterdir(): + if partner.is_dir() and not (partner / "pyproject.toml").exists(): + print(f"Removing {partner} as it does not have a pyproject.toml") + shutil.rmtree(partner) + print("Library sync completed successfully!") except Exception as e: From c616b445f270f097c4c13e32691d11c54c6d7cb2 Mon Sep 17 00:00:00 2001 From: ccurme Date: Fri, 17 Jan 2025 14:41:41 -0500 Subject: [PATCH 08/19] anthropic[patch]: support `parallel_tool_calls` (#29257) Need to: - Update docs - Decide if this is an explicit kwarg of bind_tools - Decide if this should be in standard test with flag for supporting --- .../langchain_anthropic/chat_models.py | 18 ++++++++++++++++++ .../integration_tests/test_chat_models.py | 19 +++++++++++++++++++ .../chat_models/test_base.py | 12 ++++++++++++ 3 files changed, 49 insertions(+) diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index fd64b824a8df7..842d30ae00da3 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -819,6 +819,7 @@ def bind_tools( tool_choice: Optional[ Union[Dict[str, str], Literal["any", "auto"], str] ] = None, + parallel_tool_calls: Optional[bool] = None, **kwargs: Any, ) -> Runnable[LanguageModelInput, BaseMessage]: r"""Bind tool-like objects to this chat model. @@ -832,6 +833,10 @@ def bind_tools( - name of the tool as a string or as dict ``{"type": "tool", "name": "<>"}``: calls corresponding tool; - ``"auto"``, ``{"type: "auto"}``, or None: automatically selects a tool (including no tool); - ``"any"`` or ``{"type: "any"}``: force at least one tool to be called; + parallel_tool_calls: Set to ``False`` to disable parallel tool use. + Defaults to ``None`` (no specification, which allows parallel tool use). + + .. versionadded:: 0.3.2 kwargs: Any additional parameters are passed directly to :meth:`~langchain_anthropic.chat_models.ChatAnthropic.bind`. @@ -968,6 +973,19 @@ class GetPrice(BaseModel): f"Unrecognized 'tool_choice' type {tool_choice=}. Expected dict, " f"str, or None." ) + + if parallel_tool_calls is not None: + disable_parallel_tool_use = not parallel_tool_calls + if "tool_choice" in kwargs: + kwargs["tool_choice"]["disable_parallel_tool_use"] = ( + disable_parallel_tool_use + ) + else: + kwargs["tool_choice"] = { + "type": "any", + "disable_parallel_tool_use": disable_parallel_tool_use, + } + return self.bind(tools=formatted_tools, **kwargs) def with_structured_output( diff --git a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py index 9f2eaba45549d..3da7bb94f9cf8 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py @@ -444,6 +444,25 @@ def test_tool_use() -> None: assert len(chunks) > 1 +class GenerateUsername(BaseModel): + "Get a username based on someone's name and hair color." + + name: str + hair_color: str + + +def test_disable_parallel_tool_calling() -> None: + llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") + llm_with_tools = llm.bind_tools([GenerateUsername], parallel_tool_calls=False) + result = llm_with_tools.invoke( + "Use the GenerateUsername tool to generate user names for:\n\n" + "Sally with green hair\n" + "Bob with blue hair" + ) + assert isinstance(result, AIMessage) + assert len(result.tool_calls) == 1 + + def test_anthropic_with_empty_text_block() -> None: """Anthropic SDK can return an empty text block.""" diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_base.py b/libs/partners/openai/tests/integration_tests/chat_models/test_base.py index 49d894603d726..525020192ba0d 100644 --- a/libs/partners/openai/tests/integration_tests/chat_models/test_base.py +++ b/libs/partners/openai/tests/integration_tests/chat_models/test_base.py @@ -630,6 +630,18 @@ def test_bind_tools_tool_choice() -> None: assert not msg.tool_calls +def test_disable_parallel_tool_calling() -> None: + llm = ChatOpenAI(model="gpt-4o-mini") + llm_with_tools = llm.bind_tools([GenerateUsername], parallel_tool_calls=False) + result = llm_with_tools.invoke( + "Use the GenerateUsername tool to generate user names for:\n\n" + "Sally with green hair\n" + "Bob with blue hair" + ) + assert isinstance(result, AIMessage) + assert len(result.tool_calls) == 1 + + @pytest.mark.parametrize("model", ["gpt-4o-mini", "o1"]) def test_openai_structured_output(model: str) -> None: class MyModel(BaseModel): From ac520210972caecadacf58fa6df44d873da2aeb7 Mon Sep 17 00:00:00 2001 From: ccurme Date: Fri, 17 Jan 2025 14:48:31 -0500 Subject: [PATCH 09/19] anthropic[patch]: release 0.3.2 (#29275) --- libs/partners/anthropic/poetry.lock | 8 ++++---- libs/partners/anthropic/pyproject.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/partners/anthropic/poetry.lock b/libs/partners/anthropic/poetry.lock index 8dc1383dda9d3..5ebe86a8757a2 100644 --- a/libs/partners/anthropic/poetry.lock +++ b/libs/partners/anthropic/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -453,7 +453,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.27" +version = "0.3.30" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -478,7 +478,7 @@ url = "../../core" [[package]] name = "langchain-tests" -version = "0.3.7" +version = "0.3.8" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -1357,4 +1357,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "743255e1fe91bc0e2185a1bfe1b4c6bb13f6522affe5bb35bdfda419e93736ae" +content-hash = "882c131a2202eb925560eaf821003a5c0b007131ceb3954bbbf6b1722f756e97" diff --git a/libs/partners/anthropic/pyproject.toml b/libs/partners/anthropic/pyproject.toml index 645f6b5d604fa..a57fe6c4a6f35 100644 --- a/libs/partners/anthropic/pyproject.toml +++ b/libs/partners/anthropic/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-anthropic" -version = "0.3.1" +version = "0.3.2" description = "An integration package connecting AnthropicMessages and LangChain" authors = [] readme = "README.md" @@ -22,7 +22,7 @@ plugins = ['pydantic.mypy'] [tool.poetry.dependencies] python = ">=3.9,<4.0" anthropic = ">=0.41.0,<1" -langchain-core = "^0.3.27" +langchain-core = "^0.3.30" pydantic = "^2.7.4" [tool.ruff.lint] From 184ea8aeb2be68e184c5937f8d51df22cc8ff081 Mon Sep 17 00:00:00 2001 From: ccurme Date: Fri, 17 Jan 2025 15:26:33 -0500 Subject: [PATCH 10/19] anthropic[patch]: update tool choice type (#29276) --- libs/partners/anthropic/langchain_anthropic/chat_models.py | 2 +- libs/partners/anthropic/pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 842d30ae00da3..af7045d74aeb2 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -982,7 +982,7 @@ class GetPrice(BaseModel): ) else: kwargs["tool_choice"] = { - "type": "any", + "type": "auto", "disable_parallel_tool_use": disable_parallel_tool_use, } diff --git a/libs/partners/anthropic/pyproject.toml b/libs/partners/anthropic/pyproject.toml index a57fe6c4a6f35..16ceb2ba6b636 100644 --- a/libs/partners/anthropic/pyproject.toml +++ b/libs/partners/anthropic/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-anthropic" -version = "0.3.2" +version = "0.3.3" description = "An integration package connecting AnthropicMessages and LangChain" authors = [] readme = "README.md" From d4b9404fd69f53c60728b0d5c0f6535782aa6891 Mon Sep 17 00:00:00 2001 From: Amaan Date: Sat, 18 Jan 2025 05:32:28 +0530 Subject: [PATCH 11/19] docs: add langchain dappier tool integration notebook (#29265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tools to interact with Dappier APIs with an example notebook. For `DappierRealTimeSearchTool`, the tool can be invoked with: ```python from langchain_dappier import DappierRealTimeSearchTool tool = DappierRealTimeSearchTool() tool.invoke({"query": "What happened at the last wimbledon"}) ``` ``` At the last Wimbledon in 2024, Carlos Alcaraz won the title by defeating Novak Djokovic. This victory marked Alcaraz's fourth Grand Slam title at just 21 years old! πŸŽ‰πŸ†πŸŽΎ ``` For DappierAIRecommendationTool the tool can be invoked with: ```python from langchain_dappier import DappierAIRecommendationTool tool = DappierAIRecommendationTool( data_model_id="dm_01j0pb465keqmatq9k83dthx34", similarity_top_k=3, ref="sportsnaut.com", num_articles_ref=2, search_algorithm="most_recent", ) ``` ``` [{"author": "Matt Weaver", "image_url": "https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34...", "pubdate": "Fri, 17 Jan 2025 08:04:03 +0000", "source_url": "https://sportsnaut.com/chili-bowl-thursday-bell-column/", "summary": "The article highlights the thrilling unpredictability... ", "title": "Thursday proves why every lap of Chili Bowl..."}, {"author": "Matt Higgins", "image_url": "https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34...", "pubdate": "Fri, 17 Jan 2025 02:48:42 +0000", "source_url": "https://sportsnaut.com/new-york-mets-news-pete-alonso...", "summary": "The New York Mets are likely parting ways with star...", "title": "MLB insiders reveal New York Mets’ last-ditch..."}, {"author": "Jim Cerny", "image_url": "https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34...", "pubdate": "Fri, 17 Jan 2025 05:10:39 +0000", "source_url": "https://www.foreverblueshirts.com/new-york-rangers-news...", "summary": "The New York Rangers achieved a thrilling 5-3 comeback... ", "title": "Rangers score 3 times in 3rd period for stirring 5-3..."}] ``` The integration package can be found over here - https://github.com/DappierAI/langchain-dappier --- docs/docs/integrations/providers/dappier.mdx | 11 + .../integrations/retrievers/dappier.ipynb | 8 - docs/docs/integrations/tools/dappier.ipynb | 504 ++++++++++++++++++ 3 files changed, 515 insertions(+), 8 deletions(-) create mode 100644 docs/docs/integrations/tools/dappier.ipynb diff --git a/docs/docs/integrations/providers/dappier.mdx b/docs/docs/integrations/providers/dappier.mdx index 7e15d7b7289e9..2a1235984f106 100644 --- a/docs/docs/integrations/providers/dappier.mdx +++ b/docs/docs/integrations/providers/dappier.mdx @@ -46,3 +46,14 @@ See a [usage example](/docs/integrations/retrievers/dappier). ```python from langchain_dappier import DappierRetriever ``` + +## Tool + +See a [usage example](/docs/integrations/tools/dappier). + +```python +from langchain_dappier import ( + DappierRealTimeSearchTool, + DappierAIRecommendationTool +) +``` diff --git a/docs/docs/integrations/retrievers/dappier.ipynb b/docs/docs/integrations/retrievers/dappier.ipynb index 5ceeb25d0ab12..94cf86c8ca4ca 100644 --- a/docs/docs/integrations/retrievers/dappier.ipynb +++ b/docs/docs/integrations/retrievers/dappier.ipynb @@ -25,14 +25,6 @@ "\n", "This will help you getting started with the Dappier [retriever](https://python.langchain.com/docs/concepts/retrievers/). For detailed documentation of all DappierRetriever features and configurations head to the [API reference](https://python.langchain.com/en/latest/retrievers/langchain_dappier.retrievers.Dappier.DappierRetriever.html).\n", "\n", - "### Integration details\n", - "\n", - "Bring-your-own data (i.e., index and search a custom corpus of documents):\n", - "\n", - "| Retriever | Self-host | Cloud offering | Package |\n", - "| :--- | :--- | :---: | :---: |\n", - "[DappierRetriever](https://python.langchain.com/en/latest/retrievers/langchain_dappier.retrievers.Dappier.DappierRetriever.html) | ❌ | ❌ | langchain-dappier |\n", - "\n", "### Setup\n", "\n", "Install ``langchain-dappier`` and set environment variable ``DAPPIER_API_KEY``.\n", diff --git a/docs/docs/integrations/tools/dappier.ipynb b/docs/docs/integrations/tools/dappier.ipynb new file mode 100644 index 0000000000000..52ef3b5656783 --- /dev/null +++ b/docs/docs/integrations/tools/dappier.ipynb @@ -0,0 +1,504 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "BJB3NSYqUWl4" + }, + "source": [ + "# Dappier\n", + "\n", + "[Dappier](https://dappier.com) connects any LLM or your Agentic AI to real-time, rights-cleared, proprietary data from trusted sources, making your AI an expert in anything. Our specialized models include Real-Time Web Search, News, Sports, Financial Stock Market Data, Crypto Data, and exclusive content from premium publishers. Explore a wide range of data models in our marketplace at [marketplace.dappier.com](https://marketplace.dappier.com).\n", + "\n", + "[Dappier](https://dappier.com) delivers enriched, prompt-ready, and contextually relevant data strings, optimized for seamless integration with LangChain. Whether you're building conversational AI, recommendation engines, or intelligent search, Dappier's LLM-agnostic RAG models ensure your AI has access to verified, up-to-date dataβ€”without the complexity of building and managing your own retrieval pipeline." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MAbkvI8pUb7R" + }, + "source": [ + "# Dappier Tool\n", + "\n", + "This will help you getting started with the Dappier [tool](https://python.langchain.com/docs/concepts/tools/). For detailed documentation of all DappierRetriever features and configurations head to the [API reference](https://python.langchain.com/en/latest/tools/langchain_dappier.tools.Dappier.DappierRealTimeSearchTool.html).\n", + "\n", + "## Overview\n", + "\n", + "The DappierRealTimeSearchTool and DappierAIRecommendationTool empower AI applications with real-time data and AI-driven insights. The former provides access to up-to-date information across news, weather, travel, and financial markets, while the latter supercharges applications with factual, premium content from diverse domains like News, Finance, and Sports, all powered by Dappier's pre-trained RAG models and natural language APIs." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ouA6p-E-aF34" + }, + "source": [ + "### Setup\n", + "\n", + "This tool lives in the `langchain-dappier` package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tNhKB0BUaneq" + }, + "outputs": [], + "source": [ + "%pip install -qU langchain-dappier" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oBLVZpnoYshG" + }, + "source": [ + "### Credentials\n", + "\n", + "We also need to set our Dappier API credentials, which can be generated at the [Dappier site.](https://platform.dappier.com/profile/api-keys)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UrmBR_JyY9I6" + }, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "if not os.environ.get(\"DAPPIER_API_KEY\"):\n", + " os.environ[\"DAPPIER_API_KEY\"] = getpass.getpass(\"Dappier API key:\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x_3712yIXTGc" + }, + "source": [ + "If you want to get automated tracing from individual queries, you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "id": "S1Tuwpq-XVaX" + }, + "outputs": [], + "source": [ + "# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")\n", + "# os.environ[\"LANGSMITH_TRACING\"] = \"true\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IgPgQ12wcA4i" + }, + "source": [ + "## DappierRealTimeSearchTool\n", + "\n", + "Access real-time Google search results, including the latest news, weather, travel, and deals, along with up-to-date financial news, stock prices, and trades from polygon.io, all powered by AI insights to keep you informed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W8QBSmDvbL69" + }, + "source": [ + "### Instantiation\n", + "\n", + "- ai_model_id: str\n", + " The AI model ID to use for the query. The AI model ID always starts\n", + " with the prefix \"am_\".\n", + "\n", + " Defaults to \"am_01j06ytn18ejftedz6dyhz2b15\".\n", + "\n", + " Multiple AI model IDs are available, which can be found at:\n", + " https://marketplace.dappier.com/marketplace" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "tw1edqrLbiJ4" + }, + "outputs": [], + "source": [ + "from langchain_dappier import DappierRealTimeSearchTool\n", + "\n", + "tool = DappierRealTimeSearchTool(\n", + " # ai_model_id=\"...\", # overwrite default ai_model_id\n", + " # name=\"...\", # overwrite default tool name\n", + " # description=\"...\", # overwrite default tool description\n", + " # args_schema=..., # overwrite default args_schema: BaseModel\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nTatJ6F8b0sV" + }, + "source": [ + "### Invocation\n", + "\n", + "#### [Invoke directly with args](/docs/concepts/tools)\n", + "\n", + "The `DappierRealTimeSearchTool` takes a single \"query\" argument, which should be a natural language query:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 53 + }, + "id": "ASCcnvUCdIvz", + "outputId": "91538fac-f515-4a8e-adb6-0a7aa42f704c" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "\"At the last Wimbledon in 2024, Carlos Alcaraz won the title by defeating Novak Djokovic. This victory marked Alcaraz's fourth Grand Slam title at just 21 years old! πŸŽ‰πŸ†πŸŽΎ\"" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tool.invoke({\"query\": \"What happened at the last wimbledon\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Jcbi44TRdL3D" + }, + "source": [ + "### [Invoke with ToolCall](/docs/concepts/tools)\n", + "\n", + "We can also invoke the tool with a model-generated ToolCall, in which case a ToolMessage will be returned:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FCTpjujpdQst", + "outputId": "e184c25b-0089-4896-fbb4-1fbe09ea2f6b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Euro 2024 is being hosted by Germany! πŸ‡©πŸ‡ͺ The tournament runs from June 14 to July 14, 2024, featuring 24 teams competing across various cities like Berlin and Munich. It's going to be an exciting summer of football! βš½οΈπŸ†\n" + ] + } + ], + "source": [ + "# This is usually generated by a model, but we'll create a tool call directly for demo purposes.\n", + "model_generated_tool_call = {\n", + " \"args\": {\"query\": \"euro 2024 host nation\"},\n", + " \"id\": \"1\",\n", + " \"name\": \"dappier\",\n", + " \"type\": \"tool_call\",\n", + "}\n", + "tool_msg = tool.invoke(model_generated_tool_call)\n", + "\n", + "# The content is a JSON string of results\n", + "print(tool_msg.content[:400])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PTBiq_2JdfjK" + }, + "source": [ + "### Chaining\n", + "\n", + "We can use our tool in a chain by first binding it to a [tool-calling model](/docs/how_to/tool_calling/) and then calling it:\n", + "\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "_XImV9NtdoJq" + }, + "outputs": [], + "source": [ + "# | output: false\n", + "# | echo: false\n", + "\n", + "# !pip install -qU langchain langchain-openai\n", + "from langchain.chat_models import init_chat_model\n", + "\n", + "llm = init_chat_model(model=\"gpt-4o\", model_provider=\"openai\", temperature=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "i5P5DgJOdwPI", + "outputId": "70e14f71-637e-422d-80ac-62e93b3686a9" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content=\"Barbora KrejčíkovΓ‘ won the women's singles title at Wimbledon 2024, defeating Jasmine Paolini in the final with a score of 6–2, 2–6, 6–4. This victory marked her first Wimbledon singles title and her second major singles title overall! πŸŽ‰πŸ†πŸŽΎ\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 69, 'prompt_tokens': 222, 'total_tokens': 291, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_4691090a87', 'finish_reason': 'stop', 'logprobs': None}, id='run-87a385dd-103b-4344-a3be-2d6fd1dcfdf5-0', usage_metadata={'input_tokens': 222, 'output_tokens': 69, 'total_tokens': 291, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import datetime\n", + "\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableConfig, chain\n", + "\n", + "today = datetime.datetime.today().strftime(\"%D\")\n", + "prompt = ChatPromptTemplate(\n", + " [\n", + " (\"system\", f\"You are a helpful assistant. The date today is {today}.\"),\n", + " (\"human\", \"{user_input}\"),\n", + " (\"placeholder\", \"{messages}\"),\n", + " ]\n", + ")\n", + "\n", + "# specifying tool_choice will force the model to call this tool.\n", + "llm_with_tools = llm.bind_tools([tool])\n", + "\n", + "llm_chain = prompt | llm_with_tools\n", + "\n", + "\n", + "@chain\n", + "def tool_chain(user_input: str, config: RunnableConfig):\n", + " input_ = {\"user_input\": user_input}\n", + " ai_msg = llm_chain.invoke(input_, config=config)\n", + " tool_msgs = tool.batch(ai_msg.tool_calls, config=config)\n", + " return llm_chain.invoke({**input_, \"messages\": [ai_msg, *tool_msgs]}, config=config)\n", + "\n", + "\n", + "tool_chain.invoke(\"who won the last womens singles wimbledon\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TycbUKZsfRQy" + }, + "source": [ + "## DappierAIRecommendationTool\n", + "\n", + "Supercharge your AI applications with Dappier's pre-trained RAG models and natural language APIs, delivering factual and up-to-date responses from premium content providers across verticals like News, Finance, Sports, Weather, and more." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x1XfwHzHfvUN" + }, + "source": [ + "### Instantiation\n", + "\n", + "- data_model_id: str \n", + " The data model ID to use for recommendations. Data model IDs always start with the prefix \"dm_\". Defaults to \"dm_01j0pb465keqmatq9k83dthx34\". \n", + " Multiple data model IDs are available, which can be found at [Dappier marketplace](https://marketplace.dappier.com/marketplace). \n", + "\n", + "- similarity_top_k: int \n", + " The number of top documents to retrieve based on similarity. Defaults to \"9\". \n", + "\n", + "- ref: Optional[str]\n", + " The site domain where AI recommendations should be displayed. Defaults to \"None\". \n", + "\n", + "- num_articles_ref: int\n", + " The minimum number of articles to return from the specified reference domain (\"ref\"). The remaining articles will come from other sites in the RAG model. Defaults to \"0\". \n", + "\n", + "- search_algorithm: Literal[\"most_recent\", \"semantic\", \"most_recent_semantic\", \"trending\"]\n", + " The search algorithm to use for retrieving articles. Defaults to \"most_recent\". " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "id": "-t9rS-TBhNss" + }, + "outputs": [], + "source": [ + "from langchain_dappier import DappierAIRecommendationTool\n", + "\n", + "tool = DappierAIRecommendationTool(\n", + " data_model_id=\"dm_01j0pb465keqmatq9k83dthx34\",\n", + " similarity_top_k=3,\n", + " ref=\"sportsnaut.com\",\n", + " num_articles_ref=2,\n", + " search_algorithm=\"most_recent\",\n", + " # name=\"...\", # overwrite default tool name\n", + " # description=\"...\", # overwrite default tool description\n", + " # args_schema=..., # overwrite default args_schema: BaseModel\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ad3anWusg9BI" + }, + "source": [ + "### Invocation\n", + "\n", + "#### [Invoke directly with args](/docs/concepts/tools)\n", + "\n", + "The `DappierAIRecommendationTool` takes a single \"query\" argument, which should be a natural language query:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UQ08UkTMhI17", + "outputId": "5fd145b8-a547-4caa-ba06-ab0bfac3b104" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'author': 'Matt Weaver',\n", + " 'image_url': 'https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34/Screenshot_20250117_021643_Gallery_.jpg?width=428&height=321',\n", + " 'pubdate': 'Fri, 17 Jan 2025 08:04:03 +0000',\n", + " 'source_url': 'https://sportsnaut.com/chili-bowl-thursday-bell-column/',\n", + " 'summary': \"The article highlights the thrilling unpredictability of the Chili Bowl Midget Nationals, focusing on the dramatic shifts in fortune for drivers like Christopher Bell, Tanner Thorson, and Karter Sarff during Thursday's events. Key moments included Sarff's unfortunate pull-off and a last-lap crash that allowed Ryan Bernal to capitalize and improve his standing, showcasing the chaotic nature of the race and the importance of strategy and luck.\\n\\nAs the competition intensifies leading up to Championship Saturday, Bell faces the challenge of racing from a Last Chance Race, reflecting on the excitement and difficulties of the sport. The article emphasizes the emotional highs and lows experienced by racers, with insights from Bell and Bernal on the unpredictable nature of racing. Overall, it captures the camaraderie and passion that define the Chili Bowl, illustrating how each moment contributes to the event's narrative.\",\n", + " 'title': 'Thursday proves why every lap of Chili Bowl is so consequential'},\n", + " {'author': 'Matt Higgins',\n", + " 'image_url': 'https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34/Pete-Alonso-24524027_.jpg?width=428&height=321',\n", + " 'pubdate': 'Fri, 17 Jan 2025 02:48:42 +0000',\n", + " 'source_url': 'https://sportsnaut.com/new-york-mets-news-pete-alonso-rejected-last-ditch-contract-offer/',\n", + " 'summary': \"The New York Mets are likely parting ways with star first baseman Pete Alonso after failing to finalize a contract agreement. Alonso rejected a last-minute three-year offer worth between $68 and $70 million, leading the Mets to redirect funds towards acquiring a top reliever. With Alonso's free-agent options dwindling, speculation arises about his potential signing with another team for the 2025 season, while the Mets plan to shift Mark Vientos to first base.\\n\\nIn a strategic move, the Mets are also considering a trade for Toronto Blue Jays' star first baseman Vladimir Guerrero Jr. This potential acquisition aims to enhance the Mets' competitiveness as they reshape their roster. Guerrero's impressive offensive stats make him a valuable target, and discussions are in the early stages. Fans and analysts are keenly watching the situation, as a trade involving such a prominent player could significantly impact both teams.\",\n", + " 'title': 'MLB insiders reveal New York Mets’ last-ditch contract offer that Pete Alonso rejected'},\n", + " {'author': 'Jim Cerny',\n", + " 'image_url': 'https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34/NHL-New-York-Rangers-at-Utah-25204492_.jpg?width=428&height=321',\n", + " 'pubdate': 'Fri, 17 Jan 2025 05:10:39 +0000',\n", + " 'source_url': 'https://www.foreverblueshirts.com/new-york-rangers-news/stirring-5-3-comeback-win-utah-close-road-trip/',\n", + " 'summary': \"The New York Rangers achieved a thrilling 5-3 comeback victory against the Utah Hockey Club, showcasing their resilience after a prior overtime loss. The Rangers scored three unanswered goals in the third period, with key contributions from Reilly Smith, Chris Kreider, and Artemi Panarin, who sealed the win with an empty-net goal. This victory marked their first win of the season when trailing after two periods and capped off a successful road trip, improving their record to 21-20-3.\\n\\nIgor Shesterkin's strong performance in goal, along with Arthur Kaliyev's first goal for the team, helped the Rangers overcome an early deficit. The game featured multiple lead changes, highlighting the competitive nature of both teams. As the Rangers prepare for their next game against the Columbus Blue Jackets, they aim to close the gap in the playoff race, with the Blue Jackets currently holding a five-point lead in the Eastern Conference standings.\",\n", + " 'title': 'Rangers score 3 times in 3rd period for stirring 5-3 comeback win against Utah to close road trip'}]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tool.invoke({\"query\": \"latest sports news\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dzorKILbiOyy" + }, + "source": [ + "### [Invoke with ToolCall](/docs/concepts/tools)\n", + "\n", + "We can also invoke the tool with a model-generated ToolCall, in which case a ToolMessage will be returned:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wUu-awo0iP3P", + "outputId": "af1a9679-06ae-4432-a49f-769330c1e32f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{\"author\": \"Matt Johnson\", \"image_url\": \"https://images.dappier.com/dm_01j0pb465keqmatq9k83dthx34/MLB-New-York-Mets-at-Colorado-Rockies-23948644_.jpg?width=428&height=321\", \"pubdate\": \"Fri, 17 Jan 2025 13:31:02 +0000\", \"source_url\": \"https://sportsnaut.com/new-york-mets-rumors-vladimir-guerrero-jr-news/\", \"summary\": \"The New York Mets are refocusing their strategy after failing to extend a contra\n" + ] + } + ], + "source": [ + "# This is usually generated by a model, but we'll create a tool call directly for demo purposes.\n", + "model_generated_tool_call = {\n", + " \"args\": {\"query\": \"top 3 news articles\"},\n", + " \"id\": \"1\",\n", + " \"name\": \"dappier\",\n", + " \"type\": \"tool_call\",\n", + "}\n", + "tool_msg = tool.invoke(model_generated_tool_call)\n", + "\n", + "# The content is a JSON string of results\n", + "print(tool_msg.content[:400])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "quFltDcDd2T8" + }, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all DappierRealTimeSearchTool features and configurations head to the [API reference](https://python.langchain.com/api_reference/community/tools/langchain_dappier.tools.dappier.tool.DappierRealTimeSearchTool.html)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From 1cd4d8d101a807f1bd064b0b372d132de31687f6 Mon Sep 17 00:00:00 2001 From: TheSongg <145535169+TheSongg@users.noreply.github.com> Date: Sat, 18 Jan 2025 09:31:59 +0800 Subject: [PATCH 12/19] [langchain_community.llms.xinference]: Rewrite _stream() method and support stream() method in xinference.py (#29259) - [ ] **PR title**:[langchain_community.llms.xinference]: Rewrite _stream() method and support stream() method in xinference.py - [ ] **PR message**: Rewrite the _stream method so that the chain.stream() can be used to return data streams. chain = prompt | llm chain.stream(input=user_input) - [ ] **tests**: from langchain_community.llms import Xinference from langchain.prompts import PromptTemplate llm = Xinference( server_url="http://0.0.0.0:9997", # replace your xinference server url model_uid={model_uid} # replace model_uid with the model UID return from launching the model stream = True ) prompt = PromptTemplate(input=['country'], template="Q: where can we visit in the capital of {country}? A:") chain = prompt | llm chain.stream(input={'country': 'France'}) --- .../langchain_community/llms/xinference.py | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/libs/community/langchain_community/llms/xinference.py b/libs/community/langchain_community/llms/xinference.py index ada9f8b1f1084..06bc1c9b30e62 100644 --- a/libs/community/langchain_community/llms/xinference.py +++ b/libs/community/langchain_community/llms/xinference.py @@ -1,7 +1,20 @@ -from typing import TYPE_CHECKING, Any, Dict, Generator, List, Mapping, Optional, Union +from __future__ import annotations + +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Generator, + Iterator, + List, + Mapping, + Optional, + Union, +) from langchain_core.callbacks import CallbackManagerForLLMRun from langchain_core.language_models.llms import LLM +from langchain_core.outputs import GenerationChunk if TYPE_CHECKING: from xinference.client import RESTfulChatModelHandle, RESTfulGenerateModelHandle @@ -73,6 +86,26 @@ class Xinference(LLM): generate_config={"max_tokens": 1024, "stream": True}, ) + Example: + + .. code-block:: python + + from langchain_community.llms import Xinference + from langchain.prompts import PromptTemplate + + llm = Xinference( + server_url="http://0.0.0.0:9997", + model_uid={model_uid}, # replace model_uid with the model UID return from launching the model + stream=True + ) + prompt = PromptTemplate( + input=['country'], + template="Q: where can we visit in the capital of {country}? A:" + ) + chain = prompt | llm + chain.stream(input={'country': 'France'}) + + To view all the supported builtin models, run: .. code-block:: bash @@ -216,3 +249,59 @@ def _stream_generate( token=token, verbose=self.verbose, log_probs=log_probs ) yield token + + def _stream( + self, + prompt: str, + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> Iterator[GenerationChunk]: + generate_config = kwargs.get("generate_config", {}) + generate_config = {**self.model_kwargs, **generate_config} + if stop: + generate_config["stop"] = stop + for stream_resp in self._create_generate_stream(prompt, generate_config): + if stream_resp: + chunk = self._stream_response_to_generation_chunk(stream_resp) + if run_manager: + run_manager.on_llm_new_token( + chunk.text, + verbose=self.verbose, + ) + yield chunk + + def _create_generate_stream( + self, prompt: str, generate_config: Optional[Dict[str, List[str]]] = None + ) -> Iterator[str]: + if self.client is None: + raise ValueError("Client is not initialized!") + model = self.client.get_model(self.model_uid) + yield from model.generate(prompt=prompt, generate_config=generate_config) + + @staticmethod + def _stream_response_to_generation_chunk( + stream_response: str, + ) -> GenerationChunk: + """Convert a stream response to a generation chunk.""" + token = "" + if isinstance(stream_response, dict): + choices = stream_response.get("choices", []) + if choices: + choice = choices[0] + if isinstance(choice, dict): + token = choice.get("text", "") + + return GenerationChunk( + text=token, + generation_info=dict( + finish_reason=choice.get("finish_reason", None), + logprobs=choice.get("logprobs", None), + ), + ) + else: + raise TypeError("choice type error!") + else: + return GenerationChunk(text=token) + else: + raise TypeError("stream_response type error!") From 2fb6fd7950782ac3b7c1adf2e5f84613a42cbd20 Mon Sep 17 00:00:00 2001 From: Vadim Rusin Date: Sat, 18 Jan 2025 16:17:34 -0500 Subject: [PATCH 13/19] docs: fix broken link in JSONOutputParser reference (#29292) ### PR message: - **Description:** Fixed a broken link in the documentation for the `JSONOutputParser`. Updated the link to point to the correct reference: From: `https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.json.JSONOutputParser.html` To: `https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.json.JsonOutputParser.html`. This ensures accurate navigation for users referring to the `JsonOutputParser` documentation. - **Issue:** N/A - **Dependencies:** None - **Twitter handle:** N/A ### Add tests and docs: Not applicable; this change only affects documentation. ### Lint and test: Ran `make format`, `make lint`, and `make test` to ensure no issues. --- docs/docs/concepts/output_parsers.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/concepts/output_parsers.mdx b/docs/docs/concepts/output_parsers.mdx index f2cf62c04713f..254b3bcf3fc1f 100644 --- a/docs/docs/concepts/output_parsers.mdx +++ b/docs/docs/concepts/output_parsers.mdx @@ -27,7 +27,7 @@ LangChain has lots of different types of output parsers. This is a list of outpu | Name | Supports Streaming | Has Format Instructions | Calls LLM | Input Type | Output Type | Description | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|-------------------------|-----------|--------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [Str](https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.string.StrOutputParser.html) | βœ… | | | `str` \| `Message` | String | Parses texts from message objects. Useful for handling variable formats of message content (e.g., extracting text from content blocks). | -| [JSON](https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.json.JSONOutputParser.html#langchain_core.output_parsers.json.JSONOutputParser) | βœ… | βœ… | | `str` \| `Message` | JSON object | Returns a JSON object as specified. You can specify a Pydantic model and it will return JSON for that model. Probably the most reliable output parser for getting structured data that does NOT use function calling. | +| [JSON](https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.json.JsonOutputParser.html) | βœ… | βœ… | | `str` \| `Message` | JSON object | Returns a JSON object as specified. You can specify a Pydantic model and it will return JSON for that model. Probably the most reliable output parser for getting structured data that does NOT use function calling. | | [XML](https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.xml.XMLOutputParser.html#langchain_core.output_parsers.xml.XMLOutputParser) | βœ… | βœ… | | `str` \| `Message` | `dict` | Returns a dictionary of tags. Use when XML output is needed. Use with models that are good at writing XML (like Anthropic's). | | [CSV](https://python.langchain.com/api_reference/core/output_parsers/langchain_core.output_parsers.list.CommaSeparatedListOutputParser.html#langchain_core.output_parsers.list.CommaSeparatedListOutputParser) | βœ… | βœ… | | `str` \| `Message` | `List[str]` | Returns a list of comma separated values. | | [OutputFixing](https://python.langchain.com/api_reference/langchain/output_parsers/langchain.output_parsers.fix.OutputFixingParser.html#langchain.output_parsers.fix.OutputFixingParser) | | | βœ… | `str` \| `Message` | | Wraps another output parser. If that output parser errors, then this will pass the error message and the bad output to an LLM and ask it to fix the output. | From 8fad9214c7597572c982aa0b5bfa87ec9bc399d1 Mon Sep 17 00:00:00 2001 From: Farzad Sharif Date: Sat, 18 Jan 2025 16:24:31 -0500 Subject: [PATCH 14/19] docs: fix qa_per_user.ipynb (#29290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description The `config` option was not passed to `configurable_retriever.invoke()`. Screenshot below. Fixed. Screenshot 2025-01-18 at 11 59 28β€―AM --- docs/docs/how_to/qa_per_user.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how_to/qa_per_user.ipynb b/docs/docs/how_to/qa_per_user.ipynb index acead6ed5910c..20dc9f0ee0038 100644 --- a/docs/docs/how_to/qa_per_user.ipynb +++ b/docs/docs/how_to/qa_per_user.ipynb @@ -228,7 +228,7 @@ "# highlight-next-line\n", "def retrieve(state: State, config: RunnableConfig):\n", " # highlight-next-line\n", - " retrieved_docs = configurable_retriever.invoke(state[\"question\"])\n", + " retrieved_docs = configurable_retriever.invoke(state[\"question\"], config)\n", " return {\"context\": retrieved_docs}\n", "\n", "\n", From e9abe583b2efe80b4c38f4b7548a473bcca4dde5 Mon Sep 17 00:00:00 2001 From: ThomasSaulou <54414107+ThomasSaulou@users.noreply.github.com> Date: Sat, 18 Jan 2025 23:31:10 +0100 Subject: [PATCH 15/19] chatperplexity stream-citations in additional kwargs (#29273) chatperplexity stream-citations in additional kwargs --------- Co-authored-by: Chester Curme --- .../chat_models/perplexity.py | 6 ++ .../unit_tests/chat_models/test_perplexity.py | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/libs/community/langchain_community/chat_models/perplexity.py b/libs/community/langchain_community/chat_models/perplexity.py index f5a58def0e9d2..d2decb38df93b 100644 --- a/libs/community/langchain_community/chat_models/perplexity.py +++ b/libs/community/langchain_community/chat_models/perplexity.py @@ -223,15 +223,21 @@ def _stream( stream_resp = self.client.chat.completions.create( messages=message_dicts, stream=True, **params ) + first_chunk = True for chunk in stream_resp: if not isinstance(chunk, dict): chunk = chunk.dict() if len(chunk["choices"]) == 0: continue choice = chunk["choices"][0] + citations = chunk.get("citations", []) + chunk = self._convert_delta_to_message_chunk( choice["delta"], default_chunk_class ) + if first_chunk: + chunk.additional_kwargs |= {"citations": citations} + first_chunk = False finish_reason = choice.get("finish_reason") generation_info = ( dict(finish_reason=finish_reason) if finish_reason is not None else None diff --git a/libs/community/tests/unit_tests/chat_models/test_perplexity.py b/libs/community/tests/unit_tests/chat_models/test_perplexity.py index 024274f7e84bc..d4991de422a36 100644 --- a/libs/community/tests/unit_tests/chat_models/test_perplexity.py +++ b/libs/community/tests/unit_tests/chat_models/test_perplexity.py @@ -1,8 +1,12 @@ """Test Perplexity Chat API wrapper.""" import os +from typing import Any, Dict, List, Optional +from unittest.mock import MagicMock import pytest +from langchain_core.messages import AIMessageChunk, BaseMessageChunk +from pytest_mock import MockerFixture from langchain_community.chat_models import ChatPerplexity @@ -40,3 +44,58 @@ def test_perplexity_initialization() -> None: ]: assert model.request_timeout == 1 assert model.pplx_api_key == "test" + + +@pytest.mark.requires("openai") +def test_perplexity_stream_includes_citations(mocker: MockerFixture) -> None: + """Test that the stream method includes citations in the additional_kwargs.""" + llm = ChatPerplexity( + model="test", + timeout=30, + verbose=True, + ) + mock_chunk_0 = { + "choices": [ + { + "delta": { + "content": "Hello ", + }, + "finish_reason": None, + } + ], + "citations": ["example.com", "example2.com"], + } + mock_chunk_1 = { + "choices": [ + { + "delta": { + "content": "Perplexity", + }, + "finish_reason": None, + } + ], + "citations": ["example.com", "example2.com"], + } + mock_chunks: List[Dict[str, Any]] = [mock_chunk_0, mock_chunk_1] + mock_stream = MagicMock() + mock_stream.__iter__.return_value = mock_chunks + patcher = mocker.patch.object( + llm.client.chat.completions, "create", return_value=mock_stream + ) + stream = llm.stream("Hello langchain") + full: Optional[BaseMessageChunk] = None + for i, chunk in enumerate(stream): + full = chunk if full is None else full + chunk + assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"] + if i == 0: + assert chunk.additional_kwargs["citations"] == [ + "example.com", + "example2.com", + ] + else: + assert "citations" not in chunk.additional_kwargs + assert isinstance(full, AIMessageChunk) + assert full.content == "Hello Perplexity" + assert full.additional_kwargs == {"citations": ["example.com", "example2.com"]} + + patcher.assert_called_once() From 6b249a0dc24a486fb9dac76e1de7c7847933c3d6 Mon Sep 17 00:00:00 2001 From: ccurme Date: Sun, 19 Jan 2025 12:04:00 -0500 Subject: [PATCH 16/19] openai[patch]: release 0.3.1 (#29301) --- libs/partners/openai/poetry.lock | 6 +++--- libs/partners/openai/pyproject.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/partners/openai/poetry.lock b/libs/partners/openai/poetry.lock index 85b65dbf801a6..41b8b1e48f15b 100644 --- a/libs/partners/openai/poetry.lock +++ b/libs/partners/openai/poetry.lock @@ -496,7 +496,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.29" +version = "0.3.30" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -521,7 +521,7 @@ url = "../../core" [[package]] name = "langchain-tests" -version = "0.3.7" +version = "0.3.8" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -1647,4 +1647,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "0bc715ae349e68aa13cce7541210fb9596a6a66a9a5479fdc5c891c79ca11688" +content-hash = "7b96bca67aad51e85bdceb9b491b834bc036aa20da766b999d2c3e2df8e64ae1" diff --git a/libs/partners/openai/pyproject.toml b/libs/partners/openai/pyproject.toml index 77ef40180e498..8f80837ac36f5 100644 --- a/libs/partners/openai/pyproject.toml +++ b/libs/partners/openai/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-openai" -version = "0.3.0" +version = "0.3.1" description = "An integration package connecting OpenAI and LangChain" authors = [] readme = "README.md" @@ -23,7 +23,7 @@ ignore_missing_imports = true [tool.poetry.dependencies] python = ">=3.9,<4.0" -langchain-core = "^0.3.29" +langchain-core = "^0.3.30" openai = "^1.58.1" tiktoken = ">=0.7,<1" From c20f7418c76e99db72706af0a6ca11cfcf7f7b09 Mon Sep 17 00:00:00 2001 From: ccurme Date: Sun, 19 Jan 2025 12:25:42 -0500 Subject: [PATCH 17/19] openai[patch]: fix Azure LLM test (#29302) The tokens I get are: ``` ['', '\n\n', 'The', ' sun', ' was', ' setting', ' over', ' the', ' horizon', ',', ' casting', ''] ``` so possibly an extra empty token is included in the output. lmk @efriis if we should look into this further. --- .../openai/tests/integration_tests/llms/test_azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/partners/openai/tests/integration_tests/llms/test_azure.py b/libs/partners/openai/tests/integration_tests/llms/test_azure.py index 4ae9d2900d4a3..fcbc6b3b0aafa 100644 --- a/libs/partners/openai/tests/integration_tests/llms/test_azure.py +++ b/libs/partners/openai/tests/integration_tests/llms/test_azure.py @@ -148,7 +148,7 @@ def test_openai_streaming_callback() -> None: verbose=True, ) llm.invoke("Write me a sentence with 100 words.") - assert callback_handler.llm_streams == 11 + assert callback_handler.llm_streams == 12 @pytest.mark.scheduled @@ -171,5 +171,5 @@ async def test_openai_async_streaming_callback() -> None: verbose=True, ) result = await llm.agenerate(["Write me a sentence with 100 words."]) - assert callback_handler.llm_streams == 11 + assert callback_handler.llm_streams == 12 assert isinstance(result, LLMResult) From b5fbebb3c8ef59c6ec4534fb84d9735f3540d75b Mon Sep 17 00:00:00 2001 From: Mohammad Mohtashim <45242107+keenborder786@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:15:02 +0500 Subject: [PATCH 18/19] (Community): Changing the BaseURL and Model for MiniMax (#29299) - **Description:** Changed the Base Default Model and Base URL to correct versions. Plus added a more explicit exception if user provides an invalid API Key - **Issue:** #29278 --- .../langchain_community/chat_models/minimax.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/community/langchain_community/chat_models/minimax.py b/libs/community/langchain_community/chat_models/minimax.py index b4a06e45d33be..8329a7d46515b 100644 --- a/libs/community/langchain_community/chat_models/minimax.py +++ b/libs/community/langchain_community/chat_models/minimax.py @@ -370,7 +370,7 @@ def _default_params(self) -> Dict[str, Any]: } _client: Any = None - model: str = "abab6.5-chat" + model: str = "abab6.5s-chat" """Model name to use.""" max_tokens: int = 256 """Denotes the number of tokens to predict per generation.""" @@ -381,7 +381,7 @@ def _default_params(self) -> Dict[str, Any]: model_kwargs: Dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" minimax_api_host: str = Field( - default="https://api.minimax.chat/v1/text/chatcompletion_v2", alias="base_url" + default="https://api.minimaxi.chat/v1/text/chatcompletion_v2", alias="base_url" ) minimax_group_id: Optional[str] = Field(default=None, alias="group_id") """[DEPRECATED, keeping it for for backward compatibility] Group Id""" @@ -511,7 +511,13 @@ def _generate( with httpx.Client(headers=headers, timeout=60) as client: response = client.post(self.minimax_api_host, json=payload) response.raise_for_status() - + final_response = response.json() + if ( + "base_resp" in final_response + and "status_msg" in final_response["base_resp"] + and final_response["base_resp"]["status_msg"] == "invalid api key" + ): + raise Exception("Invalid API Key Provided") return self._create_chat_result(response.json()) def _stream( From 6c52378992f1b6b07cc9ac36dc45e881f430c85b Mon Sep 17 00:00:00 2001 From: Hemant Rawat <99639231+rawathemant246@users.noreply.github.com> Date: Mon, 20 Jan 2025 01:07:21 +0530 Subject: [PATCH 19/19] Add Google-style docstring linting and update pyproject.toml (#29303) ### Description: This PR introduces Google-style docstring linting for the ModelLaboratory class in libs/langchain/langchain/model_laboratory.py. It also updates the pyproject.toml file to comply with the latest Ruff configuration standards (deprecating top-level lint settings in favor of lint). ### Changes include: - [x] Added detailed Google-style docstrings to all methods in ModelLaboratory. - [x] Updated pyproject.toml to move select and pydocstyle settings under the [tool.ruff.lint] section. - [x] Ensured all files pass Ruff linting. Issue: Closes #25154 ### Dependencies: No additional dependencies are required for this change. ### Checklist - [x] Files passes ruff linting. - [x] Docstrings conform to the Google-style convention. - [x] pyproject.toml updated to avoid deprecation warnings. - [x] My PR is ready to review, please review. --- libs/langchain/langchain/model_laboratory.py | 27 +++++++++++++++----- pyproject.toml | 6 +++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/libs/langchain/langchain/model_laboratory.py b/libs/langchain/langchain/model_laboratory.py index 114994535d8a8..914721470cebb 100644 --- a/libs/langchain/langchain/model_laboratory.py +++ b/libs/langchain/langchain/model_laboratory.py @@ -13,13 +13,23 @@ class ModelLaboratory: - """Experiment with different models.""" + """A utility to experiment with and compare the performance of different models.""" def __init__(self, chains: Sequence[Chain], names: Optional[List[str]] = None): - """Initialize with chains to experiment with. + """Initialize the ModelLaboratory with chains to experiment with. Args: - chains: list of chains to experiment with. + chains (Sequence[Chain]): A sequence of chains to experiment with. + Each chain must have exactly one input and one output variable. + names (Optional[List[str]]): Optional list of names corresponding to each chain. + If provided, its length must match the number of chains. + + + Raises: + ValueError: If any chain is not an instance of `Chain`. + ValueError: If a chain does not have exactly one input variable. + ValueError: If a chain does not have exactly one output variable. + ValueError: If the length of `names` does not match the number of chains. """ for chain in chains: if not isinstance(chain, Chain): @@ -50,12 +60,15 @@ def __init__(self, chains: Sequence[Chain], names: Optional[List[str]] = None): def from_llms( cls, llms: List[BaseLLM], prompt: Optional[PromptTemplate] = None ) -> ModelLaboratory: - """Initialize with LLMs to experiment with and optional prompt. + """Initialize the ModelLaboratory with LLMs and an optional prompt. Args: - llms: list of LLMs to experiment with - prompt: Optional prompt to use to prompt the LLMs. Defaults to None. - If a prompt was provided, it should only have one input variable. + llms (List[BaseLLM]): A list of LLMs to experiment with. + prompt (Optional[PromptTemplate]): An optional prompt to use with the LLMs. + If provided, the prompt must contain exactly one input variable. + + Returns: + ModelLaboratory: An instance of `ModelLaboratory` initialized with LLMs. """ if prompt is None: prompt = PromptTemplate(input_variables=["_input"], template="{_input}") diff --git a/pyproject.toml b/pyproject.toml index 0466cad3b8ed8..fab1f2eaf6405 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,13 +68,19 @@ extend-exclude = [ "docs/docs/expression_language/why.ipynb", # TODO: look into why linter errors ] +[tool.ruff.lint] +select = ["D"] +pydocstyle = { convention = "google" } + [tool.ruff.lint.per-file-ignores] "**/{cookbook,docs}/*" = [ "E402", # allow imports to appear anywhere in docs "F401", # allow "imported but unused" example code "F811", # allow re-importing the same module, so that cells can stay independent "F841", # allow assignments to variables that are never read -- it's example code + ] +"!libs/langchain/langchain/model_laboratory.py"=["D"] # These files were failing the listed rules at the time ruff was adopted for notebooks. # Don't require them to change at once, though we should look into them eventually.