unstructured-ingest 0.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of unstructured-ingest might be problematic. Click here for more details.

Files changed (557) hide show
  1. test/__init__.py +0 -0
  2. test/integration/__init__.py +0 -0
  3. test/integration/chunkers/__init__.py +0 -0
  4. test/integration/chunkers/test_chunkers.py +31 -0
  5. test/integration/connectors/__init__.py +0 -0
  6. test/integration/connectors/conftest.py +38 -0
  7. test/integration/connectors/databricks/__init__.py +0 -0
  8. test/integration/connectors/databricks/test_volumes_native.py +269 -0
  9. test/integration/connectors/discord/__init__.py +0 -0
  10. test/integration/connectors/discord/test_discord.py +90 -0
  11. test/integration/connectors/duckdb/__init__.py +0 -0
  12. test/integration/connectors/duckdb/conftest.py +14 -0
  13. test/integration/connectors/duckdb/test_duckdb.py +89 -0
  14. test/integration/connectors/duckdb/test_motherduck.py +95 -0
  15. test/integration/connectors/elasticsearch/__init__.py +0 -0
  16. test/integration/connectors/elasticsearch/conftest.py +34 -0
  17. test/integration/connectors/elasticsearch/test_elasticsearch.py +330 -0
  18. test/integration/connectors/elasticsearch/test_opensearch.py +325 -0
  19. test/integration/connectors/sql/__init__.py +0 -0
  20. test/integration/connectors/sql/test_postgres.py +195 -0
  21. test/integration/connectors/sql/test_singlestore.py +176 -0
  22. test/integration/connectors/sql/test_snowflake.py +238 -0
  23. test/integration/connectors/sql/test_sqlite.py +162 -0
  24. test/integration/connectors/test_astradb.py +217 -0
  25. test/integration/connectors/test_azure_ai_search.py +255 -0
  26. test/integration/connectors/test_chroma.py +120 -0
  27. test/integration/connectors/test_confluence.py +113 -0
  28. test/integration/connectors/test_delta_table.py +185 -0
  29. test/integration/connectors/test_lancedb.py +247 -0
  30. test/integration/connectors/test_milvus.py +203 -0
  31. test/integration/connectors/test_mongodb.py +335 -0
  32. test/integration/connectors/test_neo4j.py +236 -0
  33. test/integration/connectors/test_notion.py +145 -0
  34. test/integration/connectors/test_onedrive.py +118 -0
  35. test/integration/connectors/test_pinecone.py +288 -0
  36. test/integration/connectors/test_qdrant.py +215 -0
  37. test/integration/connectors/test_redis.py +119 -0
  38. test/integration/connectors/test_s3.py +183 -0
  39. test/integration/connectors/test_vectara.py +270 -0
  40. test/integration/connectors/utils/__init__.py +0 -0
  41. test/integration/connectors/utils/constants.py +7 -0
  42. test/integration/connectors/utils/docker.py +151 -0
  43. test/integration/connectors/utils/docker_compose.py +59 -0
  44. test/integration/connectors/utils/validation/__init__.py +0 -0
  45. test/integration/connectors/utils/validation/destination.py +75 -0
  46. test/integration/connectors/utils/validation/equality.py +75 -0
  47. test/integration/connectors/utils/validation/source.py +299 -0
  48. test/integration/connectors/utils/validation/utils.py +36 -0
  49. test/integration/connectors/weaviate/__init__.py +0 -0
  50. test/integration/connectors/weaviate/conftest.py +15 -0
  51. test/integration/connectors/weaviate/test_cloud.py +34 -0
  52. test/integration/connectors/weaviate/test_local.py +131 -0
  53. test/integration/embedders/__init__.py +0 -0
  54. test/integration/embedders/conftest.py +13 -0
  55. test/integration/embedders/test_azure_openai.py +59 -0
  56. test/integration/embedders/test_bedrock.py +103 -0
  57. test/integration/embedders/test_huggingface.py +26 -0
  58. test/integration/embedders/test_mixedbread.py +71 -0
  59. test/integration/embedders/test_octoai.py +77 -0
  60. test/integration/embedders/test_openai.py +76 -0
  61. test/integration/embedders/test_togetherai.py +71 -0
  62. test/integration/embedders/test_vertexai.py +65 -0
  63. test/integration/embedders/test_voyageai.py +65 -0
  64. test/integration/embedders/utils.py +68 -0
  65. test/integration/partitioners/__init__.py +0 -0
  66. test/integration/partitioners/test_partitioner.py +75 -0
  67. test/integration/utils.py +15 -0
  68. test/unit/__init__.py +0 -0
  69. test/unit/embed/__init__.py +0 -0
  70. test/unit/embed/test_mixedbreadai.py +42 -0
  71. test/unit/embed/test_octoai.py +27 -0
  72. test/unit/embed/test_openai.py +20 -0
  73. test/unit/embed/test_vertexai.py +25 -0
  74. test/unit/embed/test_voyageai.py +24 -0
  75. test/unit/test_error.py +27 -0
  76. test/unit/test_logger.py +78 -0
  77. test/unit/test_utils.py +184 -0
  78. test/unit/v2/__init__.py +0 -0
  79. test/unit/v2/chunkers/__init__.py +0 -0
  80. test/unit/v2/chunkers/test_chunkers.py +49 -0
  81. test/unit/v2/connectors/__init__.py +0 -0
  82. test/unit/v2/connectors/test_confluence.py +39 -0
  83. test/unit/v2/embedders/__init__.py +0 -0
  84. test/unit/v2/embedders/test_bedrock.py +36 -0
  85. test/unit/v2/embedders/test_huggingface.py +48 -0
  86. test/unit/v2/embedders/test_mixedbread.py +37 -0
  87. test/unit/v2/embedders/test_octoai.py +35 -0
  88. test/unit/v2/embedders/test_openai.py +35 -0
  89. test/unit/v2/embedders/test_togetherai.py +37 -0
  90. test/unit/v2/embedders/test_vertexai.py +37 -0
  91. test/unit/v2/embedders/test_voyageai.py +38 -0
  92. test/unit/v2/partitioners/__init__.py +0 -0
  93. test/unit/v2/partitioners/test_partitioner.py +63 -0
  94. test/unit/v2/test_interfaces.py +26 -0
  95. test/unit/v2/test_utils.py +82 -0
  96. test/unit/v2/utils/__init__.py +0 -0
  97. test/unit/v2/utils/data_generator.py +32 -0
  98. unstructured_ingest/__init__.py +1 -0
  99. unstructured_ingest/__version__.py +1 -0
  100. unstructured_ingest/cli/__init__.py +14 -0
  101. unstructured_ingest/cli/base/__init__.py +0 -0
  102. unstructured_ingest/cli/base/cmd.py +19 -0
  103. unstructured_ingest/cli/base/dest.py +87 -0
  104. unstructured_ingest/cli/base/src.py +57 -0
  105. unstructured_ingest/cli/cli.py +37 -0
  106. unstructured_ingest/cli/cmd_factory.py +12 -0
  107. unstructured_ingest/cli/cmds/__init__.py +145 -0
  108. unstructured_ingest/cli/cmds/airtable.py +69 -0
  109. unstructured_ingest/cli/cmds/astradb.py +99 -0
  110. unstructured_ingest/cli/cmds/azure_ai_search.py +65 -0
  111. unstructured_ingest/cli/cmds/biomed.py +52 -0
  112. unstructured_ingest/cli/cmds/chroma.py +104 -0
  113. unstructured_ingest/cli/cmds/clarifai.py +71 -0
  114. unstructured_ingest/cli/cmds/confluence.py +69 -0
  115. unstructured_ingest/cli/cmds/databricks_volumes.py +163 -0
  116. unstructured_ingest/cli/cmds/delta_table.py +94 -0
  117. unstructured_ingest/cli/cmds/discord.py +47 -0
  118. unstructured_ingest/cli/cmds/elasticsearch.py +133 -0
  119. unstructured_ingest/cli/cmds/fsspec/__init__.py +0 -0
  120. unstructured_ingest/cli/cmds/fsspec/azure.py +94 -0
  121. unstructured_ingest/cli/cmds/fsspec/box.py +48 -0
  122. unstructured_ingest/cli/cmds/fsspec/dropbox.py +51 -0
  123. unstructured_ingest/cli/cmds/fsspec/fsspec.py +15 -0
  124. unstructured_ingest/cli/cmds/fsspec/gcs.py +71 -0
  125. unstructured_ingest/cli/cmds/fsspec/s3.py +74 -0
  126. unstructured_ingest/cli/cmds/fsspec/sftp.py +58 -0
  127. unstructured_ingest/cli/cmds/github.py +54 -0
  128. unstructured_ingest/cli/cmds/gitlab.py +54 -0
  129. unstructured_ingest/cli/cmds/google_drive.py +49 -0
  130. unstructured_ingest/cli/cmds/hubspot.py +70 -0
  131. unstructured_ingest/cli/cmds/jira.py +71 -0
  132. unstructured_ingest/cli/cmds/kafka.py +102 -0
  133. unstructured_ingest/cli/cmds/local.py +43 -0
  134. unstructured_ingest/cli/cmds/mongodb.py +72 -0
  135. unstructured_ingest/cli/cmds/notion.py +48 -0
  136. unstructured_ingest/cli/cmds/onedrive.py +66 -0
  137. unstructured_ingest/cli/cmds/opensearch.py +117 -0
  138. unstructured_ingest/cli/cmds/outlook.py +67 -0
  139. unstructured_ingest/cli/cmds/pinecone.py +71 -0
  140. unstructured_ingest/cli/cmds/qdrant.py +124 -0
  141. unstructured_ingest/cli/cmds/reddit.py +67 -0
  142. unstructured_ingest/cli/cmds/salesforce.py +58 -0
  143. unstructured_ingest/cli/cmds/sharepoint.py +66 -0
  144. unstructured_ingest/cli/cmds/slack.py +56 -0
  145. unstructured_ingest/cli/cmds/sql.py +66 -0
  146. unstructured_ingest/cli/cmds/vectara.py +66 -0
  147. unstructured_ingest/cli/cmds/weaviate.py +98 -0
  148. unstructured_ingest/cli/cmds/wikipedia.py +40 -0
  149. unstructured_ingest/cli/common.py +7 -0
  150. unstructured_ingest/cli/interfaces.py +663 -0
  151. unstructured_ingest/cli/utils.py +205 -0
  152. unstructured_ingest/connector/__init__.py +0 -0
  153. unstructured_ingest/connector/airtable.py +309 -0
  154. unstructured_ingest/connector/astradb.py +267 -0
  155. unstructured_ingest/connector/azure_ai_search.py +144 -0
  156. unstructured_ingest/connector/biomed.py +320 -0
  157. unstructured_ingest/connector/chroma.py +158 -0
  158. unstructured_ingest/connector/clarifai.py +122 -0
  159. unstructured_ingest/connector/confluence.py +285 -0
  160. unstructured_ingest/connector/databricks_volumes.py +137 -0
  161. unstructured_ingest/connector/delta_table.py +203 -0
  162. unstructured_ingest/connector/discord.py +180 -0
  163. unstructured_ingest/connector/elasticsearch.py +396 -0
  164. unstructured_ingest/connector/fsspec/__init__.py +0 -0
  165. unstructured_ingest/connector/fsspec/azure.py +78 -0
  166. unstructured_ingest/connector/fsspec/box.py +109 -0
  167. unstructured_ingest/connector/fsspec/dropbox.py +160 -0
  168. unstructured_ingest/connector/fsspec/fsspec.py +359 -0
  169. unstructured_ingest/connector/fsspec/gcs.py +82 -0
  170. unstructured_ingest/connector/fsspec/s3.py +62 -0
  171. unstructured_ingest/connector/fsspec/sftp.py +81 -0
  172. unstructured_ingest/connector/git.py +124 -0
  173. unstructured_ingest/connector/github.py +174 -0
  174. unstructured_ingest/connector/gitlab.py +142 -0
  175. unstructured_ingest/connector/google_drive.py +348 -0
  176. unstructured_ingest/connector/hubspot.py +278 -0
  177. unstructured_ingest/connector/jira.py +469 -0
  178. unstructured_ingest/connector/kafka.py +293 -0
  179. unstructured_ingest/connector/local.py +139 -0
  180. unstructured_ingest/connector/mongodb.py +284 -0
  181. unstructured_ingest/connector/notion/__init__.py +0 -0
  182. unstructured_ingest/connector/notion/client.py +248 -0
  183. unstructured_ingest/connector/notion/connector.py +469 -0
  184. unstructured_ingest/connector/notion/helpers.py +584 -0
  185. unstructured_ingest/connector/notion/interfaces.py +32 -0
  186. unstructured_ingest/connector/notion/types/__init__.py +0 -0
  187. unstructured_ingest/connector/notion/types/block.py +96 -0
  188. unstructured_ingest/connector/notion/types/blocks/__init__.py +63 -0
  189. unstructured_ingest/connector/notion/types/blocks/bookmark.py +40 -0
  190. unstructured_ingest/connector/notion/types/blocks/breadcrumb.py +21 -0
  191. unstructured_ingest/connector/notion/types/blocks/bulleted_list_item.py +31 -0
  192. unstructured_ingest/connector/notion/types/blocks/callout.py +94 -0
  193. unstructured_ingest/connector/notion/types/blocks/child_database.py +23 -0
  194. unstructured_ingest/connector/notion/types/blocks/child_page.py +23 -0
  195. unstructured_ingest/connector/notion/types/blocks/code.py +43 -0
  196. unstructured_ingest/connector/notion/types/blocks/column_list.py +35 -0
  197. unstructured_ingest/connector/notion/types/blocks/divider.py +22 -0
  198. unstructured_ingest/connector/notion/types/blocks/embed.py +36 -0
  199. unstructured_ingest/connector/notion/types/blocks/equation.py +23 -0
  200. unstructured_ingest/connector/notion/types/blocks/file.py +49 -0
  201. unstructured_ingest/connector/notion/types/blocks/heading.py +37 -0
  202. unstructured_ingest/connector/notion/types/blocks/image.py +21 -0
  203. unstructured_ingest/connector/notion/types/blocks/link_preview.py +24 -0
  204. unstructured_ingest/connector/notion/types/blocks/link_to_page.py +29 -0
  205. unstructured_ingest/connector/notion/types/blocks/numbered_list.py +29 -0
  206. unstructured_ingest/connector/notion/types/blocks/paragraph.py +31 -0
  207. unstructured_ingest/connector/notion/types/blocks/pdf.py +49 -0
  208. unstructured_ingest/connector/notion/types/blocks/quote.py +37 -0
  209. unstructured_ingest/connector/notion/types/blocks/synced_block.py +57 -0
  210. unstructured_ingest/connector/notion/types/blocks/table.py +63 -0
  211. unstructured_ingest/connector/notion/types/blocks/table_of_contents.py +23 -0
  212. unstructured_ingest/connector/notion/types/blocks/template.py +30 -0
  213. unstructured_ingest/connector/notion/types/blocks/todo.py +42 -0
  214. unstructured_ingest/connector/notion/types/blocks/toggle.py +37 -0
  215. unstructured_ingest/connector/notion/types/blocks/unsupported.py +20 -0
  216. unstructured_ingest/connector/notion/types/blocks/video.py +22 -0
  217. unstructured_ingest/connector/notion/types/database.py +73 -0
  218. unstructured_ingest/connector/notion/types/database_properties/__init__.py +106 -0
  219. unstructured_ingest/connector/notion/types/database_properties/checkbox.py +38 -0
  220. unstructured_ingest/connector/notion/types/database_properties/created_by.py +35 -0
  221. unstructured_ingest/connector/notion/types/database_properties/created_time.py +34 -0
  222. unstructured_ingest/connector/notion/types/database_properties/date.py +41 -0
  223. unstructured_ingest/connector/notion/types/database_properties/email.py +36 -0
  224. unstructured_ingest/connector/notion/types/database_properties/files.py +37 -0
  225. unstructured_ingest/connector/notion/types/database_properties/formula.py +49 -0
  226. unstructured_ingest/connector/notion/types/database_properties/last_edited_by.py +34 -0
  227. unstructured_ingest/connector/notion/types/database_properties/last_edited_time.py +34 -0
  228. unstructured_ingest/connector/notion/types/database_properties/multiselect.py +73 -0
  229. unstructured_ingest/connector/notion/types/database_properties/number.py +49 -0
  230. unstructured_ingest/connector/notion/types/database_properties/people.py +40 -0
  231. unstructured_ingest/connector/notion/types/database_properties/phone_number.py +36 -0
  232. unstructured_ingest/connector/notion/types/database_properties/relation.py +67 -0
  233. unstructured_ingest/connector/notion/types/database_properties/rich_text.py +43 -0
  234. unstructured_ingest/connector/notion/types/database_properties/rollup.py +56 -0
  235. unstructured_ingest/connector/notion/types/database_properties/select.py +68 -0
  236. unstructured_ingest/connector/notion/types/database_properties/status.py +80 -0
  237. unstructured_ingest/connector/notion/types/database_properties/title.py +37 -0
  238. unstructured_ingest/connector/notion/types/database_properties/unique_id.py +50 -0
  239. unstructured_ingest/connector/notion/types/database_properties/url.py +37 -0
  240. unstructured_ingest/connector/notion/types/database_properties/verification.py +78 -0
  241. unstructured_ingest/connector/notion/types/date.py +26 -0
  242. unstructured_ingest/connector/notion/types/file.py +51 -0
  243. unstructured_ingest/connector/notion/types/page.py +45 -0
  244. unstructured_ingest/connector/notion/types/parent.py +66 -0
  245. unstructured_ingest/connector/notion/types/rich_text.py +189 -0
  246. unstructured_ingest/connector/notion/types/user.py +76 -0
  247. unstructured_ingest/connector/onedrive.py +232 -0
  248. unstructured_ingest/connector/opensearch.py +218 -0
  249. unstructured_ingest/connector/outlook.py +285 -0
  250. unstructured_ingest/connector/pinecone.py +140 -0
  251. unstructured_ingest/connector/qdrant.py +144 -0
  252. unstructured_ingest/connector/reddit.py +166 -0
  253. unstructured_ingest/connector/registry.py +109 -0
  254. unstructured_ingest/connector/salesforce.py +301 -0
  255. unstructured_ingest/connector/sharepoint.py +573 -0
  256. unstructured_ingest/connector/slack.py +224 -0
  257. unstructured_ingest/connector/sql.py +199 -0
  258. unstructured_ingest/connector/vectara.py +253 -0
  259. unstructured_ingest/connector/weaviate.py +190 -0
  260. unstructured_ingest/connector/wikipedia.py +208 -0
  261. unstructured_ingest/embed/__init__.py +0 -0
  262. unstructured_ingest/embed/azure_openai.py +31 -0
  263. unstructured_ingest/embed/bedrock.py +193 -0
  264. unstructured_ingest/embed/huggingface.py +52 -0
  265. unstructured_ingest/embed/interfaces.py +117 -0
  266. unstructured_ingest/embed/mixedbreadai.py +233 -0
  267. unstructured_ingest/embed/octoai.py +130 -0
  268. unstructured_ingest/embed/openai.py +116 -0
  269. unstructured_ingest/embed/togetherai.py +106 -0
  270. unstructured_ingest/embed/vertexai.py +126 -0
  271. unstructured_ingest/embed/voyageai.py +130 -0
  272. unstructured_ingest/enhanced_dataclass/__init__.py +4 -0
  273. unstructured_ingest/enhanced_dataclass/core.py +99 -0
  274. unstructured_ingest/enhanced_dataclass/dataclasses.py +54 -0
  275. unstructured_ingest/enhanced_dataclass/json_mixin.py +125 -0
  276. unstructured_ingest/error.py +49 -0
  277. unstructured_ingest/ingest_backoff/__init__.py +3 -0
  278. unstructured_ingest/ingest_backoff/_common.py +102 -0
  279. unstructured_ingest/ingest_backoff/_wrapper.py +122 -0
  280. unstructured_ingest/interfaces.py +852 -0
  281. unstructured_ingest/logger.py +130 -0
  282. unstructured_ingest/main.py +11 -0
  283. unstructured_ingest/pipeline/__init__.py +22 -0
  284. unstructured_ingest/pipeline/copy.py +19 -0
  285. unstructured_ingest/pipeline/doc_factory.py +12 -0
  286. unstructured_ingest/pipeline/interfaces.py +270 -0
  287. unstructured_ingest/pipeline/partition.py +60 -0
  288. unstructured_ingest/pipeline/permissions.py +12 -0
  289. unstructured_ingest/pipeline/pipeline.py +117 -0
  290. unstructured_ingest/pipeline/reformat/__init__.py +0 -0
  291. unstructured_ingest/pipeline/reformat/chunking.py +134 -0
  292. unstructured_ingest/pipeline/reformat/embedding.py +64 -0
  293. unstructured_ingest/pipeline/source.py +77 -0
  294. unstructured_ingest/pipeline/utils.py +6 -0
  295. unstructured_ingest/pipeline/write.py +18 -0
  296. unstructured_ingest/processor.py +93 -0
  297. unstructured_ingest/runner/__init__.py +104 -0
  298. unstructured_ingest/runner/airtable.py +35 -0
  299. unstructured_ingest/runner/astradb.py +34 -0
  300. unstructured_ingest/runner/base_runner.py +89 -0
  301. unstructured_ingest/runner/biomed.py +45 -0
  302. unstructured_ingest/runner/confluence.py +35 -0
  303. unstructured_ingest/runner/delta_table.py +34 -0
  304. unstructured_ingest/runner/discord.py +35 -0
  305. unstructured_ingest/runner/elasticsearch.py +40 -0
  306. unstructured_ingest/runner/fsspec/__init__.py +0 -0
  307. unstructured_ingest/runner/fsspec/azure.py +30 -0
  308. unstructured_ingest/runner/fsspec/box.py +28 -0
  309. unstructured_ingest/runner/fsspec/dropbox.py +30 -0
  310. unstructured_ingest/runner/fsspec/fsspec.py +40 -0
  311. unstructured_ingest/runner/fsspec/gcs.py +28 -0
  312. unstructured_ingest/runner/fsspec/s3.py +28 -0
  313. unstructured_ingest/runner/fsspec/sftp.py +28 -0
  314. unstructured_ingest/runner/github.py +37 -0
  315. unstructured_ingest/runner/gitlab.py +37 -0
  316. unstructured_ingest/runner/google_drive.py +35 -0
  317. unstructured_ingest/runner/hubspot.py +35 -0
  318. unstructured_ingest/runner/jira.py +35 -0
  319. unstructured_ingest/runner/kafka.py +34 -0
  320. unstructured_ingest/runner/local.py +23 -0
  321. unstructured_ingest/runner/mongodb.py +34 -0
  322. unstructured_ingest/runner/notion.py +61 -0
  323. unstructured_ingest/runner/onedrive.py +35 -0
  324. unstructured_ingest/runner/opensearch.py +40 -0
  325. unstructured_ingest/runner/outlook.py +33 -0
  326. unstructured_ingest/runner/reddit.py +35 -0
  327. unstructured_ingest/runner/salesforce.py +33 -0
  328. unstructured_ingest/runner/sharepoint.py +35 -0
  329. unstructured_ingest/runner/slack.py +33 -0
  330. unstructured_ingest/runner/utils.py +47 -0
  331. unstructured_ingest/runner/wikipedia.py +35 -0
  332. unstructured_ingest/runner/writers/__init__.py +48 -0
  333. unstructured_ingest/runner/writers/astradb.py +22 -0
  334. unstructured_ingest/runner/writers/azure_ai_search.py +24 -0
  335. unstructured_ingest/runner/writers/base_writer.py +26 -0
  336. unstructured_ingest/runner/writers/chroma.py +22 -0
  337. unstructured_ingest/runner/writers/clarifai.py +19 -0
  338. unstructured_ingest/runner/writers/databricks_volumes.py +25 -0
  339. unstructured_ingest/runner/writers/delta_table.py +24 -0
  340. unstructured_ingest/runner/writers/elasticsearch.py +24 -0
  341. unstructured_ingest/runner/writers/fsspec/__init__.py +0 -0
  342. unstructured_ingest/runner/writers/fsspec/azure.py +24 -0
  343. unstructured_ingest/runner/writers/fsspec/box.py +21 -0
  344. unstructured_ingest/runner/writers/fsspec/dropbox.py +21 -0
  345. unstructured_ingest/runner/writers/fsspec/gcs.py +19 -0
  346. unstructured_ingest/runner/writers/fsspec/s3.py +21 -0
  347. unstructured_ingest/runner/writers/kafka.py +21 -0
  348. unstructured_ingest/runner/writers/mongodb.py +21 -0
  349. unstructured_ingest/runner/writers/opensearch.py +26 -0
  350. unstructured_ingest/runner/writers/pinecone.py +21 -0
  351. unstructured_ingest/runner/writers/qdrant.py +19 -0
  352. unstructured_ingest/runner/writers/sql.py +22 -0
  353. unstructured_ingest/runner/writers/vectara.py +22 -0
  354. unstructured_ingest/runner/writers/weaviate.py +21 -0
  355. unstructured_ingest/utils/__init__.py +0 -0
  356. unstructured_ingest/utils/chunking.py +56 -0
  357. unstructured_ingest/utils/compression.py +118 -0
  358. unstructured_ingest/utils/data_prep.py +200 -0
  359. unstructured_ingest/utils/dep_check.py +78 -0
  360. unstructured_ingest/utils/google_filetype.py +9 -0
  361. unstructured_ingest/utils/string_and_date_utils.py +49 -0
  362. unstructured_ingest/utils/table.py +73 -0
  363. unstructured_ingest/v2/__init__.py +1 -0
  364. unstructured_ingest/v2/cli/__init__.py +0 -0
  365. unstructured_ingest/v2/cli/base/__init__.py +4 -0
  366. unstructured_ingest/v2/cli/base/cmd.py +269 -0
  367. unstructured_ingest/v2/cli/base/dest.py +85 -0
  368. unstructured_ingest/v2/cli/base/importer.py +34 -0
  369. unstructured_ingest/v2/cli/base/src.py +85 -0
  370. unstructured_ingest/v2/cli/cli.py +24 -0
  371. unstructured_ingest/v2/cli/cmds.py +14 -0
  372. unstructured_ingest/v2/cli/utils/__init__.py +0 -0
  373. unstructured_ingest/v2/cli/utils/click.py +237 -0
  374. unstructured_ingest/v2/cli/utils/model_conversion.py +222 -0
  375. unstructured_ingest/v2/constants.py +2 -0
  376. unstructured_ingest/v2/errors.py +18 -0
  377. unstructured_ingest/v2/interfaces/__init__.py +32 -0
  378. unstructured_ingest/v2/interfaces/connector.py +50 -0
  379. unstructured_ingest/v2/interfaces/downloader.py +89 -0
  380. unstructured_ingest/v2/interfaces/file_data.py +116 -0
  381. unstructured_ingest/v2/interfaces/indexer.py +30 -0
  382. unstructured_ingest/v2/interfaces/process.py +19 -0
  383. unstructured_ingest/v2/interfaces/processor.py +88 -0
  384. unstructured_ingest/v2/interfaces/upload_stager.py +102 -0
  385. unstructured_ingest/v2/interfaces/uploader.py +53 -0
  386. unstructured_ingest/v2/logger.py +126 -0
  387. unstructured_ingest/v2/main.py +11 -0
  388. unstructured_ingest/v2/otel.py +111 -0
  389. unstructured_ingest/v2/pipeline/__init__.py +0 -0
  390. unstructured_ingest/v2/pipeline/interfaces.py +211 -0
  391. unstructured_ingest/v2/pipeline/otel.py +32 -0
  392. unstructured_ingest/v2/pipeline/pipeline.py +384 -0
  393. unstructured_ingest/v2/pipeline/steps/__init__.py +0 -0
  394. unstructured_ingest/v2/pipeline/steps/chunk.py +80 -0
  395. unstructured_ingest/v2/pipeline/steps/download.py +207 -0
  396. unstructured_ingest/v2/pipeline/steps/embed.py +79 -0
  397. unstructured_ingest/v2/pipeline/steps/filter.py +35 -0
  398. unstructured_ingest/v2/pipeline/steps/index.py +86 -0
  399. unstructured_ingest/v2/pipeline/steps/partition.py +79 -0
  400. unstructured_ingest/v2/pipeline/steps/stage.py +65 -0
  401. unstructured_ingest/v2/pipeline/steps/uncompress.py +50 -0
  402. unstructured_ingest/v2/pipeline/steps/upload.py +58 -0
  403. unstructured_ingest/v2/processes/__init__.py +18 -0
  404. unstructured_ingest/v2/processes/chunker.py +124 -0
  405. unstructured_ingest/v2/processes/connector_registry.py +69 -0
  406. unstructured_ingest/v2/processes/connectors/__init__.py +117 -0
  407. unstructured_ingest/v2/processes/connectors/airtable.py +235 -0
  408. unstructured_ingest/v2/processes/connectors/astradb.py +402 -0
  409. unstructured_ingest/v2/processes/connectors/azure_ai_search.py +276 -0
  410. unstructured_ingest/v2/processes/connectors/chroma.py +190 -0
  411. unstructured_ingest/v2/processes/connectors/confluence.py +207 -0
  412. unstructured_ingest/v2/processes/connectors/couchbase.py +334 -0
  413. unstructured_ingest/v2/processes/connectors/databricks/__init__.py +52 -0
  414. unstructured_ingest/v2/processes/connectors/databricks/volumes.py +208 -0
  415. unstructured_ingest/v2/processes/connectors/databricks/volumes_aws.py +87 -0
  416. unstructured_ingest/v2/processes/connectors/databricks/volumes_azure.py +102 -0
  417. unstructured_ingest/v2/processes/connectors/databricks/volumes_gcp.py +85 -0
  418. unstructured_ingest/v2/processes/connectors/databricks/volumes_native.py +86 -0
  419. unstructured_ingest/v2/processes/connectors/delta_table.py +191 -0
  420. unstructured_ingest/v2/processes/connectors/discord.py +158 -0
  421. unstructured_ingest/v2/processes/connectors/duckdb/__init__.py +15 -0
  422. unstructured_ingest/v2/processes/connectors/duckdb/base.py +100 -0
  423. unstructured_ingest/v2/processes/connectors/duckdb/duckdb.py +127 -0
  424. unstructured_ingest/v2/processes/connectors/duckdb/motherduck.py +126 -0
  425. unstructured_ingest/v2/processes/connectors/elasticsearch/__init__.py +19 -0
  426. unstructured_ingest/v2/processes/connectors/elasticsearch/elasticsearch.py +470 -0
  427. unstructured_ingest/v2/processes/connectors/elasticsearch/opensearch.py +195 -0
  428. unstructured_ingest/v2/processes/connectors/fsspec/__init__.py +37 -0
  429. unstructured_ingest/v2/processes/connectors/fsspec/azure.py +197 -0
  430. unstructured_ingest/v2/processes/connectors/fsspec/box.py +170 -0
  431. unstructured_ingest/v2/processes/connectors/fsspec/dropbox.py +168 -0
  432. unstructured_ingest/v2/processes/connectors/fsspec/fsspec.py +332 -0
  433. unstructured_ingest/v2/processes/connectors/fsspec/gcs.py +197 -0
  434. unstructured_ingest/v2/processes/connectors/fsspec/s3.py +185 -0
  435. unstructured_ingest/v2/processes/connectors/fsspec/sftp.py +171 -0
  436. unstructured_ingest/v2/processes/connectors/fsspec/utils.py +17 -0
  437. unstructured_ingest/v2/processes/connectors/gitlab.py +268 -0
  438. unstructured_ingest/v2/processes/connectors/google_drive.py +348 -0
  439. unstructured_ingest/v2/processes/connectors/kafka/__init__.py +17 -0
  440. unstructured_ingest/v2/processes/connectors/kafka/cloud.py +121 -0
  441. unstructured_ingest/v2/processes/connectors/kafka/kafka.py +273 -0
  442. unstructured_ingest/v2/processes/connectors/kafka/local.py +103 -0
  443. unstructured_ingest/v2/processes/connectors/kdbai.py +148 -0
  444. unstructured_ingest/v2/processes/connectors/lancedb/__init__.py +30 -0
  445. unstructured_ingest/v2/processes/connectors/lancedb/aws.py +43 -0
  446. unstructured_ingest/v2/processes/connectors/lancedb/azure.py +43 -0
  447. unstructured_ingest/v2/processes/connectors/lancedb/cloud.py +42 -0
  448. unstructured_ingest/v2/processes/connectors/lancedb/gcp.py +44 -0
  449. unstructured_ingest/v2/processes/connectors/lancedb/lancedb.py +169 -0
  450. unstructured_ingest/v2/processes/connectors/lancedb/local.py +44 -0
  451. unstructured_ingest/v2/processes/connectors/local.py +217 -0
  452. unstructured_ingest/v2/processes/connectors/milvus.py +225 -0
  453. unstructured_ingest/v2/processes/connectors/mongodb.py +361 -0
  454. unstructured_ingest/v2/processes/connectors/neo4j.py +385 -0
  455. unstructured_ingest/v2/processes/connectors/notion/__init__.py +0 -0
  456. unstructured_ingest/v2/processes/connectors/notion/client.py +349 -0
  457. unstructured_ingest/v2/processes/connectors/notion/connector.py +346 -0
  458. unstructured_ingest/v2/processes/connectors/notion/helpers.py +448 -0
  459. unstructured_ingest/v2/processes/connectors/notion/interfaces.py +32 -0
  460. unstructured_ingest/v2/processes/connectors/notion/types/__init__.py +0 -0
  461. unstructured_ingest/v2/processes/connectors/notion/types/block.py +96 -0
  462. unstructured_ingest/v2/processes/connectors/notion/types/blocks/__init__.py +63 -0
  463. unstructured_ingest/v2/processes/connectors/notion/types/blocks/bookmark.py +40 -0
  464. unstructured_ingest/v2/processes/connectors/notion/types/blocks/breadcrumb.py +21 -0
  465. unstructured_ingest/v2/processes/connectors/notion/types/blocks/bulleted_list_item.py +31 -0
  466. unstructured_ingest/v2/processes/connectors/notion/types/blocks/callout.py +94 -0
  467. unstructured_ingest/v2/processes/connectors/notion/types/blocks/child_database.py +23 -0
  468. unstructured_ingest/v2/processes/connectors/notion/types/blocks/child_page.py +23 -0
  469. unstructured_ingest/v2/processes/connectors/notion/types/blocks/code.py +43 -0
  470. unstructured_ingest/v2/processes/connectors/notion/types/blocks/column_list.py +35 -0
  471. unstructured_ingest/v2/processes/connectors/notion/types/blocks/divider.py +22 -0
  472. unstructured_ingest/v2/processes/connectors/notion/types/blocks/embed.py +36 -0
  473. unstructured_ingest/v2/processes/connectors/notion/types/blocks/equation.py +23 -0
  474. unstructured_ingest/v2/processes/connectors/notion/types/blocks/file.py +49 -0
  475. unstructured_ingest/v2/processes/connectors/notion/types/blocks/heading.py +37 -0
  476. unstructured_ingest/v2/processes/connectors/notion/types/blocks/image.py +21 -0
  477. unstructured_ingest/v2/processes/connectors/notion/types/blocks/link_preview.py +24 -0
  478. unstructured_ingest/v2/processes/connectors/notion/types/blocks/link_to_page.py +29 -0
  479. unstructured_ingest/v2/processes/connectors/notion/types/blocks/numbered_list.py +29 -0
  480. unstructured_ingest/v2/processes/connectors/notion/types/blocks/paragraph.py +31 -0
  481. unstructured_ingest/v2/processes/connectors/notion/types/blocks/pdf.py +49 -0
  482. unstructured_ingest/v2/processes/connectors/notion/types/blocks/quote.py +37 -0
  483. unstructured_ingest/v2/processes/connectors/notion/types/blocks/synced_block.py +57 -0
  484. unstructured_ingest/v2/processes/connectors/notion/types/blocks/table.py +63 -0
  485. unstructured_ingest/v2/processes/connectors/notion/types/blocks/table_of_contents.py +23 -0
  486. unstructured_ingest/v2/processes/connectors/notion/types/blocks/template.py +30 -0
  487. unstructured_ingest/v2/processes/connectors/notion/types/blocks/todo.py +42 -0
  488. unstructured_ingest/v2/processes/connectors/notion/types/blocks/toggle.py +37 -0
  489. unstructured_ingest/v2/processes/connectors/notion/types/blocks/unsupported.py +20 -0
  490. unstructured_ingest/v2/processes/connectors/notion/types/blocks/video.py +22 -0
  491. unstructured_ingest/v2/processes/connectors/notion/types/database.py +73 -0
  492. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/__init__.py +106 -0
  493. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/checkbox.py +38 -0
  494. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/created_by.py +35 -0
  495. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/created_time.py +34 -0
  496. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/date.py +41 -0
  497. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/email.py +36 -0
  498. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/files.py +37 -0
  499. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/formula.py +49 -0
  500. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/last_edited_by.py +34 -0
  501. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/last_edited_time.py +34 -0
  502. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/multiselect.py +73 -0
  503. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/number.py +49 -0
  504. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/people.py +41 -0
  505. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/phone_number.py +36 -0
  506. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/relation.py +67 -0
  507. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/rich_text.py +43 -0
  508. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/rollup.py +56 -0
  509. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/select.py +69 -0
  510. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/status.py +81 -0
  511. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/title.py +37 -0
  512. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/unique_id.py +50 -0
  513. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/url.py +37 -0
  514. unstructured_ingest/v2/processes/connectors/notion/types/database_properties/verification.py +78 -0
  515. unstructured_ingest/v2/processes/connectors/notion/types/date.py +29 -0
  516. unstructured_ingest/v2/processes/connectors/notion/types/file.py +54 -0
  517. unstructured_ingest/v2/processes/connectors/notion/types/page.py +45 -0
  518. unstructured_ingest/v2/processes/connectors/notion/types/parent.py +66 -0
  519. unstructured_ingest/v2/processes/connectors/notion/types/rich_text.py +189 -0
  520. unstructured_ingest/v2/processes/connectors/notion/types/user.py +79 -0
  521. unstructured_ingest/v2/processes/connectors/onedrive.py +447 -0
  522. unstructured_ingest/v2/processes/connectors/outlook.py +239 -0
  523. unstructured_ingest/v2/processes/connectors/pinecone.py +277 -0
  524. unstructured_ingest/v2/processes/connectors/qdrant/__init__.py +16 -0
  525. unstructured_ingest/v2/processes/connectors/qdrant/cloud.py +59 -0
  526. unstructured_ingest/v2/processes/connectors/qdrant/local.py +58 -0
  527. unstructured_ingest/v2/processes/connectors/qdrant/qdrant.py +160 -0
  528. unstructured_ingest/v2/processes/connectors/qdrant/server.py +60 -0
  529. unstructured_ingest/v2/processes/connectors/redisdb.py +182 -0
  530. unstructured_ingest/v2/processes/connectors/salesforce.py +303 -0
  531. unstructured_ingest/v2/processes/connectors/sharepoint.py +448 -0
  532. unstructured_ingest/v2/processes/connectors/slack.py +248 -0
  533. unstructured_ingest/v2/processes/connectors/sql/__init__.py +27 -0
  534. unstructured_ingest/v2/processes/connectors/sql/postgres.py +162 -0
  535. unstructured_ingest/v2/processes/connectors/sql/singlestore.py +166 -0
  536. unstructured_ingest/v2/processes/connectors/sql/snowflake.py +210 -0
  537. unstructured_ingest/v2/processes/connectors/sql/sql.py +434 -0
  538. unstructured_ingest/v2/processes/connectors/sql/sqlite.py +168 -0
  539. unstructured_ingest/v2/processes/connectors/utils.py +29 -0
  540. unstructured_ingest/v2/processes/connectors/vectara.py +350 -0
  541. unstructured_ingest/v2/processes/connectors/weaviate/__init__.py +22 -0
  542. unstructured_ingest/v2/processes/connectors/weaviate/cloud.py +165 -0
  543. unstructured_ingest/v2/processes/connectors/weaviate/embedded.py +90 -0
  544. unstructured_ingest/v2/processes/connectors/weaviate/local.py +73 -0
  545. unstructured_ingest/v2/processes/connectors/weaviate/weaviate.py +267 -0
  546. unstructured_ingest/v2/processes/embedder.py +195 -0
  547. unstructured_ingest/v2/processes/filter.py +60 -0
  548. unstructured_ingest/v2/processes/partitioner.py +188 -0
  549. unstructured_ingest/v2/processes/uncompress.py +61 -0
  550. unstructured_ingest/v2/unstructured_api.py +128 -0
  551. unstructured_ingest/v2/utils.py +61 -0
  552. unstructured_ingest-0.3.13.dist-info/LICENSE.md +201 -0
  553. unstructured_ingest-0.3.13.dist-info/METADATA +205 -0
  554. unstructured_ingest-0.3.13.dist-info/RECORD +557 -0
  555. unstructured_ingest-0.3.13.dist-info/WHEEL +5 -0
  556. unstructured_ingest-0.3.13.dist-info/entry_points.txt +2 -0
  557. unstructured_ingest-0.3.13.dist-info/top_level.txt +2 -0
@@ -0,0 +1,288 @@
1
+ import json
2
+ import math
3
+ import os
4
+ import re
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Generator
8
+ from uuid import uuid4
9
+
10
+ import pytest
11
+ from _pytest.fixtures import TopRequest
12
+ from pinecone import Pinecone, ServerlessSpec
13
+ from pinecone.core.openapi.shared.exceptions import NotFoundException
14
+
15
+ from test.integration.connectors.utils.constants import (
16
+ DESTINATION_TAG,
17
+ )
18
+ from test.integration.connectors.utils.validation.destination import (
19
+ StagerValidationConfigs,
20
+ stager_validation,
21
+ )
22
+ from test.integration.utils import requires_env
23
+ from unstructured_ingest.error import DestinationConnectionError
24
+ from unstructured_ingest.v2.interfaces import FileData, SourceIdentifiers
25
+ from unstructured_ingest.v2.logger import logger
26
+ from unstructured_ingest.v2.processes.connectors.pinecone import (
27
+ CONNECTOR_TYPE,
28
+ MAX_QUERY_RESULTS,
29
+ PineconeAccessConfig,
30
+ PineconeConnectionConfig,
31
+ PineconeUploader,
32
+ PineconeUploaderConfig,
33
+ PineconeUploadStager,
34
+ PineconeUploadStagerConfig,
35
+ )
36
+
37
+ METADATA_BYTES_LIMIT = (
38
+ 40960 # 40KB https://docs.pinecone.io/reference/quotas-and-limits#hard-limits
39
+ )
40
+ VECTOR_DIMENSION = 384
41
+ SPEC = {"serverless": {"cloud": "aws", "region": "us-east-1"}}
42
+ ALLOWED_METADATA_FIELD = "text"
43
+ API_KEY = "PINECONE_API_KEY"
44
+
45
+
46
+ def get_api_key() -> str:
47
+ api_key = os.getenv(API_KEY, None)
48
+ assert api_key
49
+ return api_key
50
+
51
+
52
+ def wait_for_delete(client: Pinecone, index_name: str, timeout=60, interval=1) -> None:
53
+ start = time.time()
54
+ while True and time.time() - start < timeout:
55
+ try:
56
+ description = client.describe_index(name=index_name)
57
+ logger.info(f"current index status: {description}")
58
+ except NotFoundException:
59
+ return
60
+ time.sleep(interval)
61
+
62
+ raise TimeoutError("time out waiting for index to delete")
63
+
64
+
65
+ def wait_for_ready(client: Pinecone, index_name: str, timeout=60, interval=1) -> None:
66
+ def is_ready_status():
67
+ description = client.describe_index(name=index_name)
68
+ status = description["status"]
69
+ return status["ready"]
70
+
71
+ start = time.time()
72
+ is_ready = is_ready_status()
73
+ while not is_ready and time.time() - start < timeout:
74
+ time.sleep(interval)
75
+ is_ready = is_ready_status()
76
+ if not is_ready:
77
+ raise TimeoutError("time out waiting for index to be ready")
78
+
79
+
80
+ @pytest.fixture
81
+ def pinecone_index() -> Generator[str, None, None]:
82
+ pinecone = Pinecone(api_key=get_api_key())
83
+ random_id = str(uuid4()).split("-")[0]
84
+ index_name = f"ingest-test-{random_id}"
85
+ assert len(index_name) < 45
86
+ logger.info(f"Creating index: {index_name}")
87
+ try:
88
+ pinecone.create_index(
89
+ name=index_name,
90
+ dimension=384,
91
+ metric="cosine",
92
+ spec=ServerlessSpec(
93
+ cloud="aws",
94
+ region="us-east-1",
95
+ ),
96
+ deletion_protection="disabled",
97
+ )
98
+ wait_for_ready(client=pinecone, index_name=index_name)
99
+ yield index_name
100
+ except Exception as e:
101
+ logger.error(f"failed to create index {index_name}: {e}")
102
+ finally:
103
+ try:
104
+ logger.info(f"deleting index: {index_name}")
105
+ pinecone.delete_index(name=index_name)
106
+ wait_for_delete(client=pinecone, index_name=index_name)
107
+ except NotFoundException:
108
+ return
109
+
110
+
111
+ def validate_pinecone_index(
112
+ index_name: str, expected_num_of_vectors: int, retries=30, interval=1
113
+ ) -> None:
114
+ # Because there's a delay for the index to catch up to the recent writes, add in a retry
115
+ pinecone = Pinecone(api_key=get_api_key())
116
+ index = pinecone.Index(name=index_name)
117
+ vector_count = -1
118
+ for i in range(retries):
119
+ index_stats = index.describe_index_stats()
120
+ vector_count = index_stats["total_vector_count"]
121
+ if vector_count == expected_num_of_vectors:
122
+ logger.info(f"expected {expected_num_of_vectors} == vector count {vector_count}")
123
+ break
124
+ logger.info(
125
+ f"retry attempt {i}: expected {expected_num_of_vectors} != vector count {vector_count}"
126
+ )
127
+ time.sleep(interval)
128
+ assert vector_count == expected_num_of_vectors, (
129
+ f"vector count from index ({vector_count}) doesn't "
130
+ f"match expected number: {expected_num_of_vectors}"
131
+ )
132
+
133
+
134
+ @requires_env(API_KEY)
135
+ @pytest.mark.asyncio
136
+ @pytest.mark.tags(CONNECTOR_TYPE, DESTINATION_TAG)
137
+ async def test_pinecone_destination(pinecone_index: str, upload_file: Path, temp_dir: Path):
138
+ file_data = FileData(
139
+ source_identifiers=SourceIdentifiers(fullpath=upload_file.name, filename=upload_file.name),
140
+ connector_type=CONNECTOR_TYPE,
141
+ identifier="pinecone_mock_id",
142
+ )
143
+ connection_config = PineconeConnectionConfig(
144
+ index_name=pinecone_index,
145
+ access_config=PineconeAccessConfig(api_key=get_api_key()),
146
+ )
147
+ stager_config = PineconeUploadStagerConfig()
148
+ stager = PineconeUploadStager(upload_stager_config=stager_config)
149
+ new_upload_file = stager.run(
150
+ elements_filepath=upload_file,
151
+ output_dir=temp_dir,
152
+ output_filename=upload_file.name,
153
+ file_data=file_data,
154
+ )
155
+
156
+ upload_config = PineconeUploaderConfig()
157
+ uploader = PineconeUploader(connection_config=connection_config, upload_config=upload_config)
158
+ uploader.precheck()
159
+
160
+ uploader.run(path=new_upload_file, file_data=file_data)
161
+ with new_upload_file.open() as f:
162
+ staged_content = json.load(f)
163
+ expected_num_of_vectors = len(staged_content)
164
+ logger.info("validating first upload")
165
+ validate_pinecone_index(
166
+ index_name=pinecone_index, expected_num_of_vectors=expected_num_of_vectors
167
+ )
168
+
169
+ # Rerun uploader and make sure no duplicates exist
170
+ uploader.run(path=new_upload_file, file_data=file_data)
171
+ logger.info("validating second upload")
172
+ validate_pinecone_index(
173
+ index_name=pinecone_index, expected_num_of_vectors=expected_num_of_vectors
174
+ )
175
+
176
+
177
+ @requires_env(API_KEY)
178
+ @pytest.mark.asyncio
179
+ @pytest.mark.tags(CONNECTOR_TYPE, DESTINATION_TAG)
180
+ @pytest.mark.skip(reason="TODO: get this to work")
181
+ async def test_pinecone_destination_large_index(
182
+ pinecone_index: str, upload_file: Path, temp_dir: Path
183
+ ):
184
+ new_file = temp_dir / "large_file.json"
185
+ with upload_file.open() as f:
186
+ upload_content = json.load(f)
187
+
188
+ min_entries = math.ceil((MAX_QUERY_RESULTS * 2) / len(upload_content))
189
+ new_content = (upload_content * min_entries)[: (2 * MAX_QUERY_RESULTS)]
190
+ print(f"Creating large index content with {len(new_content)} records")
191
+ with new_file.open("w") as f:
192
+ json.dump(new_content, f)
193
+
194
+ expected_num_of_vectors = len(new_content)
195
+ file_data = FileData(
196
+ source_identifiers=SourceIdentifiers(fullpath=new_file.name, filename=new_file.name),
197
+ connector_type=CONNECTOR_TYPE,
198
+ identifier="pinecone_mock_id",
199
+ )
200
+ connection_config = PineconeConnectionConfig(
201
+ index_name=pinecone_index,
202
+ access_config=PineconeAccessConfig(api_key=get_api_key()),
203
+ )
204
+ stager_config = PineconeUploadStagerConfig()
205
+ stager = PineconeUploadStager(upload_stager_config=stager_config)
206
+ new_upload_file = stager.run(
207
+ elements_filepath=new_file,
208
+ output_dir=temp_dir,
209
+ output_filename=new_file.name,
210
+ file_data=file_data,
211
+ )
212
+
213
+ upload_config = PineconeUploaderConfig()
214
+ uploader = PineconeUploader(connection_config=connection_config, upload_config=upload_config)
215
+ uploader.precheck()
216
+
217
+ uploader.run(path=new_upload_file, file_data=file_data)
218
+ validate_pinecone_index(
219
+ index_name=pinecone_index, expected_num_of_vectors=expected_num_of_vectors
220
+ )
221
+ # Rerun uploader and make sure no duplicates exist
222
+ uploader.run(path=new_upload_file, file_data=file_data)
223
+ logger.info("validating second upload")
224
+ validate_pinecone_index(
225
+ index_name=pinecone_index, expected_num_of_vectors=expected_num_of_vectors
226
+ )
227
+
228
+
229
+ @requires_env(API_KEY)
230
+ @pytest.mark.tags(CONNECTOR_TYPE, DESTINATION_TAG)
231
+ def test_large_metadata(pinecone_index: str, tmp_path: Path, upload_file: Path):
232
+ stager = PineconeUploadStager()
233
+ uploader = PineconeUploader(
234
+ connection_config=PineconeConnectionConfig(
235
+ access_config=PineconeAccessConfig(api_key=get_api_key()),
236
+ index_name=pinecone_index,
237
+ ),
238
+ upload_config=PineconeUploaderConfig(),
239
+ )
240
+ large_metadata_upload_file = tmp_path / "mock-upload-file.pdf.json"
241
+ large_metadata = {ALLOWED_METADATA_FIELD: "0" * 2 * METADATA_BYTES_LIMIT}
242
+
243
+ with open(upload_file) as file:
244
+ elements = json.load(file)
245
+
246
+ with open(large_metadata_upload_file, "w") as file:
247
+ mock_element = elements[0]
248
+ mock_element["metadata"] = large_metadata
249
+ json.dump([mock_element], file)
250
+
251
+ file_data = FileData(
252
+ source_identifiers=SourceIdentifiers(
253
+ fullpath=large_metadata_upload_file.name, filename=large_metadata_upload_file.name
254
+ ),
255
+ connector_type=CONNECTOR_TYPE,
256
+ identifier="mock-file-data",
257
+ )
258
+ staged_file = stager.run(
259
+ elements_filepath=large_metadata_upload_file,
260
+ file_data=file_data,
261
+ output_dir=tmp_path,
262
+ output_filename=large_metadata_upload_file.name,
263
+ )
264
+ try:
265
+ uploader.run(staged_file, file_data)
266
+ except DestinationConnectionError as e:
267
+ error_line = r"Metadata size is \d+ bytes, which exceeds the limit of \d+ bytes per vector"
268
+ if re.search(re.compile(error_line), str(e)) is None:
269
+ raise e
270
+ raise pytest.fail("Upload request failed due to metadata exceeding limits.")
271
+
272
+ validate_pinecone_index(pinecone_index, 1, interval=5)
273
+
274
+
275
+ @pytest.mark.parametrize("upload_file_str", ["upload_file_ndjson", "upload_file"])
276
+ def test_pinecone_stager(
277
+ request: TopRequest,
278
+ upload_file_str: str,
279
+ tmp_path: Path,
280
+ ):
281
+ upload_file: Path = request.getfixturevalue(upload_file_str)
282
+ stager = PineconeUploadStager()
283
+ stager_validation(
284
+ configs=StagerValidationConfigs(test_id=CONNECTOR_TYPE, expected_count=22),
285
+ input_file=upload_file,
286
+ stager=stager,
287
+ tmp_dir=tmp_path,
288
+ )
@@ -0,0 +1,215 @@
1
+ import json
2
+ import os
3
+ import uuid
4
+ from contextlib import asynccontextmanager
5
+ from pathlib import Path
6
+ from typing import AsyncGenerator
7
+
8
+ import pytest
9
+ from _pytest.fixtures import TopRequest
10
+ from qdrant_client import AsyncQdrantClient
11
+
12
+ from test.integration.connectors.utils.constants import DESTINATION_TAG
13
+ from test.integration.connectors.utils.docker import container_context
14
+ from test.integration.connectors.utils.validation.destination import (
15
+ StagerValidationConfigs,
16
+ stager_validation,
17
+ )
18
+ from test.integration.utils import requires_env
19
+ from unstructured_ingest.v2.interfaces.file_data import FileData, SourceIdentifiers
20
+ from unstructured_ingest.v2.processes.connectors.qdrant.cloud import (
21
+ CloudQdrantAccessConfig,
22
+ CloudQdrantConnectionConfig,
23
+ CloudQdrantUploader,
24
+ CloudQdrantUploaderConfig,
25
+ CloudQdrantUploadStager,
26
+ CloudQdrantUploadStagerConfig,
27
+ )
28
+ from unstructured_ingest.v2.processes.connectors.qdrant.local import (
29
+ CONNECTOR_TYPE as LOCAL_CONNECTOR_TYPE,
30
+ )
31
+ from unstructured_ingest.v2.processes.connectors.qdrant.local import (
32
+ LocalQdrantConnectionConfig,
33
+ LocalQdrantUploader,
34
+ LocalQdrantUploaderConfig,
35
+ LocalQdrantUploadStager,
36
+ LocalQdrantUploadStagerConfig,
37
+ )
38
+ from unstructured_ingest.v2.processes.connectors.qdrant.server import (
39
+ CONNECTOR_TYPE as SERVER_CONNECTOR_TYPE,
40
+ )
41
+ from unstructured_ingest.v2.processes.connectors.qdrant.server import (
42
+ ServerQdrantConnectionConfig,
43
+ ServerQdrantUploader,
44
+ ServerQdrantUploaderConfig,
45
+ ServerQdrantUploadStager,
46
+ ServerQdrantUploadStagerConfig,
47
+ )
48
+
49
+ COLLECTION_NAME = f"test-coll-{uuid.uuid4().hex[:12]}"
50
+ VECTORS_CONFIG = {"size": 384, "distance": "Cosine"}
51
+
52
+
53
+ @asynccontextmanager
54
+ async def qdrant_client(client_params: dict) -> AsyncGenerator[AsyncQdrantClient, None]:
55
+ client = AsyncQdrantClient(**client_params)
56
+ try:
57
+ yield client
58
+ finally:
59
+ await client.close()
60
+
61
+
62
+ async def validate_upload(client: AsyncQdrantClient, upload_file: Path):
63
+ with upload_file.open() as upload_fp:
64
+ elements = json.load(upload_fp)
65
+ expected_point_count = len(elements)
66
+ first_element = elements[0]
67
+ expected_text = first_element["text"]
68
+ embeddings = first_element["embeddings"]
69
+ collection = await client.get_collection(COLLECTION_NAME)
70
+ assert collection.points_count == expected_point_count
71
+
72
+ response = await client.query_points(COLLECTION_NAME, query=embeddings, limit=1)
73
+ assert response.points[0].payload is not None
74
+ assert response.points[0].payload["text"] == expected_text
75
+
76
+
77
+ @pytest.mark.asyncio
78
+ @pytest.mark.tags(LOCAL_CONNECTOR_TYPE, DESTINATION_TAG, "qdrant")
79
+ async def test_qdrant_destination_local(upload_file: Path, tmp_path: Path):
80
+ connection_kwargs = {"path": str(tmp_path / "qdrant")}
81
+ async with qdrant_client(connection_kwargs) as client:
82
+ await client.create_collection(COLLECTION_NAME, vectors_config=VECTORS_CONFIG)
83
+ AsyncQdrantClient(**connection_kwargs)
84
+ stager = LocalQdrantUploadStager(
85
+ upload_stager_config=LocalQdrantUploadStagerConfig(),
86
+ )
87
+ uploader = LocalQdrantUploader(
88
+ connection_config=LocalQdrantConnectionConfig(**connection_kwargs),
89
+ upload_config=LocalQdrantUploaderConfig(collection_name=COLLECTION_NAME),
90
+ )
91
+
92
+ file_data = FileData(
93
+ source_identifiers=SourceIdentifiers(fullpath=upload_file.name, filename=upload_file.name),
94
+ connector_type=LOCAL_CONNECTOR_TYPE,
95
+ identifier="mock-file-data",
96
+ )
97
+
98
+ staged_upload_file = stager.run(
99
+ elements_filepath=upload_file,
100
+ file_data=file_data,
101
+ output_dir=tmp_path,
102
+ output_filename=upload_file.name,
103
+ )
104
+
105
+ if uploader.is_async():
106
+ await uploader.run_async(path=staged_upload_file, file_data=file_data)
107
+ else:
108
+ uploader.run(path=upload_file, file_data=file_data)
109
+ async with qdrant_client(connection_kwargs) as client:
110
+ await validate_upload(client=client, upload_file=upload_file)
111
+
112
+
113
+ @pytest.fixture
114
+ def docker_context():
115
+ with container_context(image="qdrant/qdrant:latest", ports={"6333": "6333"}) as container:
116
+ yield container
117
+
118
+
119
+ @pytest.mark.asyncio
120
+ @pytest.mark.tags(SERVER_CONNECTOR_TYPE, DESTINATION_TAG, "qdrant")
121
+ async def test_qdrant_destination_server(upload_file: Path, tmp_path: Path, docker_context):
122
+ connection_kwargs = {"location": "http://localhost:6333"}
123
+ async with qdrant_client(connection_kwargs) as client:
124
+ await client.create_collection(COLLECTION_NAME, vectors_config=VECTORS_CONFIG)
125
+ AsyncQdrantClient(**connection_kwargs)
126
+ stager = ServerQdrantUploadStager(
127
+ upload_stager_config=ServerQdrantUploadStagerConfig(),
128
+ )
129
+ uploader = ServerQdrantUploader(
130
+ connection_config=ServerQdrantConnectionConfig(**connection_kwargs),
131
+ upload_config=ServerQdrantUploaderConfig(collection_name=COLLECTION_NAME),
132
+ )
133
+
134
+ file_data = FileData(
135
+ source_identifiers=SourceIdentifiers(fullpath=upload_file.name, filename=upload_file.name),
136
+ connector_type=SERVER_CONNECTOR_TYPE,
137
+ identifier="mock-file-data",
138
+ )
139
+
140
+ staged_upload_file = stager.run(
141
+ elements_filepath=upload_file,
142
+ file_data=file_data,
143
+ output_dir=tmp_path,
144
+ output_filename=upload_file.name,
145
+ )
146
+ uploader.precheck()
147
+ if uploader.is_async():
148
+ await uploader.run_async(path=staged_upload_file, file_data=file_data)
149
+ else:
150
+ uploader.run(path=upload_file, file_data=file_data)
151
+ async with qdrant_client(connection_kwargs) as client:
152
+ await validate_upload(client=client, upload_file=upload_file)
153
+
154
+
155
+ @pytest.mark.asyncio
156
+ @pytest.mark.tags(SERVER_CONNECTOR_TYPE, DESTINATION_TAG, "qdrant")
157
+ @requires_env("QDRANT_API_KEY", "QDRANT_SERVER_URL")
158
+ async def test_qdrant_destination_cloud(upload_file: Path, tmp_path: Path):
159
+ server_url = os.environ["QDRANT_SERVER_URL"]
160
+ api_key = os.environ["QDRANT_API_KEY"]
161
+ connection_kwargs = {"location": server_url, "api_key": api_key}
162
+ async with qdrant_client(connection_kwargs) as client:
163
+ await client.create_collection(COLLECTION_NAME, vectors_config=VECTORS_CONFIG)
164
+ AsyncQdrantClient(**connection_kwargs)
165
+
166
+ stager = CloudQdrantUploadStager(
167
+ upload_stager_config=CloudQdrantUploadStagerConfig(),
168
+ )
169
+ uploader = CloudQdrantUploader(
170
+ connection_config=CloudQdrantConnectionConfig(
171
+ url=server_url,
172
+ access_config=CloudQdrantAccessConfig(
173
+ api_key=api_key,
174
+ ),
175
+ ),
176
+ upload_config=CloudQdrantUploaderConfig(collection_name=COLLECTION_NAME),
177
+ )
178
+
179
+ file_data = FileData(
180
+ source_identifiers=SourceIdentifiers(fullpath=upload_file.name, filename=upload_file.name),
181
+ connector_type=SERVER_CONNECTOR_TYPE,
182
+ identifier="mock-file-data",
183
+ )
184
+
185
+ staged_upload_file = stager.run(
186
+ elements_filepath=upload_file,
187
+ file_data=file_data,
188
+ output_dir=tmp_path,
189
+ output_filename=upload_file.name,
190
+ )
191
+ uploader.precheck()
192
+ if uploader.is_async():
193
+ await uploader.run_async(path=staged_upload_file, file_data=file_data)
194
+ else:
195
+ uploader.run(path=staged_upload_file, file_data=file_data)
196
+ async with qdrant_client(connection_kwargs) as client:
197
+ await validate_upload(client=client, upload_file=upload_file)
198
+
199
+
200
+ @pytest.mark.parametrize("upload_file_str", ["upload_file_ndjson", "upload_file"])
201
+ def test_qdrant_stager(
202
+ request: TopRequest,
203
+ upload_file_str: str,
204
+ tmp_path: Path,
205
+ ):
206
+ upload_file: Path = request.getfixturevalue(upload_file_str)
207
+ stager = LocalQdrantUploadStager(
208
+ upload_stager_config=LocalQdrantUploadStagerConfig(),
209
+ )
210
+ stager_validation(
211
+ configs=StagerValidationConfigs(test_id=LOCAL_CONNECTOR_TYPE, expected_count=22),
212
+ input_file=upload_file,
213
+ stager=stager,
214
+ tmp_dir=tmp_path,
215
+ )
@@ -0,0 +1,119 @@
1
+ import asyncio
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ import numpy as np
8
+ import pytest
9
+ from redis import exceptions as redis_exceptions
10
+ from redis.asyncio import Redis, from_url
11
+
12
+ from test.integration.connectors.utils.constants import DESTINATION_TAG
13
+ from test.integration.utils import requires_env
14
+ from unstructured_ingest.v2.interfaces.file_data import FileData, SourceIdentifiers
15
+ from unstructured_ingest.v2.processes.connectors.redisdb import (
16
+ CONNECTOR_TYPE as REDIS_CONNECTOR_TYPE,
17
+ )
18
+ from unstructured_ingest.v2.processes.connectors.redisdb import (
19
+ RedisAccessConfig,
20
+ RedisConnectionConfig,
21
+ RedisUploader,
22
+ RedisUploaderConfig,
23
+ )
24
+
25
+
26
+ async def delete_record(client: Redis, element_id: str) -> None:
27
+ await client.delete(element_id)
28
+
29
+
30
+ async def validate_upload(client: Redis, first_element: dict):
31
+ element_id = first_element["element_id"]
32
+ expected_text = first_element["text"]
33
+ expected_embeddings = first_element["embeddings"]
34
+ async with client.pipeline(transaction=True) as pipe:
35
+ try:
36
+ response = await pipe.json().get(element_id, "$").execute()
37
+ response = response[0][0]
38
+ except redis_exceptions.ResponseError:
39
+ response = await pipe.get(element_id).execute()
40
+ response = json.loads(response[0])
41
+
42
+ embedding_similarity = np.linalg.norm(
43
+ np.array(response["embeddings"]) - np.array(expected_embeddings)
44
+ )
45
+
46
+ assert response is not None
47
+ assert response["element_id"] == element_id
48
+ assert response["text"] == expected_text
49
+ assert embedding_similarity < 1e-10
50
+
51
+
52
+ async def redis_destination_test(
53
+ upload_file: Path,
54
+ tmp_path: Path,
55
+ connection_kwargs: dict,
56
+ uri: Optional[str] = None,
57
+ password: Optional[str] = None,
58
+ ):
59
+ uploader = RedisUploader(
60
+ connection_config=RedisConnectionConfig(
61
+ **connection_kwargs, access_config=RedisAccessConfig(uri=uri, password=password)
62
+ ),
63
+ upload_config=RedisUploaderConfig(batch_size=10),
64
+ )
65
+
66
+ file_data = FileData(
67
+ source_identifiers=SourceIdentifiers(fullpath=upload_file.name, filename=upload_file.name),
68
+ connector_type=REDIS_CONNECTOR_TYPE,
69
+ identifier="mock-file-data",
70
+ )
71
+ with upload_file.open() as upload_fp:
72
+ elements = json.load(upload_fp)
73
+ first_element = elements[0]
74
+
75
+ try:
76
+ if uploader.is_async():
77
+ await uploader.run_data_async(data=elements, file_data=file_data)
78
+
79
+ if uri:
80
+ async with from_url(uri) as client:
81
+ await validate_upload(client=client, first_element=first_element)
82
+ else:
83
+ async with Redis(**connection_kwargs, password=password) as client:
84
+ await validate_upload(client=client, first_element=first_element)
85
+ except Exception as e:
86
+ raise e
87
+ finally:
88
+ if uri:
89
+ async with from_url(uri) as client:
90
+ tasks = [delete_record(client, element["element_id"]) for element in elements]
91
+ await asyncio.gather(*tasks)
92
+ else:
93
+ async with Redis(**connection_kwargs, password=password) as client:
94
+ tasks = [delete_record(client, element["element_id"]) for element in elements]
95
+ await asyncio.gather(*tasks)
96
+
97
+
98
+ @pytest.mark.asyncio
99
+ @pytest.mark.tags(REDIS_CONNECTOR_TYPE, DESTINATION_TAG)
100
+ @requires_env("AZURE_REDIS_INGEST_TEST_PASSWORD")
101
+ async def test_redis_destination_azure_with_password(upload_file: Path, tmp_path: Path):
102
+ connection_kwargs = {
103
+ "host": "utic-dashboard-dev.redis.cache.windows.net",
104
+ "port": 6380,
105
+ "db": 0,
106
+ "ssl": True,
107
+ }
108
+ redis_pw = os.environ["AZURE_REDIS_INGEST_TEST_PASSWORD"]
109
+ await redis_destination_test(upload_file, tmp_path, connection_kwargs, password=redis_pw)
110
+
111
+
112
+ @pytest.mark.asyncio
113
+ @pytest.mark.tags(REDIS_CONNECTOR_TYPE, DESTINATION_TAG, "redis")
114
+ @requires_env("AZURE_REDIS_INGEST_TEST_PASSWORD")
115
+ async def test_redis_destination_azure_with_uri(upload_file: Path, tmp_path: Path):
116
+ connection_kwargs = {}
117
+ redis_pw = os.environ["AZURE_REDIS_INGEST_TEST_PASSWORD"]
118
+ uri = f"rediss://:{redis_pw}@utic-dashboard-dev.redis.cache.windows.net:6380/0"
119
+ await redis_destination_test(upload_file, tmp_path, connection_kwargs, uri=uri)