diff --git a/docs/core_docs/.gitignore b/docs/core_docs/.gitignore index 099af2f2d642..8f648f622a5e 100644 --- a/docs/core_docs/.gitignore +++ b/docs/core_docs/.gitignore @@ -218,14 +218,28 @@ docs/how_to/agent_executor.md docs/how_to/agent_executor.mdx docs/concepts/t.md docs/concepts/t.mdx +docs/troubleshooting/errors/INVALID_TOOL_RESULTS.md +docs/troubleshooting/errors/INVALID_TOOL_RESULTS.mdx docs/versions/migrating_memory/conversation_summary_memory.md docs/versions/migrating_memory/conversation_summary_memory.mdx docs/versions/migrating_memory/conversation_buffer_window_memory.md docs/versions/migrating_memory/conversation_buffer_window_memory.mdx docs/versions/migrating_memory/chat_history.md docs/versions/migrating_memory/chat_history.mdx -docs/troubleshooting/errors/INVALID_TOOL_RESULTS.md -docs/troubleshooting/errors/INVALID_TOOL_RESULTS.mdx +docs/integrations/tools/tavily_search.md +docs/integrations/tools/tavily_search.mdx +docs/integrations/tools/serpapi.md +docs/integrations/tools/serpapi.mdx +docs/integrations/tools/exa_search.md +docs/integrations/tools/exa_search.mdx +docs/integrations/tools/duckduckgo_search.md +docs/integrations/tools/duckduckgo_search.mdx +docs/integrations/toolkits/vectorstore.md +docs/integrations/toolkits/vectorstore.mdx +docs/integrations/toolkits/sql.md +docs/integrations/toolkits/sql.mdx +docs/integrations/toolkits/openapi.md +docs/integrations/toolkits/openapi.mdx docs/integrations/vectorstores/weaviate.md docs/integrations/vectorstores/weaviate.mdx docs/integrations/vectorstores/upstash.md @@ -252,22 +266,24 @@ docs/integrations/vectorstores/elasticsearch.md docs/integrations/vectorstores/elasticsearch.mdx docs/integrations/vectorstores/chroma.md docs/integrations/vectorstores/chroma.mdx -docs/integrations/tools/tavily_search.md -docs/integrations/tools/tavily_search.mdx -docs/integrations/tools/serpapi.md -docs/integrations/tools/serpapi.mdx -docs/integrations/tools/exa_search.md -docs/integrations/tools/exa_search.mdx -docs/integrations/tools/duckduckgo_search.md -docs/integrations/tools/duckduckgo_search.mdx -docs/integrations/toolkits/vectorstore.md -docs/integrations/toolkits/vectorstore.mdx -docs/integrations/toolkits/sql.md -docs/integrations/toolkits/sql.mdx -docs/integrations/toolkits/openapi.md -docs/integrations/toolkits/openapi.mdx +docs/integrations/stores/in_memory.md +docs/integrations/stores/in_memory.mdx +docs/integrations/stores/file_system.md +docs/integrations/stores/file_system.mdx +docs/integrations/retrievers/tavily.md +docs/integrations/retrievers/tavily.mdx +docs/integrations/retrievers/kendra-retriever.md +docs/integrations/retrievers/kendra-retriever.mdx +docs/integrations/retrievers/exa.md +docs/integrations/retrievers/exa.mdx +docs/integrations/retrievers/bm25.md +docs/integrations/retrievers/bm25.mdx +docs/integrations/retrievers/bedrock-knowledge-bases.md +docs/integrations/retrievers/bedrock-knowledge-bases.mdx docs/integrations/text_embedding/togetherai.md docs/integrations/text_embedding/togetherai.mdx +docs/integrations/text_embedding/pinecone.md +docs/integrations/text_embedding/pinecone.mdx docs/integrations/text_embedding/openai.md docs/integrations/text_embedding/openai.mdx docs/integrations/text_embedding/ollama.md @@ -290,20 +306,6 @@ docs/integrations/text_embedding/bedrock.md docs/integrations/text_embedding/bedrock.mdx docs/integrations/text_embedding/azure_openai.md docs/integrations/text_embedding/azure_openai.mdx -docs/integrations/stores/in_memory.md -docs/integrations/stores/in_memory.mdx -docs/integrations/stores/file_system.md -docs/integrations/stores/file_system.mdx -docs/integrations/retrievers/tavily.md -docs/integrations/retrievers/tavily.mdx -docs/integrations/retrievers/kendra-retriever.md -docs/integrations/retrievers/kendra-retriever.mdx -docs/integrations/retrievers/exa.md -docs/integrations/retrievers/exa.mdx -docs/integrations/retrievers/bm25.md -docs/integrations/retrievers/bm25.mdx -docs/integrations/retrievers/bedrock-knowledge-bases.md -docs/integrations/retrievers/bedrock-knowledge-bases.mdx docs/integrations/llms/together.md docs/integrations/llms/together.mdx docs/integrations/llms/openai.md @@ -328,6 +330,10 @@ docs/integrations/llms/azure.md docs/integrations/llms/azure.mdx docs/integrations/llms/arcjet.md docs/integrations/llms/arcjet.mdx +docs/integrations/document_compressors/ibm.md +docs/integrations/document_compressors/ibm.mdx +docs/integrations/chat/xai.md +docs/integrations/chat/xai.mdx docs/integrations/chat/togetherai.md docs/integrations/chat/togetherai.mdx docs/integrations/chat/openai.md @@ -376,6 +382,16 @@ docs/integrations/retrievers/self_query/hnswlib.md docs/integrations/retrievers/self_query/hnswlib.mdx docs/integrations/retrievers/self_query/chroma.md docs/integrations/retrievers/self_query/chroma.mdx +docs/integrations/document_loaders/file_loaders/unstructured.md +docs/integrations/document_loaders/file_loaders/unstructured.mdx +docs/integrations/document_loaders/file_loaders/text.md +docs/integrations/document_loaders/file_loaders/text.mdx +docs/integrations/document_loaders/file_loaders/pdf.md +docs/integrations/document_loaders/file_loaders/pdf.mdx +docs/integrations/document_loaders/file_loaders/directory.md +docs/integrations/document_loaders/file_loaders/directory.mdx +docs/integrations/document_loaders/file_loaders/csv.md +docs/integrations/document_loaders/file_loaders/csv.mdx docs/integrations/document_loaders/web_loaders/web_puppeteer.md docs/integrations/document_loaders/web_loaders/web_puppeteer.mdx docs/integrations/document_loaders/web_loaders/web_cheerio.md @@ -387,14 +403,4 @@ docs/integrations/document_loaders/web_loaders/pdf.mdx docs/integrations/document_loaders/web_loaders/langsmith.md docs/integrations/document_loaders/web_loaders/langsmith.mdx docs/integrations/document_loaders/web_loaders/firecrawl.md -docs/integrations/document_loaders/web_loaders/firecrawl.mdx -docs/integrations/document_loaders/file_loaders/unstructured.md -docs/integrations/document_loaders/file_loaders/unstructured.mdx -docs/integrations/document_loaders/file_loaders/text.md -docs/integrations/document_loaders/file_loaders/text.mdx -docs/integrations/document_loaders/file_loaders/pdf.md -docs/integrations/document_loaders/file_loaders/pdf.mdx -docs/integrations/document_loaders/file_loaders/directory.md -docs/integrations/document_loaders/file_loaders/directory.mdx -docs/integrations/document_loaders/file_loaders/csv.md -docs/integrations/document_loaders/file_loaders/csv.mdx \ No newline at end of file +docs/integrations/document_loaders/web_loaders/firecrawl.mdx \ No newline at end of file diff --git a/docs/core_docs/docs/integrations/document_compressors/ibm.ipynb b/docs/core_docs/docs/integrations/document_compressors/ibm.ipynb new file mode 100644 index 000000000000..9645a4e126a3 --- /dev/null +++ b/docs/core_docs/docs/integrations/document_compressors/ibm.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "afaf8039", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "sidebar_label: IBM watsonx.ai\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "e49f1e0d", + "metadata": {}, + "source": [ + "# IBM watsonx.ai\n", + "\n", + "## Overview\n", + "\n", + "This will help you getting started with the [Watsonx document compressor](/docs/concepts/#document_compressors). For detailed documentation of all Watsonx document compressor features and configurations head to the [API reference]https://api.js.langchain.com/modules/_langchain_community.document_compressors_ibm.WatsonxRerank.html).\n", + "\n", + "### Integration details\n", + "\n", + "| Class | Package | [PY support](https://python.langchain.com/docs/integrations/llms/ibm_watsonx/) | Package downloads | Package latest |\n", + "| :--- | :--- | :---: | :---: | :---: |\n", + "| [`WatsonxRerank`](https://api.js.langchain.com/modules/_langchain_community.document_compressors_ibm.WatsonxRerank.html) | [@langchain/community](https://www.npmjs.com/package/@langchain/community) | ✅ | ![NPM - Downloads](https://img.shields.io/npm/dm/@langchain/community?style=flat-square&label=%20&) | ![NPM - Version](https://img.shields.io/npm/v/@langchain/community?style=flat-square&label=%20&) |\n", + "\n", + "## Setup\n", + "\n", + "To access IBM WatsonxAI models you'll need to create an IBM watsonx.ai account, get an API key or any other type of credentials, and install the `@langchain/community` integration package.\n", + "\n", + "### Credentials\n", + "\n", + "Head to [IBM Cloud](https://cloud.ibm.com/login) to sign up to IBM watsonx.ai and generate an API key or provide any other authentication form as presented below.\n", + "\n", + "#### IAM authentication\n", + "\n", + "```bash\n", + "export WATSONX_AI_AUTH_TYPE=iam\n", + "export WATSONX_AI_APIKEY=\n", + "```\n", + "\n", + "#### Bearer token authentication\n", + "\n", + "```bash\n", + "export WATSONX_AI_AUTH_TYPE=bearertoken\n", + "export WATSONX_AI_BEARER_TOKEN=\n", + "```\n", + "\n", + "#### CP4D authentication\n", + "\n", + "```bash\n", + "export WATSONX_AI_AUTH_TYPE=cp4d\n", + "export WATSONX_AI_USERNAME=\n", + "export WATSONX_AI_PASSWORD=\n", + "export WATSONX_AI_URL=\n", + "```\n", + "\n", + "Once these are placed in your environment variables and object is initialized authentication will proceed automatically.\n", + "\n", + "Authentication can also be accomplished by passing these values as parameters to a new instance.\n", + "\n", + "## IAM authentication\n", + "\n", + "```typescript\n", + "import { WatsonxLLM } from \"@langchain/community/llms/ibm\";\n", + "\n", + "const props = {\n", + " version: \"YYYY-MM-DD\",\n", + " serviceUrl: \"\",\n", + " projectId: \"\",\n", + " watsonxAIAuthType: \"iam\",\n", + " watsonxAIApikey: \"\",\n", + "};\n", + "const instance = new WatsonxLLM(props);\n", + "```\n", + "\n", + "## Bearer token authentication\n", + "\n", + "```typescript\n", + "import { WatsonxLLM } from \"@langchain/community/llms/ibm\";\n", + "\n", + "const props = {\n", + " version: \"YYYY-MM-DD\",\n", + " serviceUrl: \"\",\n", + " projectId: \"\",\n", + " watsonxAIAuthType: \"bearertoken\",\n", + " watsonxAIBearerToken: \"\",\n", + "};\n", + "const instance = new WatsonxLLM(props);\n", + "```\n", + "\n", + "### CP4D authentication\n", + "\n", + "```typescript\n", + "import { WatsonxLLM } from \"@langchain/community/llms/ibm\";\n", + "\n", + "const props = {\n", + " version: \"YYYY-MM-DD\",\n", + " serviceUrl: \"\",\n", + " projectId: \"\",\n", + " watsonxAIAuthType: \"cp4d\",\n", + " watsonxAIUsername: \"\",\n", + " watsonxAIPassword: \"\",\n", + " watsonxAIUrl: \"\",\n", + "};\n", + "const instance = new WatsonxLLM(props);\n", + "```\n", + "\n", + "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:\n", + "\n", + "```typescript\n", + "// process.env.LANGSMITH_API_KEY = \"\";\n", + "// process.env.LANGSMITH_TRACING = \"true\";\n", + "```\n", + "\n", + "### Installation\n", + "\n", + "This document compressor lives in the `@langchain/community` package:\n", + "\n", + "```{=mdx}\n", + "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n", + "import Npm2Yarn from \"@theme/Npm2Yarn\";\n", + "\n", + "\n", + "\n", + "\n", + " @langchain/community @langchain/core\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "a38cde65-254d-4219-a441-068766c0d4b5", + "metadata": {}, + "source": [ + "## Instantiation\n", + "\n", + "Now we can instantiate our compressor:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70cc8e65-2a02-408a-bbc6-8ef649057d82", + "metadata": {}, + "outputs": [], + "source": [ + "import { WatsonxRerank } from \"@langchain/community/document_compressors/ibm\";\n", + "\n", + "const watsonxRerank = new WatsonxRerank({\n", + " version: \"2024-05-31\",\n", + " serviceUrl: process.env.WATSONX_AI_SERVICE_URL,\n", + " projectId: process.env.WATSONX_AI_PROJECT_ID,\n", + " model: \"ibm/slate-125m-english-rtrvr\",\n", + "});" + ] + }, + { + "cell_type": "markdown", + "id": "5c5f2839-4020-424e-9fc9-07777eede442", + "metadata": {}, + "source": [ + "## Usage" + ] + }, + { + "cell_type": "markdown", + "id": "b195b0c7", + "metadata": {}, + "source": [ + "First, set up a basic RAG ingest pipeline with embeddings, a text splitter and a vector store. We'll use this to and rerank some documents regarding the selected query:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "51a60dbe-9f2e-4e04-bb62-23968f17164a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\n", + " Document {\n", + " pageContent: 'And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.',\n", + " metadata: { loc: [Object] },\n", + " id: undefined\n", + " },\n", + " Document {\n", + " pageContent: 'I spoke with their families and told them that we are forever in debt for their sacrifice, and we will carry on their mission to restore the trust and safety every community deserves. \\n' +\n", + " '\\n' +\n", + " 'I’ve worked on these issues a long time. \\n' +\n", + " '\\n' +\n", + " 'I know what works: Investing in crime preventionand community police officers who’ll walk the beat, who’ll know the neighborhood, and who can restore trust and safety.',\n", + " metadata: { loc: [Object] },\n", + " id: undefined\n", + " },\n", + " Document {\n", + " pageContent: 'We are the only nation on Earth that has always turned every crisis we have faced into an opportunity. \\n' +\n", + " '\\n' +\n", + " 'The only nation that can be defined by a single word: possibilities. \\n' +\n", + " '\\n' +\n", + " 'So on this night, in our 245th year as a nation, I have come to report on the State of the Union. \\n' +\n", + " '\\n' +\n", + " 'And my report is this: the State of the Union is strong—because you, the American people, are strong.',\n", + " metadata: { loc: [Object] },\n", + " id: undefined\n", + " },\n", + " Document {\n", + " pageContent: 'And I’m taking robust action to make sure the pain of our sanctions is targeted at Russia’s economy. And I will use every tool at our disposal to protect American businesses and consumers. \\n' +\n", + " '\\n' +\n", + " 'Tonight, I can announce that the United States has worked with 30 other countries to release 60 Million barrels of oil from reserves around the world.',\n", + " metadata: { loc: [Object] },\n", + " id: undefined\n", + " }\n", + "]\n" + ] + } + ], + "source": [ + "import { readFileSync } from \"node:fs\";\n", + "import { MemoryVectorStore } from \"langchain/vectorstores/memory\";\n", + "import { WatsonxEmbeddings } from \"@langchain/community/embeddings/ibm\";\n", + "import { CharacterTextSplitter } from \"@langchain/textsplitters\";\n", + "\n", + "const embeddings = new WatsonxEmbeddings({\n", + " version: \"YYYY-MM-DD\",\n", + " serviceUrl: process.env.API_URL,\n", + " projectId: \"\",\n", + " spaceId: \"\",\n", + " model: \"ibm/slate-125m-english-rtrvr\",\n", + "});\n", + "\n", + "const textSplitter = new CharacterTextSplitter({\n", + " chunkSize: 400,\n", + " chunkOverlap: 0,\n", + "});\n", + " \n", + "const query = \"What did the president say about Ketanji Brown Jackson\";\n", + "const text = readFileSync(\"state_of_the_union.txt\", \"utf8\");\n", + "\n", + "const docs = await textSplitter.createDocuments([text]);\n", + "const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);\n", + "const vectorStoreRetriever = vectorStore.asRetriever();\n", + "\n", + "const result = await vectorStoreRetriever.invoke(query);\n", + "console.log(result);" + ] + }, + { + "cell_type": "markdown", + "id": "b13ebf96", + "metadata": {}, + "source": [ + "Pass selected documents to rerank and recive specific score for each" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "fad30397", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\n", + " { index: 0, relevanceScore: 0.726995587348938 },\n", + " { index: 1, relevanceScore: 0.5758284330368042 },\n", + " { index: 2, relevanceScore: 0.5479092597961426 },\n", + " { index: 3, relevanceScore: 0.5468723773956299 }\n", + "]\n" + ] + } + ], + "source": [ + "import { WatsonxRerank } from \"@langchain/community/document_compressors/ibm\";\n", + "\n", + "const reranker = new WatsonxRerank({\n", + " version: \"2024-05-31\",\n", + " serviceUrl: process.env.WATSONX_AI_SERVICE_URL,\n", + " projectId: process.env.WATSONX_AI_PROJECT_ID,\n", + " model: \"ibm/slate-125m-english-rtrvr\",\n", + "});\n", + "const compressed = await reranker.rerank(result, query);\n", + "console.log(compressed);" + ] + }, + { + "cell_type": "markdown", + "id": "55f4ec18", + "metadata": {}, + "source": [ + "Or else you could have the documents returned with the result, for that use .compressDocuments() method as below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6cc39ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\n", + " Document {\n", + " pageContent: 'And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.',\n", + " metadata: { loc: [Object], relevanceScore: 0.726995587348938 },\n", + " id: undefined\n", + " },\n", + " Document {\n", + " pageContent: 'I spoke with their families and told them that we are forever in debt for their sacrifice, and we will carry on their mission to restore the trust and safety every community deserves. \\n' +\n", + " '\\n' +\n", + " 'I’ve worked on these issues a long time. \\n' +\n", + " '\\n' +\n", + " 'I know what works: Investing in crime preventionand community police officers who’ll walk the beat, who’ll know the neighborhood, and who can restore trust and safety.',\n", + " metadata: { loc: [Object], relevanceScore: 0.5758284330368042 },\n", + " id: undefined\n", + " },\n", + " Document {\n", + " pageContent: 'We are the only nation on Earth that has always turned every crisis we have faced into an opportunity. \\n' +\n", + " '\\n' +\n", + " 'The only nation that can be defined by a single word: possibilities. \\n' +\n", + " '\\n' +\n", + " 'So on this night, in our 245th year as a nation, I have come to report on the State of the Union. \\n' +\n", + " '\\n' +\n", + " 'And my report is this: the State of the Union is strong—because you, the American people, are strong.',\n", + " metadata: { loc: [Object], relevanceScore: 0.5479092597961426 },\n", + " id: undefined\n", + " },\n", + " Document {\n", + " pageContent: 'And I’m taking robust action to make sure the pain of our sanctions is targeted at Russia’s economy. And I will use every tool at our disposal to protect American businesses and consumers. \\n' +\n", + " '\\n' +\n", + " 'Tonight, I can announce that the United States has worked with 30 other countries to release 60 Million barrels of oil from reserves around the world.',\n", + " metadata: { loc: [Object], relevanceScore: 0.5468723773956299 },\n", + " id: undefined\n", + " }\n", + "]\n" + ] + } + ], + "source": [ + "const compressedWithResults = await reranker.compressDocuments(result, query);\n", + "console.log(compressedWithResults);" + ] + }, + { + "cell_type": "markdown", + "id": "3a5bb5ca-c3ae-4a58-be67-2cd18574b9a3", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all Watsonx document compressor features and configurations head to the [API reference](https://api.js.langchain.com/modules/_langchain_community.document_compressors_ibm.WatsonxRerank.html)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "JavaScript (Node.js)", + "language": "javascript", + "name": "javascript" + }, + "language_info": { + "file_extension": ".js", + "mimetype": "application/javascript", + "name": "javascript", + "version": "20.17.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/core_docs/docs/integrations/document_compressors/mixedbread_ai.mdx b/docs/core_docs/docs/integrations/document_compressors/mixedbread_ai.mdx index e74cdf488b0d..5ed78bdbc864 100644 --- a/docs/core_docs/docs/integrations/document_compressors/mixedbread_ai.mdx +++ b/docs/core_docs/docs/integrations/document_compressors/mixedbread_ai.mdx @@ -51,4 +51,4 @@ console.log(result); ## Additional Resources -For more information, refer to the [Reranking API documentation](https://mixedbread.ai/docs/reranking). +For more information, refer to the [Reranking API documentation](https://www.mixedbread.ai/docs/reranking/overview). diff --git a/docs/core_docs/sidebars.js b/docs/core_docs/sidebars.js index 95bf57ec5859..978a4a1614b2 100644 --- a/docs/core_docs/sidebars.js +++ b/docs/core_docs/sidebars.js @@ -347,6 +347,22 @@ module.exports = { slug: "integrations/document_transformers", }, }, + { + type: "category", + label: "Document rerankers", + collapsible: false, + items: [ + { + type: "autogenerated", + dirName: "integrations/document_compressors", + className: "hidden", + }, + ], + link: { + type: "generated-index", + slug: "integrations/document_compressors", + }, + }, { type: "category", label: "Model caches", diff --git a/libs/langchain-community/.gitignore b/libs/langchain-community/.gitignore index b554afb6f1ec..99f77ac328f8 100644 --- a/libs/langchain-community/.gitignore +++ b/libs/langchain-community/.gitignore @@ -698,6 +698,10 @@ graphs/memgraph_graph.cjs graphs/memgraph_graph.js graphs/memgraph_graph.d.ts graphs/memgraph_graph.d.cts +document_compressors/ibm.cjs +document_compressors/ibm.js +document_compressors/ibm.d.ts +document_compressors/ibm.d.cts document_transformers/html_to_text.cjs document_transformers/html_to_text.js document_transformers/html_to_text.d.ts diff --git a/libs/langchain-community/langchain.config.js b/libs/langchain-community/langchain.config.js index 7bd552b4dcb3..4a402c6941e8 100644 --- a/libs/langchain-community/langchain.config.js +++ b/libs/langchain-community/langchain.config.js @@ -220,6 +220,8 @@ export const config = { // graphs "graphs/neo4j_graph": "graphs/neo4j_graph", "graphs/memgraph_graph": "graphs/memgraph_graph", + // document_compressors + "document_compressors/ibm": "document_compressors/ibm", // document transformers "document_transformers/html_to_text": "document_transformers/html_to_text", "document_transformers/mozilla_readability": @@ -446,6 +448,8 @@ export const config = { "cache/upstash_redis", "graphs/neo4j_graph", "graphs/memgraph_graph", + // document_compressors + "document_compressors/ibm", // document_transformers "document_transformers/html_to_text", "document_transformers/mozilla_readability", diff --git a/libs/langchain-community/package.json b/libs/langchain-community/package.json index 7758dd43a8b3..be0bd452a057 100644 --- a/libs/langchain-community/package.json +++ b/libs/langchain-community/package.json @@ -2287,6 +2287,15 @@ "import": "./graphs/memgraph_graph.js", "require": "./graphs/memgraph_graph.cjs" }, + "./document_compressors/ibm": { + "types": { + "import": "./document_compressors/ibm.d.ts", + "require": "./document_compressors/ibm.d.cts", + "default": "./document_compressors/ibm.d.ts" + }, + "import": "./document_compressors/ibm.js", + "require": "./document_compressors/ibm.cjs" + }, "./document_transformers/html_to_text": { "types": { "import": "./document_transformers/html_to_text.d.ts", @@ -3783,6 +3792,10 @@ "graphs/memgraph_graph.js", "graphs/memgraph_graph.d.ts", "graphs/memgraph_graph.d.cts", + "document_compressors/ibm.cjs", + "document_compressors/ibm.js", + "document_compressors/ibm.d.ts", + "document_compressors/ibm.d.cts", "document_transformers/html_to_text.cjs", "document_transformers/html_to_text.js", "document_transformers/html_to_text.d.ts", diff --git a/libs/langchain-community/src/document_compressors/ibm.ts b/libs/langchain-community/src/document_compressors/ibm.ts new file mode 100644 index 000000000000..348f60685480 --- /dev/null +++ b/libs/langchain-community/src/document_compressors/ibm.ts @@ -0,0 +1,168 @@ +import { DocumentInterface } from "@langchain/core/documents"; +import { BaseDocumentCompressor } from "@langchain/core/retrievers/document_compressors"; +import { WatsonXAI } from "@ibm-cloud/watsonx-ai"; +import { AsyncCaller } from "@langchain/core/utils/async_caller"; +import { WatsonxAuth, WatsonxParams } from "../types/ibm.js"; +import { authenticateAndSetInstance } from "../utils/ibm.js"; + +export interface WatsonxInputRerank extends Omit { + truncateInputTokens?: number; + returnOptions?: { + topN?: number; + inputs?: boolean; + }; +} +export class WatsonxRerank + extends BaseDocumentCompressor + implements WatsonxInputRerank +{ + maxRetries = 0; + + version = "2024-05-31"; + + truncateInputTokens?: number | undefined; + + returnOptions?: + | { topN?: number; inputs?: boolean; query?: boolean } + | undefined; + + model: string; + + spaceId?: string | undefined; + + projectId?: string | undefined; + + maxConcurrency?: number | undefined; + + serviceUrl: string; + + service: WatsonXAI; + + constructor(fields: WatsonxInputRerank & WatsonxAuth) { + super(); + if (fields.projectId && fields.spaceId) + throw new Error("Maximum 1 id type can be specified per instance"); + + if (!fields.projectId && !fields.spaceId) + throw new Error( + "No id specified! At least id of 1 type has to be specified" + ); + this.model = fields.model; + this.serviceUrl = fields.serviceUrl; + this.version = fields.version; + this.projectId = fields?.projectId; + this.spaceId = fields?.spaceId; + this.maxRetries = fields.maxRetries ?? this.maxRetries; + this.maxConcurrency = fields.maxConcurrency; + this.truncateInputTokens = fields.truncateInputTokens; + this.returnOptions = fields.returnOptions; + + const { + watsonxAIApikey, + watsonxAIAuthType, + watsonxAIBearerToken, + watsonxAIUsername, + watsonxAIPassword, + watsonxAIUrl, + version, + serviceUrl, + } = fields; + + const auth = authenticateAndSetInstance({ + watsonxAIApikey, + watsonxAIAuthType, + watsonxAIBearerToken, + watsonxAIUsername, + watsonxAIPassword, + watsonxAIUrl, + version, + serviceUrl, + }); + if (auth) this.service = auth; + else throw new Error("You have not provided one type of authentication"); + } + + scopeId() { + if (this.projectId) + return { projectId: this.projectId, modelId: this.model }; + else return { spaceId: this.spaceId, modelId: this.model }; + } + + invocationParams(options?: Partial) { + return { + truncate_input_tokens: + options?.truncateInputTokens ?? this.truncateInputTokens, + return_options: { + top_n: options?.returnOptions?.topN ?? this.returnOptions?.topN, + inputs: options?.returnOptions?.inputs ?? this.returnOptions?.inputs, + }, + }; + } + + async compressDocuments( + documents: DocumentInterface[], + query: string + ): Promise { + const caller = new AsyncCaller({ + maxConcurrency: this.maxConcurrency, + maxRetries: this.maxRetries, + }); + const inputs = documents.map((document) => ({ + text: document.pageContent, + })); + const { result } = await caller.call(() => + this.service.textRerank({ + ...this.scopeId(), + inputs, + query, + }) + ); + const resultDocuments = result.results.map(({ index, score }) => { + const rankedDocument = documents[index]; + rankedDocument.metadata.relevanceScore = score; + return rankedDocument; + }); + return resultDocuments; + } + + async rerank( + documents: Array< + DocumentInterface | string | Record<"pageContent", string> + >, + query: string, + options?: Partial + ): Promise> { + const inputs = documents.map((document) => { + if (typeof document === "string") { + return { text: document }; + } + return { text: document.pageContent }; + }); + + const caller = new AsyncCaller({ + maxConcurrency: this.maxConcurrency, + maxRetries: this.maxRetries, + }); + const { result } = await caller.call(() => + this.service.textRerank({ + ...this.scopeId(), + inputs, + query, + parameters: this.invocationParams(options), + }) + ); + const response = result.results.map((document) => { + return document?.input + ? { + index: document.index, + relevanceScore: document.score, + input: document?.input, + } + : { + index: document.index, + relevanceScore: document.score, + }; + }); + return response; + } +} diff --git a/libs/langchain-community/src/document_compressors/tests/ibm.int.test.ts b/libs/langchain-community/src/document_compressors/tests/ibm.int.test.ts new file mode 100644 index 000000000000..e65ea9e1eff3 --- /dev/null +++ b/libs/langchain-community/src/document_compressors/tests/ibm.int.test.ts @@ -0,0 +1,80 @@ +/* eslint-disable no-process-env */ +import { Document } from "@langchain/core/documents"; +import { WatsonxRerank } from "../ibm.js"; + +const query = "What is the capital of the United States?"; +const docs = [ + new Document({ + pageContent: + "Carson City is the capital city of the American state of Nevada. At the 2010 United States Census, Carson City had a population of 55,274.", + }), + new Document({ + pageContent: + "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that are a political division controlled by the United States. Its capital is Saipan.", + }), + new Document({ + pageContent: + "Charlotte Amalie is the capital and largest city of the United States Virgin Islands. It has about 20,000 people. The city is on the island of Saint Thomas.", + }), + new Document({ + pageContent: + "Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.", + }), + new Document({ + pageContent: + "Capital punishment (the death penalty) has existed in the United States since before the United States was a country. As of 2017, capital punishment is legal in 30 of the 50 states. The federal government (including the United States military) also uses capital punishment.", + }), +]; +describe("Integration tests on WatsonxRerank", () => { + describe(".compressDocuments() method", () => { + test("Basic call", async () => { + const instance = new WatsonxRerank({ + model: "cross-encoder/ms-marco-minilm-l-12-v2", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + version: "2024-05-31", + projectId: process.env.WATSONX_AI_PROJECT_ID ?? "testString", + }); + const result = await instance.compressDocuments(docs, query); + expect(result.length).toBe(docs.length); + result.forEach((item) => + expect(typeof item.metadata.relevanceScore).toBe("number") + ); + }); + }); + + describe(".rerank() method", () => { + test("Basic call", async () => { + const instance = new WatsonxRerank({ + model: "cross-encoder/ms-marco-minilm-l-12-v2", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + version: "2024-05-31", + projectId: process.env.WATSONX_AI_PROJECT_ID ?? "testString", + }); + const result = await instance.rerank(docs, query); + expect(result.length).toBe(docs.length); + result.forEach((item) => { + expect(typeof item.relevanceScore).toBe("number"); + expect(item.input).toBeUndefined(); + }); + }); + }); + test("Basic call with options", async () => { + const instance = new WatsonxRerank({ + model: "cross-encoder/ms-marco-minilm-l-12-v2", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + version: "2024-05-31", + projectId: process.env.WATSONX_AI_PROJECT_ID ?? "testString", + }); + const result = await instance.rerank(docs, query, { + returnOptions: { + topN: 3, + inputs: true, + }, + }); + expect(result.length).toBe(3); + result.forEach((item) => { + expect(typeof item.relevanceScore).toBe("number"); + expect(item.input).toBeDefined(); + }); + }); +}); diff --git a/libs/langchain-community/src/document_compressors/tests/ibm.test.ts b/libs/langchain-community/src/document_compressors/tests/ibm.test.ts new file mode 100644 index 000000000000..c9332bc8c1b3 --- /dev/null +++ b/libs/langchain-community/src/document_compressors/tests/ibm.test.ts @@ -0,0 +1,159 @@ +/* eslint-disable no-process-env */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { WatsonxRerank, WatsonxInputRerank } from "../ibm.js"; + +function getKey(key: K): K { + return key; +} +const testProperties = ( + instance: WatsonxRerank, + testProps: WatsonxInputRerank, + notExTestProps?: { [key: string]: any } +) => { + const checkProperty = ( + testProps: T, + instance: T, + existing = true + ) => { + Object.keys(testProps).forEach((key) => { + const keys = getKey(key); + type Type = Pick; + + if (typeof testProps[key as keyof T] === "object") + checkProperty(testProps[key as keyof T], instance[key], existing); + else { + if (existing) + expect(instance[key as keyof T]).toBe(testProps[key as keyof T]); + else if (instance) expect(instance[key as keyof T]).toBeUndefined(); + } + }); + }; + checkProperty(testProps, instance); + if (notExTestProps) + checkProperty(notExTestProps, instance, false); +}; +const fakeAuthProp = { + watsonxAIAuthType: "iam", + watsonxAIApikey: "fake_key", +}; +describe("Embeddings unit tests", () => { + describe("Positive tests", () => { + test("Basic properties", () => { + const testProps = { + model: "cross-encoder/ms-marco-minilm-l-12-v2", + version: "2024-05-31", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + projectId: process.env.WATSONX_AI_PROJECT_ID || "testString", + }; + const instance = new WatsonxRerank({ ...testProps, ...fakeAuthProp }); + testProperties(instance, testProps); + }); + + test("Basic properties", () => { + const testProps: WatsonxInputRerank = { + model: "cross-encoder/ms-marco-minilm-l-12-v2", + version: "2024-05-31", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + projectId: process.env.WATSONX_AI_PROJECT_ID || "testString", + truncateInputTokens: 10, + maxConcurrency: 2, + maxRetries: 2, + returnOptions: { + topN: 5, + inputs: false, + }, + }; + const instance = new WatsonxRerank({ ...testProps, ...fakeAuthProp }); + testProperties(instance, testProps); + }); + }); + + describe("Negative tests", () => { + test("Missing id", async () => { + const testProps = { + model: "cross-encoder/ms-marco-minilm-l-12-v2", + version: "2024-05-31", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + }; + expect( + () => + new WatsonxRerank({ + ...testProps, + ...fakeAuthProp, + }) + ).toThrowError(); + }); + + test("Missing other props", async () => { + // @ts-expect-error Intentionally passing wrong value + const testPropsProjectId: WatsonxInputLLM = { + projectId: process.env.WATSONX_AI_PROJECT_ID || "testString", + }; + expect( + () => + new WatsonxRerank({ + ...testPropsProjectId, + }) + ).toThrowError(); + // @ts-expect-error //Intentionally passing wrong value + const testPropsServiceUrl: WatsonxInputLLM = { + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + }; + expect( + () => + new WatsonxRerank({ + ...testPropsServiceUrl, + }) + ).toThrowError(); + const testPropsVersion = { + version: "2024-05-31", + }; + expect( + () => + new WatsonxRerank({ + // @ts-expect-error Intentionally passing wrong props + testPropsVersion, + }) + ).toThrowError(); + }); + + test("Passing more than one id", async () => { + const testProps = { + model: "cross-encoder/ms-marco-minilm-l-12-v2", + version: "2024-05-31", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + projectId: process.env.WATSONX_AI_PROJECT_ID || "testString", + spaceId: process.env.WATSONX_AI_PROJECT_ID || "testString", + }; + expect( + () => + new WatsonxRerank({ + ...testProps, + ...fakeAuthProp, + }) + ).toThrowError(); + }); + + test("Invalid properties", () => { + const testProps = { + model: "cross-encoder/ms-marco-minilm-l-12-v2", + version: "2024-05-31", + serviceUrl: process.env.WATSONX_AI_SERVICE_URL as string, + projectId: process.env.WATSONX_AI_PROJECT_ID || "testString", + }; + const notExTestProps = { + notExisting: 12, + notExObj: { + notExProp: 12, + }, + }; + const instance = new WatsonxRerank({ + ...testProps, + ...notExTestProps, + ...fakeAuthProp, + }); + + testProperties(instance, testProps, notExTestProps); + }); + }); +}); diff --git a/libs/langchain-community/src/load/import_constants.ts b/libs/langchain-community/src/load/import_constants.ts index 65d40fc1f7d4..7be97f096fde 100644 --- a/libs/langchain-community/src/load/import_constants.ts +++ b/libs/langchain-community/src/load/import_constants.ts @@ -111,6 +111,7 @@ export const optionalImportEntrypoints: string[] = [ "langchain_community/retrievers/zep_cloud", "langchain_community/graphs/neo4j_graph", "langchain_community/graphs/memgraph_graph", + "langchain_community/document_compressors/ibm", "langchain_community/document_transformers/html_to_text", "langchain_community/document_transformers/mozilla_readability", "langchain_community/storage/cassandra",