-
Notifications
You must be signed in to change notification settings - Fork 1
DA-831: Update LangChain to use CouchbaseSearchVectorStore #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,16 @@ | ||
| import { | ||
| CouchbaseVectorStore, | ||
| CouchbaseVectorStoreArgs, | ||
| } from "@langchain/community/vectorstores/couchbase"; | ||
| CouchbaseSearchVectorStore, | ||
| } from "@langchain/community/vectorstores/couchbase_search"; | ||
| import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai"; | ||
| import { createCouchbaseCluster } from "@/lib/couchbase-connection"; | ||
| import { Message as VercelChatMessage } from "ai"; | ||
| import { HumanMessage, AIMessage, ChatMessage } from "@langchain/core/messages"; | ||
| import { createHistoryAwareRetriever } from "langchain/chains/history_aware_retriever"; | ||
| import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; | ||
| import { createStuffDocumentsChain } from "langchain/chains/combine_documents"; | ||
| import { createRetrievalChain } from "langchain/chains/retrieval"; | ||
| import { createHistoryAwareRetriever } from "@langchain/classic/chains/history_aware_retriever"; | ||
| import { createStuffDocumentsChain } from "@langchain/classic/chains/combine_documents"; | ||
| import { createRetrievalChain } from "@langchain/classic/chains/retrieval"; | ||
| import { Document } from '@langchain/core/documents'; | ||
| import {NextResponse} from 'next/server'; | ||
|
|
||
| const formatVercelMessages = (message: VercelChatMessage) => { | ||
| if (message.role === "user") { | ||
|
|
@@ -26,6 +26,12 @@ const formatVercelMessages = (message: VercelChatMessage) => { | |
| }; | ||
|
|
||
| export async function POST(request: Request) { | ||
| // Load environment variables explicitly | ||
| const openaiApiKey = process.env.OPENAI_API_KEY; | ||
| if (!openaiApiKey) { | ||
| return NextResponse.json({ error: "OPENAI_API_KEY not set" }, { status: 500 }); | ||
| } | ||
|
|
||
| const body = await request.json(); | ||
| const messages = body.messages ?? []; | ||
| if (!messages.length) { | ||
|
|
@@ -38,10 +44,12 @@ export async function POST(request: Request) { | |
| const currentMessageContent = messages[messages.length - 1].content; | ||
| try { | ||
| const model = new ChatOpenAI({ | ||
| model: "gpt-4" | ||
| model: "gpt-4", | ||
| openAIApiKey: openaiApiKey, | ||
| }); | ||
| const embeddings = new OpenAIEmbeddings({ | ||
| openAIApiKey: process.env.OPENAI_API_KEY, | ||
| apiKey: openaiApiKey, | ||
| model: "text-embedding-3-small", | ||
| }); | ||
|
|
||
| const historyAwarePrompt = ChatPromptTemplate.fromMessages([ | ||
|
|
@@ -78,7 +86,12 @@ export async function POST(request: Request) { | |
| const scopedIndex = true; | ||
|
|
||
| const cluster = await createCouchbaseCluster(); | ||
| const couchbaseConfig: CouchbaseVectorStoreArgs = { | ||
|
|
||
| if (!cluster) { | ||
| throw new Error("Couchbase cluster connection failed"); | ||
| } | ||
|
|
||
| const couchbaseConfig = { | ||
| cluster, | ||
| bucketName, | ||
| scopeName, | ||
|
|
@@ -88,7 +101,7 @@ export async function POST(request: Request) { | |
| embeddingKey, | ||
| scopedIndex, | ||
| }; | ||
| const couchbaseVectorStore = await CouchbaseVectorStore.initialize( | ||
| const couchbaseSearchVectorStore = await CouchbaseSearchVectorStore.initialize( | ||
| embeddings, | ||
| couchbaseConfig | ||
| ); | ||
|
|
@@ -98,7 +111,9 @@ export async function POST(request: Request) { | |
| resolveWithDocuments = resolve; | ||
| }); | ||
|
|
||
| const retriever = couchbaseVectorStore.asRetriever({ | ||
| const retriever = couchbaseSearchVectorStore.asRetriever({ | ||
| searchType: "similarity", | ||
| searchKwargs: { k: 4 }, | ||
| callbacks: [ | ||
| { | ||
| handleRetrieverEnd(documents) { | ||
|
|
@@ -108,15 +123,20 @@ export async function POST(request: Request) { | |
| }, | ||
| }, | ||
| ], | ||
| } | ||
| ); | ||
| }); | ||
|
|
||
| // Create the history-aware retriever chain | ||
| const historyAwareRetrieverChain = await createHistoryAwareRetriever({ | ||
| llm: model, | ||
| retriever, | ||
| rephrasePrompt: historyAwarePrompt, | ||
| }); | ||
|
|
||
| // Use history-aware retriever only when there's chat history | ||
| const retrieverToUse = formattedPreviousMessages.length > 0 | ||
| ? historyAwareRetrieverChain | ||
| : retriever; | ||
|
|
||
| // Create a chain that answers questions using retrieved relevant documents as context. | ||
| const documentChain = await createStuffDocumentsChain({ | ||
| llm: model, | ||
|
|
@@ -125,7 +145,7 @@ export async function POST(request: Request) { | |
|
|
||
| // Create a chain that combines the above retriever and question answering chains. | ||
| const conversationalRetrievalChain = await createRetrievalChain({ | ||
| retriever: historyAwareRetrieverChain, | ||
| retriever: retrieverToUse, | ||
| combineDocsChain: documentChain, | ||
| }); | ||
|
|
||
|
|
@@ -164,5 +184,6 @@ export async function POST(request: Request) { | |
|
|
||
| } catch (err) { | ||
| console.log("Error Received ", err); | ||
| return NextResponse.json({ error: "Failed to process chat message" }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,9 @@ | ||
| import { NextResponse } from "next/server"; | ||
| import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; | ||
| import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters"; | ||
| import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf'; | ||
| import { | ||
| CouchbaseVectorStore, | ||
| CouchbaseVectorStoreArgs, | ||
| } from "@langchain/community/vectorstores/couchbase"; | ||
| CouchbaseSearchVectorStore, CouchbaseSearchVectorStoreArgs, | ||
| } from "@langchain/community/vectorstores/couchbase_search"; | ||
| import { OpenAIEmbeddings } from "@langchain/openai"; | ||
| import { createCouchbaseCluster } from "@/lib/couchbase-connection"; | ||
| import { writeFile } from "fs/promises"; | ||
|
|
@@ -52,7 +51,12 @@ export async function POST(request: Request) { | |
| const scopedIndex = true; | ||
|
|
||
| const cluster = await createCouchbaseCluster(); | ||
| const couchbaseConfig: CouchbaseVectorStoreArgs = { | ||
|
|
||
| if (!cluster) { | ||
| throw new Error("Couchbase cluster connection failed"); | ||
| } | ||
|
Comment on lines
+55
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| const couchbaseConfig: CouchbaseSearchVectorStoreArgs = { | ||
| cluster, | ||
| bucketName, | ||
| scopeName, | ||
|
|
@@ -62,7 +66,7 @@ export async function POST(request: Request) { | |
| embeddingKey, | ||
| scopedIndex, | ||
| }; | ||
| await CouchbaseVectorStore.fromDocuments(docs, embeddings, couchbaseConfig); | ||
| await CouchbaseSearchVectorStore.fromDocuments(docs, embeddings, couchbaseConfig); | ||
|
|
||
| console.log("creating vector store..."); | ||
| } catch (error) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||
| import { connect, Cluster } from "couchbase"; | ||||||
|
|
||||||
| export async function createCouchbaseCluster(): Promise<Cluster> { | ||||||
| export async function createCouchbaseCluster(): Promise<Cluster | void> { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function's return type is declared as
Suggested change
|
||||||
| const connectionString = process.env.DB_CONN_STR; | ||||||
| const databaseUsername = process.env.DB_USERNAME; | ||||||
| const databasePassword = process.env.DB_PASSWORD; | ||||||
|
|
@@ -23,11 +23,16 @@ export async function createCouchbaseCluster(): Promise<Cluster> { | |||||
| ); | ||||||
| } | ||||||
|
|
||||||
| const cluster = await connect(connectionString, { | ||||||
| username: databaseUsername, | ||||||
| password: databasePassword, | ||||||
| configProfile: "wanDevelopment", | ||||||
| }); | ||||||
| try { | ||||||
| return await connect(connectionString, { | ||||||
| username: databaseUsername, | ||||||
| password: databasePassword, | ||||||
| configProfile: "wanDevelopment", | ||||||
| }); | ||||||
| } catch (e) { | ||||||
| throw new Error( | ||||||
| `Could not connect to the Couchbase cluster. Please check your DB_CONN_STR, DB_USERNAME, and DB_PASSWORD environment variables. \n${e}`, | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| return cluster; | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check for
!clusteris redundant. ThecreateCouchbaseClusterfunction is designed to throw an error if the connection fails, which will be caught by the outertry...catchblock in thisPOSThandler. As a result, thisifblock is unreachable and can be safely removed.