add vectara chain
This commit is contained in:
parent
10c3066a91
commit
40a63008ec
|
|
@ -0,0 +1,147 @@
|
||||||
|
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||||
|
import { getBaseClasses } from '../../../src/utils'
|
||||||
|
import { VectorDBQAChain } from 'langchain/chains'
|
||||||
|
import { Document } from 'langchain/document'
|
||||||
|
import { VectaraStore } from 'langchain/vectorstores/vectara'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
|
||||||
|
class VectaraChain_Chains implements INode {
|
||||||
|
label: string
|
||||||
|
name: string
|
||||||
|
version: number
|
||||||
|
type: string
|
||||||
|
icon: string
|
||||||
|
category: string
|
||||||
|
baseClasses: string[]
|
||||||
|
description: string
|
||||||
|
inputs: INodeParams[]
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.label = 'Vectara QA Chain'
|
||||||
|
this.name = 'vectaraQAChain'
|
||||||
|
this.version = 1.0
|
||||||
|
this.type = 'VectaraQAChain'
|
||||||
|
this.icon = 'vectara.png'
|
||||||
|
this.category = 'Chains'
|
||||||
|
this.description = 'QA chain for Vectara'
|
||||||
|
this.baseClasses = [this.type, ...getBaseClasses(VectorDBQAChain)]
|
||||||
|
this.inputs = [
|
||||||
|
{
|
||||||
|
label: 'Vectara Vector Store',
|
||||||
|
name: 'vectaraStore',
|
||||||
|
type: 'VectorStore'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(): Promise<any> {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(nodeData: INodeData, input: string): Promise<object> {
|
||||||
|
const vectorStore = nodeData.inputs?.vectaraStore as VectaraStore
|
||||||
|
const topK = (vectorStore as any)?.k ?? 4
|
||||||
|
|
||||||
|
const headers = await vectorStore.getJsonHeader()
|
||||||
|
const vectaraFilter = (vectorStore as any).vectaraFilter ?? {}
|
||||||
|
const corpusId: number[] = (vectorStore as any).corpusId ?? []
|
||||||
|
const customerId = (vectorStore as any).customerId ?? ''
|
||||||
|
|
||||||
|
const corpusKeys = corpusId.map((corpusId) => ({
|
||||||
|
customerId,
|
||||||
|
corpusId,
|
||||||
|
metadataFilter: vectaraFilter?.filter ?? '',
|
||||||
|
lexicalInterpolationConfig: { lambda: vectaraFilter?.lambda ?? 0.025 }
|
||||||
|
}))
|
||||||
|
|
||||||
|
let summarizerPromptName = 'vectara-experimental-summary-ext-2023-10-23-med' // can let user select
|
||||||
|
let responseLang = 'en' // can let user select
|
||||||
|
let maxSummarizedResults = 5 // can let user specify
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
query: [
|
||||||
|
{
|
||||||
|
query: input,
|
||||||
|
start: 0,
|
||||||
|
numResults: topK,
|
||||||
|
contextConfig: {
|
||||||
|
sentencesAfter: vectaraFilter?.contextConfig?.sentencesAfter ?? 2,
|
||||||
|
sentencesBefore: vectaraFilter?.contextConfig?.sentencesBefore ?? 2
|
||||||
|
},
|
||||||
|
corpusKey: corpusKeys,
|
||||||
|
summary: [
|
||||||
|
{
|
||||||
|
summarizerPromptName,
|
||||||
|
responseLang,
|
||||||
|
maxSummarizedResults
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://api.vectara.io/v1/query`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers?.headers,
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(`Vectara API returned status code ${response.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
const responses = result.responseSet[0].response
|
||||||
|
const documents = result.responseSet[0].document
|
||||||
|
let summarizedText = ''
|
||||||
|
|
||||||
|
for (let i = 0; i < responses.length; i += 1) {
|
||||||
|
const responseMetadata = responses[i].metadata
|
||||||
|
const documentMetadata = documents[responses[i].documentIndex].metadata
|
||||||
|
const combinedMetadata: Record<string, unknown> = {}
|
||||||
|
|
||||||
|
responseMetadata.forEach((item: { name: string; value: unknown }) => {
|
||||||
|
combinedMetadata[item.name] = item.value
|
||||||
|
})
|
||||||
|
|
||||||
|
documentMetadata.forEach((item: { name: string; value: unknown }) => {
|
||||||
|
combinedMetadata[item.name] = item.value
|
||||||
|
})
|
||||||
|
|
||||||
|
responses[i].metadata = combinedMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
const summaryStatus = result.responseSet[0].summary[0].status
|
||||||
|
if (summaryStatus.length > 0 && summaryStatus[0].code === 'BAD_REQUEST') {
|
||||||
|
throw new Error(
|
||||||
|
`BAD REQUEST: Too much text for the summarizer to summarize. Please try reducing the number of search results to summarize, or the context of each result by adjusting the 'summary_num_sentences', and 'summary_num_results' parameters respectively.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
summaryStatus.length > 0 &&
|
||||||
|
summaryStatus[0].code === 'NOT_FOUND' &&
|
||||||
|
summaryStatus[0].statusDetail === 'Failed to retrieve summarizer.'
|
||||||
|
) {
|
||||||
|
throw new Error(`BAD REQUEST: summarizer ${summarizerPromptName} is invalid for this account.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
summarizedText = result.responseSet[0].summary[0]?.text
|
||||||
|
|
||||||
|
const sourceDocuments: Document[] = responses.map(
|
||||||
|
(response: { text: string; metadata: Record<string, unknown>; score: number }) =>
|
||||||
|
new Document({
|
||||||
|
pageContent: response.text,
|
||||||
|
metadata: response.metadata
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return { text: summarizedText, sourceDocuments: sourceDocuments }
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { nodeClass: VectaraChain_Chains }
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
|
|
@ -842,7 +842,7 @@ export const isFlowValidForStream = (reactFlowNodes: IReactFlowNode[], endingNod
|
||||||
let isValidChainOrAgent = false
|
let isValidChainOrAgent = false
|
||||||
if (endingNodeData.category === 'Chains') {
|
if (endingNodeData.category === 'Chains') {
|
||||||
// Chains that are not available to stream
|
// Chains that are not available to stream
|
||||||
const blacklistChains = ['openApiChain']
|
const blacklistChains = ['openApiChain', 'vectaraQAChain']
|
||||||
isValidChainOrAgent = !blacklistChains.includes(endingNodeData.name)
|
isValidChainOrAgent = !blacklistChains.includes(endingNodeData.name)
|
||||||
} else if (endingNodeData.category === 'Agents') {
|
} else if (endingNodeData.category === 'Agents') {
|
||||||
// Agent that are available to stream
|
// Agent that are available to stream
|
||||||
|
|
|
||||||
|
|
@ -699,7 +699,10 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||||
{message.sourceDocuments && (
|
{message.sourceDocuments && (
|
||||||
<div style={{ display: 'block', flexDirection: 'row', width: '100%' }}>
|
<div style={{ display: 'block', flexDirection: 'row', width: '100%' }}>
|
||||||
{removeDuplicateURL(message).map((source, index) => {
|
{removeDuplicateURL(message).map((source, index) => {
|
||||||
const URL = isValidURL(source.metadata.source)
|
const URL =
|
||||||
|
source.metadata && source.metadata.source
|
||||||
|
? isValidURL(source.metadata.source)
|
||||||
|
: undefined
|
||||||
return (
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
size='small'
|
size='small'
|
||||||
|
|
|
||||||
|
|
@ -423,10 +423,14 @@ export const removeDuplicateURL = (message) => {
|
||||||
if (!message.sourceDocuments) return newSourceDocuments
|
if (!message.sourceDocuments) return newSourceDocuments
|
||||||
|
|
||||||
message.sourceDocuments.forEach((source) => {
|
message.sourceDocuments.forEach((source) => {
|
||||||
if (isValidURL(source.metadata.source) && !visitedURLs.includes(source.metadata.source)) {
|
if (source.metadata && source.metadata.source) {
|
||||||
visitedURLs.push(source.metadata.source)
|
if (isValidURL(source.metadata.source) && !visitedURLs.includes(source.metadata.source)) {
|
||||||
newSourceDocuments.push(source)
|
visitedURLs.push(source.metadata.source)
|
||||||
} else if (!isValidURL(source.metadata.source)) {
|
newSourceDocuments.push(source)
|
||||||
|
} else if (!isValidURL(source.metadata.source)) {
|
||||||
|
newSourceDocuments.push(source)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
newSourceDocuments.push(source)
|
newSourceDocuments.push(source)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -379,7 +379,10 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
||||||
{message.sourceDocuments && (
|
{message.sourceDocuments && (
|
||||||
<div style={{ display: 'block', flexDirection: 'row', width: '100%' }}>
|
<div style={{ display: 'block', flexDirection: 'row', width: '100%' }}>
|
||||||
{removeDuplicateURL(message).map((source, index) => {
|
{removeDuplicateURL(message).map((source, index) => {
|
||||||
const URL = isValidURL(source.metadata.source)
|
const URL =
|
||||||
|
source.metadata && source.metadata.source
|
||||||
|
? isValidURL(source.metadata.source)
|
||||||
|
: undefined
|
||||||
return (
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
size='small'
|
size='small'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue