diff --git a/packages/components/README-ZH.md b/packages/components/README-ZH.md
index 0605812f..12cb240b 100644
--- a/packages/components/README-ZH.md
+++ b/packages/components/README-ZH.md
@@ -16,4 +16,4 @@ npm i flowise-components
## 许可证
-此存储库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
\ No newline at end of file
+此存储库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
diff --git a/packages/components/credentials/MilvusAuth.credential.ts b/packages/components/credentials/MilvusAuth.credential.ts
new file mode 100644
index 00000000..b94e1fc8
--- /dev/null
+++ b/packages/components/credentials/MilvusAuth.credential.ts
@@ -0,0 +1,31 @@
+import { INodeParams, INodeCredential } from '../src/Interface'
+
+class MilvusCredential implements INodeCredential {
+ label: string
+ name: string
+ version: number
+ description: string
+ inputs: INodeParams[]
+
+ constructor() {
+ this.label = 'Milvus Auth'
+ this.name = 'milvusAuth'
+ this.version = 1.0
+ this.description =
+ 'You can find the Milvus Authentication from here page.'
+ this.inputs = [
+ {
+ label: 'Milvus User',
+ name: 'milvusUser',
+ type: 'string'
+ },
+ {
+ label: 'Milvus Password',
+ name: 'milvusPassword',
+ type: 'password'
+ }
+ ]
+ }
+}
+
+module.exports = { credClass: MilvusCredential }
diff --git a/packages/components/nodes/chains/ApiChain/apichain.svg b/packages/components/nodes/chains/ApiChain/apichain.svg
index ef62e168..3b86b905 100644
--- a/packages/components/nodes/chains/ApiChain/apichain.svg
+++ b/packages/components/nodes/chains/ApiChain/apichain.svg
@@ -1,3 +1,3 @@
-
\ No newline at end of file
diff --git a/packages/components/nodes/vectorstores/Milvus/Milvus_Existing.ts b/packages/components/nodes/vectorstores/Milvus/Milvus_Existing.ts
new file mode 100644
index 00000000..514fdc73
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Milvus/Milvus_Existing.ts
@@ -0,0 +1,185 @@
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { DataType, ErrorCode } from '@zilliz/milvus2-sdk-node'
+import { MilvusLibArgs, Milvus } from 'langchain/vectorstores/milvus'
+import { Embeddings } from 'langchain/embeddings/base'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { Document } from 'langchain/document'
+
+class Milvus_Existing_VectorStores implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Milvus Load Existing collection'
+ this.name = 'milvusExistingCollection'
+ this.version = 1.0
+ this.type = 'Milvus'
+ this.icon = 'milvus.svg'
+ this.category = 'Vector Stores'
+ this.description = 'Load existing collection from Milvus (i.e: Document has been upserted)'
+ this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ optional: true,
+ credentialNames: ['milvusAuth']
+ }
+ this.inputs = [
+ {
+ label: 'Embeddings',
+ name: 'embeddings',
+ type: 'Embeddings'
+ },
+ {
+ label: 'Milvus Server URL',
+ name: 'milvusServerUrl',
+ type: 'string',
+ placeholder: 'http://localhost:19530'
+ },
+ {
+ label: 'Milvus Collection Name',
+ name: 'milvusCollection',
+ type: 'string'
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Milvus Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Milvus Vector Store',
+ name: 'vectorStore',
+ baseClasses: [this.type, ...getBaseClasses(Milvus)]
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ // server setup
+ const address = nodeData.inputs?.milvusServerUrl as string
+ const collectionName = nodeData.inputs?.milvusCollection as string
+
+ // embeddings
+ const embeddings = nodeData.inputs?.embeddings as Embeddings
+ const topK = nodeData.inputs?.topK as string
+
+ // output
+ const output = nodeData.outputs?.output as string
+
+ // format data
+ const k = topK ? parseInt(topK, 10) : 4
+
+ // credential
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const milvusUser = getCredentialParam('milvusUser', credentialData, nodeData)
+ const milvusPassword = getCredentialParam('milvusPassword', credentialData, nodeData)
+
+ // init MilvusLibArgs
+ const milVusArgs: MilvusLibArgs = {
+ url: address,
+ collectionName: collectionName
+ }
+
+ if (milvusUser) milVusArgs.username = milvusUser
+ if (milvusPassword) milVusArgs.password = milvusPassword
+
+ const vectorStore = await Milvus.fromExistingCollection(embeddings, milVusArgs)
+
+ // Avoid Illegal Invocation
+ vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: string) => {
+ const hasColResp = await vectorStore.client.hasCollection({
+ collection_name: vectorStore.collectionName
+ })
+ if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error checking collection: ${hasColResp}`)
+ }
+ if (hasColResp.value === false) {
+ throw new Error(`Collection not found: ${vectorStore.collectionName}, please create collection before search.`)
+ }
+
+ const filterStr = filter ?? ''
+
+ await vectorStore.grabCollectionFields()
+
+ const loadResp = await vectorStore.client.loadCollectionSync({
+ collection_name: vectorStore.collectionName
+ })
+
+ if (loadResp.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error loading collection: ${loadResp}`)
+ }
+
+ const outputFields = vectorStore.fields.filter((field) => field !== vectorStore.vectorField)
+
+ const searchResp = await vectorStore.client.search({
+ collection_name: vectorStore.collectionName,
+ search_params: {
+ anns_field: vectorStore.vectorField,
+ topk: k.toString(),
+ metric_type: vectorStore.indexCreateParams.metric_type,
+ params: vectorStore.indexSearchParams
+ },
+ output_fields: outputFields,
+ vector_type: DataType.FloatVector,
+ vectors: [query],
+ filter: filterStr
+ })
+ if (searchResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`)
+ }
+ const results: [Document, number][] = []
+ searchResp.results.forEach((result) => {
+ const fields = {
+ pageContent: '',
+ metadata: {} as Record
+ }
+ Object.keys(result).forEach((key) => {
+ if (key === vectorStore.textField) {
+ fields.pageContent = result[key]
+ } else if (vectorStore.fields.includes(key) || key === vectorStore.primaryField) {
+ if (typeof result[key] === 'string') {
+ const { isJson, obj } = checkJsonString(result[key])
+ fields.metadata[key] = isJson ? obj : result[key]
+ } else {
+ fields.metadata[key] = result[key]
+ }
+ }
+ })
+ results.push([new Document(fields), result.score])
+ })
+ return results
+ }
+
+ if (output === 'retriever') {
+ const retriever = vectorStore.asRetriever(k)
+ return retriever
+ } else if (output === 'vectorStore') {
+ ;(vectorStore as any).k = k
+ return vectorStore
+ }
+ return vectorStore
+ }
+}
+
+function checkJsonString(value: string): { isJson: boolean; obj: any } {
+ try {
+ const result = JSON.parse(value)
+ return { isJson: true, obj: result }
+ } catch (e) {
+ return { isJson: false, obj: null }
+ }
+}
+
+module.exports = { nodeClass: Milvus_Existing_VectorStores }
diff --git a/packages/components/nodes/vectorstores/Milvus/Milvus_Upsert.ts b/packages/components/nodes/vectorstores/Milvus/Milvus_Upsert.ts
new file mode 100644
index 00000000..ca69cb39
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Milvus/Milvus_Upsert.ts
@@ -0,0 +1,281 @@
+import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
+import { DataType, ErrorCode, MetricType, IndexType } from '@zilliz/milvus2-sdk-node'
+import { MilvusLibArgs, Milvus } from 'langchain/vectorstores/milvus'
+import { Embeddings } from 'langchain/embeddings/base'
+import { Document } from 'langchain/document'
+import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
+import { flatten } from 'lodash'
+
+interface InsertRow {
+ [x: string]: string | number[]
+}
+
+class Milvus_Upsert_VectorStores implements INode {
+ label: string
+ name: string
+ version: number
+ description: string
+ type: string
+ icon: string
+ category: string
+ baseClasses: string[]
+ inputs: INodeParams[]
+ credential: INodeParams
+ outputs: INodeOutputsValue[]
+
+ constructor() {
+ this.label = 'Milvus Upsert Document'
+ this.name = 'milvusUpsert'
+ this.version = 1.0
+ this.type = 'Milvus'
+ this.icon = 'milvus.svg'
+ this.category = 'Vector Stores'
+ this.description = 'Upsert documents to Milvus'
+ this.baseClasses = [this.type, 'VectorStoreRetriever', 'BaseRetriever']
+ this.credential = {
+ label: 'Connect Credential',
+ name: 'credential',
+ type: 'credential',
+ optional: true,
+ credentialNames: ['milvusAuth']
+ }
+ this.inputs = [
+ {
+ label: 'Document',
+ name: 'document',
+ type: 'Document',
+ list: true
+ },
+ {
+ label: 'Embeddings',
+ name: 'embeddings',
+ type: 'Embeddings'
+ },
+ {
+ label: 'Milvus Server URL',
+ name: 'milvusServerUrl',
+ type: 'string',
+ placeholder: 'http://localhost:19530'
+ },
+ {
+ label: 'Milvus Collection Name',
+ name: 'milvusCollection',
+ type: 'string'
+ }
+ ]
+ this.outputs = [
+ {
+ label: 'Milvus Retriever',
+ name: 'retriever',
+ baseClasses: this.baseClasses
+ },
+ {
+ label: 'Milvus Vector Store',
+ name: 'vectorStore',
+ baseClasses: [this.type, ...getBaseClasses(Milvus)]
+ }
+ ]
+ }
+
+ async init(nodeData: INodeData, _: string, options: ICommonObject): Promise {
+ // server setup
+ const address = nodeData.inputs?.milvusServerUrl as string
+ const collectionName = nodeData.inputs?.milvusCollection as string
+
+ // embeddings
+ const docs = nodeData.inputs?.document as Document[]
+ const embeddings = nodeData.inputs?.embeddings as Embeddings
+ const topK = nodeData.inputs?.topK as string
+
+ // output
+ const output = nodeData.outputs?.output as string
+
+ // format data
+ const k = topK ? parseInt(topK, 10) : 4
+
+ // credential
+ const credentialData = await getCredentialData(nodeData.credential ?? '', options)
+ const milvusUser = getCredentialParam('milvusUser', credentialData, nodeData)
+ const milvusPassword = getCredentialParam('milvusPassword', credentialData, nodeData)
+
+ // init MilvusLibArgs
+ const milVusArgs: MilvusLibArgs = {
+ url: address,
+ collectionName: collectionName
+ }
+
+ if (milvusUser) milVusArgs.username = milvusUser
+ if (milvusPassword) milVusArgs.password = milvusPassword
+
+ const flattenDocs = docs && docs.length ? flatten(docs) : []
+ const finalDocs = []
+ for (let i = 0; i < flattenDocs.length; i += 1) {
+ finalDocs.push(new Document(flattenDocs[i]))
+ }
+
+ const vectorStore = await MilvusUpsert.fromDocuments(finalDocs, embeddings, milVusArgs)
+
+ // Avoid Illegal Invocation
+ vectorStore.similaritySearchVectorWithScore = async (query: number[], k: number, filter?: string) => {
+ const hasColResp = await vectorStore.client.hasCollection({
+ collection_name: vectorStore.collectionName
+ })
+ if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error checking collection: ${hasColResp}`)
+ }
+ if (hasColResp.value === false) {
+ throw new Error(`Collection not found: ${vectorStore.collectionName}, please create collection before search.`)
+ }
+
+ const filterStr = filter ?? ''
+
+ await vectorStore.grabCollectionFields()
+
+ const loadResp = await vectorStore.client.loadCollectionSync({
+ collection_name: vectorStore.collectionName
+ })
+ if (loadResp.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error loading collection: ${loadResp}`)
+ }
+
+ const outputFields = vectorStore.fields.filter((field) => field !== vectorStore.vectorField)
+
+ const searchResp = await vectorStore.client.search({
+ collection_name: vectorStore.collectionName,
+ search_params: {
+ anns_field: vectorStore.vectorField,
+ topk: k.toString(),
+ metric_type: vectorStore.indexCreateParams.metric_type,
+ params: vectorStore.indexSearchParams
+ },
+ output_fields: outputFields,
+ vector_type: DataType.FloatVector,
+ vectors: [query],
+ filter: filterStr
+ })
+ if (searchResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`)
+ }
+ const results: [Document, number][] = []
+ searchResp.results.forEach((result) => {
+ const fields = {
+ pageContent: '',
+ metadata: {} as Record
+ }
+ Object.keys(result).forEach((key) => {
+ if (key === vectorStore.textField) {
+ fields.pageContent = result[key]
+ } else if (vectorStore.fields.includes(key) || key === vectorStore.primaryField) {
+ if (typeof result[key] === 'string') {
+ const { isJson, obj } = checkJsonString(result[key])
+ fields.metadata[key] = isJson ? obj : result[key]
+ } else {
+ fields.metadata[key] = result[key]
+ }
+ }
+ })
+ results.push([new Document(fields), result.score])
+ })
+ return results
+ }
+
+ if (output === 'retriever') {
+ const retriever = vectorStore.asRetriever(k)
+ return retriever
+ } else if (output === 'vectorStore') {
+ ;(vectorStore as any).k = k
+ return vectorStore
+ }
+ return vectorStore
+ }
+}
+
+function checkJsonString(value: string): { isJson: boolean; obj: any } {
+ try {
+ const result = JSON.parse(value)
+ return { isJson: true, obj: result }
+ } catch (e) {
+ return { isJson: false, obj: null }
+ }
+}
+
+class MilvusUpsert extends Milvus {
+ async addVectors(vectors: number[][], documents: Document[]): Promise {
+ if (vectors.length === 0) {
+ return
+ }
+ await this.ensureCollection(vectors, documents)
+
+ const insertDatas: InsertRow[] = []
+
+ for (let index = 0; index < vectors.length; index++) {
+ const vec = vectors[index]
+ const doc = documents[index]
+ const data: InsertRow = {
+ [this.textField]: doc.pageContent,
+ [this.vectorField]: vec
+ }
+ this.fields.forEach((field) => {
+ switch (field) {
+ case this.primaryField:
+ if (!this.autoId) {
+ if (doc.metadata[this.primaryField] === undefined) {
+ throw new Error(
+ `The Collection's primaryField is configured with autoId=false, thus its value must be provided through metadata.`
+ )
+ }
+ data[field] = doc.metadata[this.primaryField]
+ }
+ break
+ case this.textField:
+ data[field] = doc.pageContent
+ break
+ case this.vectorField:
+ data[field] = vec
+ break
+ default: // metadata fields
+ if (doc.metadata[field] === undefined) {
+ throw new Error(`The field "${field}" is not provided in documents[${index}].metadata.`)
+ } else if (typeof doc.metadata[field] === 'object') {
+ data[field] = JSON.stringify(doc.metadata[field])
+ } else {
+ data[field] = doc.metadata[field]
+ }
+ break
+ }
+ })
+
+ insertDatas.push(data)
+ }
+
+ const descIndexResp = await this.client.describeIndex({
+ collection_name: this.collectionName
+ })
+
+ if (descIndexResp.status.error_code === ErrorCode.INDEX_NOT_EXIST) {
+ const resp = await this.client.createIndex({
+ collection_name: this.collectionName,
+ field_name: this.vectorField,
+ index_name: `myindex_${Date.now().toString()}`,
+ index_type: IndexType.AUTOINDEX,
+ metric_type: MetricType.L2
+ })
+ if (resp.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error creating index`)
+ }
+ }
+
+ const insertResp = await this.client.insert({
+ collection_name: this.collectionName,
+ fields_data: insertDatas
+ })
+
+ if (insertResp.status.error_code !== ErrorCode.SUCCESS) {
+ throw new Error(`Error inserting data: ${JSON.stringify(insertResp)}`)
+ }
+
+ await this.client.flushSync({ collection_names: [this.collectionName] })
+ }
+}
+
+module.exports = { nodeClass: Milvus_Upsert_VectorStores }
diff --git a/packages/components/nodes/vectorstores/Milvus/milvus.svg b/packages/components/nodes/vectorstores/Milvus/milvus.svg
new file mode 100644
index 00000000..68dfef66
--- /dev/null
+++ b/packages/components/nodes/vectorstores/Milvus/milvus.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/components/package.json b/packages/components/package.json
index 2192dba8..a1de20d7 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -28,6 +28,7 @@
"@types/js-yaml": "^4.0.5",
"apify-client": "^2.7.1",
"@types/jsdom": "^21.1.1",
+ "@zilliz/milvus2-sdk-node": "^2.2.24",
"axios": "^0.27.2",
"cheerio": "^1.0.0-rc.12",
"chromadb": "^1.5.3",
diff --git a/packages/ui/README-ZH.md b/packages/ui/README-ZH.md
index 5d33c07f..c6307935 100644
--- a/packages/ui/README-ZH.md
+++ b/packages/ui/README-ZH.md
@@ -16,4 +16,4 @@ npm i flowise-ui
## 许可证
-本仓库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。
\ No newline at end of file
+本仓库中的源代码在[MIT许可证](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md)下提供。