{"version":3,"file":"milvus.cjs","names":["DEFAULT_INDEX_SEARCH_PARAMS: Record<IndexType, IndexSearchParam>","VectorStore","embeddings: EmbeddingsInterface","args: MilvusLibArgs","clientConfig: ClientConfig","MilvusClient","documents: Document[]","options?: { ids?: string[] }","vectors: number[][]","insertDatas: InsertRow[]","data: InsertRow","params: InsertReq","ErrorCode","query: number[]","k: number","filter?: string","DataType","results: [Document, number][]","Document","vectors?: number[][]","documents?: Document[]","fieldList: FieldType[]","DataTypeMap","texts: string[]","metadatas: object[] | object","dbConfig?: MilvusLibArgs","docs: Document[]","dbConfig: MilvusLibArgs","params: { filter?: string; ids?: string[] }","primaryFieldName: string","partitionKey?: string","fields: FieldType[]","value: string"],"sources":["../../src/vectorstores/milvus.ts"],"sourcesContent":["import * as uuid from \"uuid\";\nimport {\n  MilvusClient,\n  DataType,\n  DataTypeMap,\n  ErrorCode,\n  FieldType,\n  ClientConfig,\n  InsertReq,\n  keyValueObj,\n} from \"@zilliz/milvus2-sdk-node\";\n\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\n\n/**\n * Interface for the arguments required by the Milvus class constructor.\n */\nexport interface MilvusLibArgs {\n  collectionName?: string;\n  partitionName?: string;\n  primaryField?: string;\n  vectorField?: string;\n  textField?: string;\n  url?: string; // db address\n  ssl?: boolean;\n  username?: string;\n  password?: string;\n  textFieldMaxLength?: number;\n  clientConfig?: ClientConfig;\n  autoId?: boolean;\n  indexCreateOptions?: IndexCreateOptions;\n  partitionKey?: string; // doc: https://milvus.io/docs/use-partition-key.md\n  partitionKeyMaxLength?: number;\n}\n\nexport interface IndexCreateOptions {\n  index_type: IndexType;\n  metric_type: MetricType;\n  params?: keyValueObj;\n  // index search params\n  search_params?: keyValueObj;\n}\n\nexport type MetricType = \"L2\" | \"IP\" | \"COSINE\";\n\n/**\n * Type representing the type of index used in the Milvus database.\n */\ntype IndexType =\n  | \"FLAT\"\n  | \"IVF_FLAT\"\n  | \"IVF_SQ8\"\n  | \"IVF_PQ\"\n  | \"HNSW\"\n  | \"RHNSW_FLAT\"\n  | \"RHNSW_SQ\"\n  | \"RHNSW_PQ\"\n  | \"IVF_HNSW\"\n  | \"ANNOY\";\n\n/**\n * Interface for vector search parameters.\n */\ninterface IndexSearchParam {\n  params: { nprobe?: number; ef?: number; search_k?: number };\n}\n\ninterface InsertRow {\n  [x: string]: string | number[];\n}\n\nconst MILVUS_PRIMARY_FIELD_NAME = \"langchain_primaryid\";\nconst MILVUS_VECTOR_FIELD_NAME = \"langchain_vector\";\nconst MILVUS_TEXT_FIELD_NAME = \"langchain_text\";\nconst MILVUS_COLLECTION_NAME_PREFIX = \"langchain_col\";\nconst MILVUS_PARTITION_KEY_MAX_LENGTH = 512;\n\n/**\n * Default parameters for index searching.\n */\nconst DEFAULT_INDEX_SEARCH_PARAMS: Record<IndexType, IndexSearchParam> = {\n  FLAT: { params: {} },\n  IVF_FLAT: { params: { nprobe: 10 } },\n  IVF_SQ8: { params: { nprobe: 10 } },\n  IVF_PQ: { params: { nprobe: 10 } },\n  HNSW: { params: { ef: 10 } },\n  RHNSW_FLAT: { params: { ef: 10 } },\n  RHNSW_SQ: { params: { ef: 10 } },\n  RHNSW_PQ: { params: { ef: 10 } },\n  IVF_HNSW: { params: { nprobe: 10, ef: 10 } },\n  ANNOY: { params: { search_k: 10 } },\n};\n\n/**\n * Class for interacting with a Milvus database. Extends the VectorStore\n * class.\n */\nexport class Milvus extends VectorStore {\n  get lc_secrets(): { [key: string]: string } {\n    return {\n      ssl: \"MILVUS_SSL\",\n      username: \"MILVUS_USERNAME\",\n      password: \"MILVUS_PASSWORD\",\n    };\n  }\n\n  _vectorstoreType(): string {\n    return \"milvus\";\n  }\n\n  declare FilterType: string;\n\n  collectionName: string;\n\n  partitionName?: string;\n\n  numDimensions?: number;\n\n  autoId?: boolean;\n\n  primaryField: string;\n\n  vectorField: string;\n\n  textField: string;\n\n  textFieldMaxLength: number;\n\n  partitionKey?: string;\n\n  partitionKeyMaxLength?: number;\n\n  fields: string[];\n\n  client: MilvusClient;\n\n  indexCreateParams: IndexCreateOptions;\n\n  indexSearchParams: keyValueObj;\n\n  constructor(\n    public embeddings: EmbeddingsInterface,\n    args: MilvusLibArgs\n  ) {\n    super(embeddings, args);\n    this.collectionName = args.collectionName ?? genCollectionName();\n    this.partitionName = args.partitionName;\n    this.textField = args.textField ?? MILVUS_TEXT_FIELD_NAME;\n\n    this.autoId = args.autoId ?? true;\n    this.primaryField = args.primaryField ?? MILVUS_PRIMARY_FIELD_NAME;\n    this.vectorField = args.vectorField ?? MILVUS_VECTOR_FIELD_NAME;\n\n    this.textFieldMaxLength = args.textFieldMaxLength ?? 0;\n\n    this.partitionKey = args.partitionKey;\n    this.partitionKeyMaxLength =\n      args.partitionKeyMaxLength ?? MILVUS_PARTITION_KEY_MAX_LENGTH;\n\n    this.fields = [];\n\n    const url = args.url ?? getEnvironmentVariable(\"MILVUS_URL\");\n    const {\n      address = \"\",\n      username = \"\",\n      password = \"\",\n      ssl,\n    } = args.clientConfig || {};\n\n    // Index creation parameters\n    const { indexCreateOptions } = args;\n    if (indexCreateOptions) {\n      const {\n        metric_type,\n        index_type,\n        params,\n        search_params = {},\n      } = indexCreateOptions;\n      this.indexCreateParams = {\n        metric_type,\n        index_type,\n        params,\n      };\n      this.indexSearchParams = {\n        ...DEFAULT_INDEX_SEARCH_PARAMS[index_type].params,\n        ...search_params,\n      };\n    } else {\n      // Default index creation parameters.\n      this.indexCreateParams = {\n        index_type: \"HNSW\",\n        metric_type: \"L2\",\n        params: { M: 8, efConstruction: 64 },\n      };\n      // Default index search parameters.\n      this.indexSearchParams = {\n        ...DEFAULT_INDEX_SEARCH_PARAMS.HNSW.params,\n      };\n    }\n\n    // combine args clientConfig and env variables\n    const clientConfig: ClientConfig = {\n      ...(args.clientConfig || {}),\n      address: url || address,\n      username: args.username || username,\n      password: args.password || password,\n      ssl: args.ssl || ssl,\n    };\n\n    if (!clientConfig.address) {\n      throw new Error(\"Milvus URL address is not provided.\");\n    }\n    this.client = new MilvusClient(clientConfig);\n  }\n\n  /**\n   * Adds documents to the Milvus database.\n   * @param documents Array of Document instances to be added to the database.\n   * @param options Optional parameter that can include specific IDs for the documents.\n   * @returns Promise resolving to void.\n   */\n  async addDocuments(\n    documents: Document[],\n    options?: { ids?: string[] }\n  ): Promise<void> {\n    const texts = documents.map(({ pageContent }) => pageContent);\n    await this.addVectors(\n      await this.embeddings.embedDocuments(texts),\n      documents,\n      options\n    );\n  }\n\n  /**\n   * Adds vectors to the Milvus database.\n   * @param vectors Array of vectors to be added to the database.\n   * @param documents Array of Document instances associated with the vectors.\n   * @param options Optional parameter that can include specific IDs for the documents.\n   * @returns Promise resolving to void.\n   */\n  async addVectors(\n    vectors: number[][],\n    documents: Document[],\n    options?: { ids?: string[] }\n  ): Promise<void> {\n    if (vectors.length === 0) {\n      return;\n    }\n    await this.ensureCollection(vectors, documents);\n    if (this.partitionName !== undefined) {\n      await this.ensurePartition();\n    }\n\n    const documentIds = options?.ids ?? [];\n\n    const insertDatas: InsertRow[] = [];\n    for (let index = 0; index < vectors.length; index++) {\n      const vec = vectors[index];\n      const doc = documents[index];\n      const data: InsertRow = {\n        [this.textField]: doc.pageContent,\n        [this.vectorField]: vec,\n      };\n      this.fields.forEach((field) => {\n        switch (field) {\n          case this.primaryField:\n            if (documentIds[index] !== undefined) {\n              data[field] = documentIds[index];\n            } else if (!this.autoId) {\n              if (doc.metadata[this.primaryField] === undefined) {\n                throw new Error(\n                  `The Collection's primaryField is configured with autoId=false, thus its value must be provided through metadata.`\n                );\n              }\n              data[field] = doc.metadata[this.primaryField];\n            }\n            break;\n          case this.textField:\n            data[field] = doc.pageContent;\n            break;\n          case this.vectorField:\n            data[field] = vec;\n            break;\n          default: // metadata fields\n            if (doc.metadata[field] === undefined) {\n              throw new Error(\n                `The field \"${field}\" is not provided in documents[${index}].metadata.`\n              );\n            } else if (typeof doc.metadata[field] === \"object\") {\n              data[field] = JSON.stringify(doc.metadata[field]);\n            } else {\n              data[field] = doc.metadata[field];\n            }\n            break;\n        }\n      });\n\n      insertDatas.push(data);\n    }\n\n    const params: InsertReq = {\n      collection_name: this.collectionName,\n      fields_data: insertDatas,\n    };\n    if (this.partitionName !== undefined) {\n      params.partition_name = this.partitionName;\n    }\n    const insertResp = this.autoId\n      ? await this.client.insert(params)\n      : await this.client.upsert(params);\n\n    if (insertResp.status.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(\n        `Error ${\n          this.autoId ? \"inserting\" : \"upserting\"\n        } data: ${JSON.stringify(insertResp)}`\n      );\n    }\n    await this.client.flushSync({ collection_names: [this.collectionName] });\n  }\n\n  /**\n   * Searches for vectors in the Milvus database that are similar to a given\n   * vector.\n   * @param query Vector to compare with the vectors in the database.\n   * @param k Number of similar vectors to return.\n   * @param filter Optional filter to apply to the search.\n   * @returns Promise resolving to an array of tuples, each containing a Document instance and a similarity score.\n   */\n  async similaritySearchVectorWithScore(\n    query: number[],\n    k: number,\n    filter?: string\n  ): Promise<[Document, number][]> {\n    const hasColResp = await this.client.hasCollection({\n      collection_name: this.collectionName,\n    });\n    if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(`Error checking collection: ${hasColResp}`);\n    }\n    if (hasColResp.value === false) {\n      throw new Error(\n        `Collection not found: ${this.collectionName}, please create collection before search.`\n      );\n    }\n\n    const filterStr = filter ?? \"\";\n\n    await this.grabCollectionFields();\n\n    const loadResp = await this.client.loadCollectionSync({\n      collection_name: this.collectionName,\n    });\n    if (loadResp.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(`Error loading collection: ${loadResp}`);\n    }\n\n    // clone this.field and remove vectorField\n    const outputFields = this.fields.filter(\n      (field) => field !== this.vectorField\n    );\n\n    const searchResp = await this.client.search({\n      collection_name: this.collectionName,\n      search_params: {\n        anns_field: this.vectorField,\n        topk: k,\n        metric_type: this.indexCreateParams.metric_type,\n        params: JSON.stringify(this.indexSearchParams),\n      },\n      output_fields: outputFields,\n      vector_type: DataType.FloatVector,\n      vectors: [query],\n      filter: filterStr,\n    });\n    if (searchResp.status.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(`Error searching data: ${JSON.stringify(searchResp)}`);\n    }\n    const results: [Document, number][] = [];\n    searchResp.results.forEach((result) => {\n      const fields = {\n        pageContent: \"\",\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        metadata: {} as Record<string, any>,\n      };\n      Object.keys(result).forEach((key) => {\n        if (key === this.textField) {\n          fields.pageContent = result[key];\n        } else if (this.fields.includes(key) || key === this.primaryField) {\n          if (typeof result[key] === \"string\") {\n            const { isJson, obj } = checkJsonString(result[key]);\n            fields.metadata[key] = isJson ? obj : result[key];\n          } else {\n            fields.metadata[key] = result[key];\n          }\n        }\n      });\n      results.push([new Document(fields), result.score]);\n    });\n    // console.log(\"Search result: \" + JSON.stringify(results, null, 2));\n    return results;\n  }\n\n  /**\n   * Ensures that a collection exists in the Milvus database.\n   * @param vectors Optional array of vectors to be used if a new collection needs to be created.\n   * @param documents Optional array of Document instances to be used if a new collection needs to be created.\n   * @returns Promise resolving to void.\n   */\n  async ensureCollection(vectors?: number[][], documents?: Document[]) {\n    const hasColResp = await this.client.hasCollection({\n      collection_name: this.collectionName,\n    });\n    if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(\n        `Error checking collection: ${JSON.stringify(hasColResp, null, 2)}`\n      );\n    }\n\n    if (hasColResp.value === false) {\n      if (vectors === undefined || documents === undefined) {\n        throw new Error(\n          `Collection not found: ${this.collectionName}, please provide vectors and documents to create collection.`\n        );\n      }\n      await this.createCollection(vectors, documents);\n    } else {\n      await this.grabCollectionFields();\n    }\n  }\n\n  /**\n   * Ensures that a partition exists in the Milvus collection.\n   * @returns Promise resolving to void.\n   */\n  async ensurePartition() {\n    if (this.partitionName === undefined) {\n      return;\n    }\n    const hasPartResp = await this.client.hasPartition({\n      collection_name: this.collectionName,\n      partition_name: this.partitionName,\n    });\n    if (hasPartResp.status.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(\n        `Error checking partition: ${JSON.stringify(hasPartResp, null, 2)}`\n      );\n    }\n\n    if (hasPartResp.value === false) {\n      await this.client.createPartition({\n        collection_name: this.collectionName,\n        partition_name: this.partitionName,\n      });\n    }\n  }\n\n  /**\n   * Creates a collection in the Milvus database.\n   * @param vectors Array of vectors to be added to the new collection.\n   * @param documents Array of Document instances to be added to the new collection.\n   * @returns Promise resolving to void.\n   */\n  async createCollection(\n    vectors: number[][],\n    documents: Document[]\n  ): Promise<void> {\n    const fieldList: FieldType[] = [];\n\n    fieldList.push(\n      ...createFieldTypeForMetadata(\n        documents,\n        this.primaryField,\n        this.partitionKey\n      )\n    );\n\n    if (this.autoId) {\n      fieldList.push({\n        name: this.primaryField,\n        description: \"Primary key\",\n        data_type: DataType.Int64,\n        is_primary_key: true,\n        autoID: true,\n      });\n    } else {\n      fieldList.push({\n        name: this.primaryField,\n        description: \"Primary key\",\n        data_type: DataType.VarChar,\n        is_primary_key: true,\n        autoID: false,\n        max_length: 65535,\n      });\n    }\n\n    fieldList.push(\n      {\n        name: this.textField,\n        description: \"Text field\",\n        data_type: DataType.VarChar,\n        type_params: {\n          max_length:\n            this.textFieldMaxLength > 0\n              ? this.textFieldMaxLength.toString()\n              : getTextFieldMaxLength(documents).toString(),\n        },\n      },\n      {\n        name: this.vectorField,\n        description: \"Vector field\",\n        data_type: DataType.FloatVector,\n        type_params: {\n          dim: getVectorFieldDim(vectors).toString(),\n        },\n      }\n    );\n\n    if (this.partitionKey) {\n      fieldList.push({\n        name: this.partitionKey,\n        description: \"Partition key\",\n        data_type: DataType.VarChar,\n        max_length: this.partitionKeyMaxLength,\n        is_partition_key: true,\n      });\n    }\n\n    fieldList.forEach((field) => {\n      if (!field.autoID) {\n        this.fields.push(field.name);\n      }\n    });\n\n    const createRes = await this.client.createCollection({\n      collection_name: this.collectionName,\n      fields: fieldList,\n    });\n\n    if (createRes.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(`Failed to create collection: ${createRes}`);\n    }\n\n    const extraParams = {\n      ...this.indexCreateParams,\n      params: JSON.stringify(this.indexCreateParams.params),\n    };\n    await this.client.createIndex({\n      collection_name: this.collectionName,\n      field_name: this.vectorField,\n      extra_params: extraParams,\n    });\n  }\n\n  /**\n   * Retrieves the fields of a collection in the Milvus database.\n   * @returns Promise resolving to void.\n   */\n  async grabCollectionFields(): Promise<void> {\n    if (!this.collectionName) {\n      throw new Error(\"Need collection name to grab collection fields\");\n    }\n    if (\n      this.primaryField &&\n      this.vectorField &&\n      this.textField &&\n      this.fields.length > 0\n    ) {\n      return;\n    }\n    const desc = await this.client.describeCollection({\n      collection_name: this.collectionName,\n    });\n    desc.schema.fields.forEach((field) => {\n      this.fields.push(field.name);\n      // Remove autoID fields from this.fields if we're using autoId mode\n      // When autoId is false, we need to include the primary field for upsert operations\n      if (field.autoID && this.autoId) {\n        const index = this.fields.indexOf(field.name);\n        if (index !== -1) {\n          this.fields.splice(index, 1);\n        }\n      }\n      // Remove isFunctionOutput fields from this.fields if this field is calculated on server side\n      // When isFunctionOutput is false, we need to include this field for upsert operations\n      if (field.is_function_output) {\n        const index = this.fields.indexOf(field.name);\n        if (index !== -1) {\n          this.fields.splice(index, 1);\n        }\n      }\n      if (field.is_primary_key) {\n        this.primaryField = field.name;\n      }\n      const dtype = DataTypeMap[field.data_type];\n      if (dtype === DataType.FloatVector || dtype === DataType.BinaryVector) {\n        this.vectorField = field.name;\n      }\n\n      if (dtype === DataType.VarChar && field.name === MILVUS_TEXT_FIELD_NAME) {\n        this.textField = field.name;\n      }\n    });\n  }\n\n  /**\n   * Creates a Milvus instance from a set of texts and their associated\n   * metadata.\n   * @param texts Array of texts to be added to the database.\n   * @param metadatas Array of metadata objects associated with the texts.\n   * @param embeddings Embeddings instance used to generate vector embeddings for the texts.\n   * @param dbConfig Optional configuration for the Milvus database.\n   * @returns Promise resolving to a new Milvus instance.\n   */\n  static async fromTexts(\n    texts: string[],\n    metadatas: object[] | object,\n    embeddings: EmbeddingsInterface,\n    dbConfig?: MilvusLibArgs\n  ): Promise<Milvus> {\n    const docs: Document[] = [];\n    for (let i = 0; i < texts.length; i += 1) {\n      const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;\n      const newDoc = new Document({\n        pageContent: texts[i],\n        metadata,\n      });\n      docs.push(newDoc);\n    }\n    return Milvus.fromDocuments(docs, embeddings, dbConfig);\n  }\n\n  /**\n   * Creates a Milvus instance from a set of Document instances.\n   * @param docs Array of Document instances to be added to the database.\n   * @param embeddings Embeddings instance used to generate vector embeddings for the documents.\n   * @param dbConfig Optional configuration for the Milvus database.\n   * @returns Promise resolving to a new Milvus instance.\n   */\n  static async fromDocuments(\n    docs: Document[],\n    embeddings: EmbeddingsInterface,\n    dbConfig?: MilvusLibArgs\n  ): Promise<Milvus> {\n    const args: MilvusLibArgs = {\n      ...dbConfig,\n      collectionName: dbConfig?.collectionName ?? genCollectionName(),\n    };\n    const instance = new this(embeddings, args);\n    await instance.addDocuments(docs);\n    return instance;\n  }\n\n  /**\n   * Creates a Milvus instance from an existing collection in the Milvus\n   * database.\n   * @param embeddings Embeddings instance used to generate vector embeddings for the documents in the collection.\n   * @param dbConfig Configuration for the Milvus database.\n   * @returns Promise resolving to a new Milvus instance.\n   */\n  static async fromExistingCollection(\n    embeddings: EmbeddingsInterface,\n    dbConfig: MilvusLibArgs\n  ): Promise<Milvus> {\n    const instance = new this(embeddings, dbConfig);\n    await instance.ensureCollection();\n    return instance;\n  }\n\n  /**\n   * Deletes data from the Milvus database.\n   * @param params Object containing a filter to apply to the deletion.\n   * @returns Promise resolving to void.\n   */\n  async delete(params: { filter?: string; ids?: string[] }): Promise<void> {\n    const hasColResp = await this.client.hasCollection({\n      collection_name: this.collectionName,\n    });\n    if (hasColResp.status.error_code !== ErrorCode.SUCCESS) {\n      throw new Error(`Error checking collection: ${hasColResp}`);\n    }\n    if (hasColResp.value === false) {\n      throw new Error(\n        `Collection not found: ${this.collectionName}, please create collection before search.`\n      );\n    }\n\n    const { filter, ids } = params;\n\n    if (filter && !ids) {\n      const deleteResp = await this.client.deleteEntities({\n        collection_name: this.collectionName,\n        expr: filter,\n      });\n\n      if (deleteResp.status.error_code !== ErrorCode.SUCCESS) {\n        throw new Error(`Error deleting data: ${JSON.stringify(deleteResp)}`);\n      }\n    } else if (!filter && ids && ids.length > 0) {\n      const deleteResp = await this.client.delete({\n        collection_name: this.collectionName,\n        ids,\n      });\n\n      if (deleteResp.status.error_code !== ErrorCode.SUCCESS) {\n        throw new Error(\n          `Error deleting data with ids: ${JSON.stringify(deleteResp)}`\n        );\n      }\n    }\n  }\n}\n\nfunction createFieldTypeForMetadata(\n  documents: Document[],\n  primaryFieldName: string,\n  partitionKey?: string\n): FieldType[] {\n  const sampleMetadata = documents[0].metadata;\n  let textFieldMaxLength = 0;\n  let jsonFieldMaxLength = 0;\n  documents.forEach(({ metadata }) => {\n    // check all keys name and count in metadata is same as sampleMetadata\n    Object.keys(metadata).forEach((key) => {\n      if (\n        !(key in metadata) ||\n        typeof metadata[key] !== typeof sampleMetadata[key]\n      ) {\n        throw new Error(\n          \"All documents must have same metadata keys and datatype\"\n        );\n      }\n\n      // find max length of string field and json field, cache json string value\n      if (typeof metadata[key] === \"string\") {\n        if (metadata[key].length > textFieldMaxLength) {\n          textFieldMaxLength = metadata[key].length;\n        }\n      } else if (typeof metadata[key] === \"object\") {\n        const json = JSON.stringify(metadata[key]);\n        if (json.length > jsonFieldMaxLength) {\n          jsonFieldMaxLength = json.length;\n        }\n      }\n    });\n  });\n\n  const fields: FieldType[] = [];\n  for (const [key, value] of Object.entries(sampleMetadata)) {\n    const type = typeof value;\n\n    if (key === primaryFieldName || key === partitionKey) {\n      /**\n       * skip primary field and partition key\n       * because we will create primary field and partition key in createCollection\n       *  */\n    } else if (type === \"string\") {\n      fields.push({\n        name: key,\n        description: `Metadata String field`,\n        data_type: DataType.VarChar,\n        type_params: {\n          max_length: textFieldMaxLength.toString(),\n        },\n      });\n    } else if (type === \"number\") {\n      fields.push({\n        name: key,\n        description: `Metadata Number field`,\n        data_type: DataType.Float,\n      });\n    } else if (type === \"boolean\") {\n      fields.push({\n        name: key,\n        description: `Metadata Boolean field`,\n        data_type: DataType.Bool,\n      });\n    } else if (value === null) {\n      // skip\n    } else {\n      // use json for other types\n      try {\n        fields.push({\n          name: key,\n          description: `Metadata JSON field`,\n          data_type: DataType.VarChar,\n          type_params: {\n            max_length: jsonFieldMaxLength.toString(),\n          },\n        });\n      } catch {\n        throw new Error(\"Failed to parse metadata field as JSON\");\n      }\n    }\n  }\n  return fields;\n}\n\nfunction genCollectionName(): string {\n  return `${MILVUS_COLLECTION_NAME_PREFIX}_${uuid.v4().replaceAll(\"-\", \"\")}`;\n}\n\nfunction getTextFieldMaxLength(documents: Document[]) {\n  let textMaxLength = 0;\n  const textEncoder = new TextEncoder();\n  for (let i = 0; i < documents.length; i++) {\n    const text = documents[i].pageContent;\n    const textLengthInBytes = textEncoder.encode(text).length;\n    if (textLengthInBytes > textMaxLength) {\n      textMaxLength = textLengthInBytes;\n    }\n  }\n  return textMaxLength;\n}\n\nfunction getVectorFieldDim(vectors: number[][]) {\n  if (vectors.length === 0) {\n    throw new Error(\"No vectors found\");\n  }\n  return vectors[0].length;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction checkJsonString(value: string): { isJson: boolean; obj: any } {\n  try {\n    const result = JSON.parse(value);\n    return { isJson: true, obj: result };\n  } catch {\n    return { isJson: false, obj: null };\n  }\n}\n"],"mappings":"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"}