morphik 0.1.9__tar.gz → 0.2.0__tar.gz

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.
@@ -1,14 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: morphik
3
- Version: 0.1.9
3
+ Version: 0.2.0
4
4
  Summary: Morphik Python Client
5
5
  Author-email: Morphik <founders@morphik.ai>
6
6
  Requires-Python: >=3.8
7
+ Requires-Dist: build>=1.2.2.post1
7
8
  Requires-Dist: httpx>=0.24.0
8
9
  Requires-Dist: pillow>=10.4.0
9
10
  Requires-Dist: pydantic>=2.10.3
10
11
  Requires-Dist: pyjwt>=2.0.0
11
12
  Requires-Dist: requests>=2.32.3
13
+ Requires-Dist: twine>=6.1.0
12
14
  Description-Content-Type: text/markdown
13
15
 
14
16
  # Morphik
@@ -172,8 +172,14 @@ class _MorphikClientLogic:
172
172
  rules: Optional[List[RuleOrDict]],
173
173
  folder_name: Optional[str],
174
174
  end_user_id: Optional[str],
175
+ use_colpali: Optional[bool] = None,
175
176
  ) -> Dict[str, Any]:
176
- """Prepare form data for ingest_file endpoint"""
177
+ """Prepare form data for ingest_file endpoint.
178
+
179
+ All parameters are included in the multipart body so that the server
180
+ never relies on query-string values. *use_colpali* is therefore always
181
+ embedded here when provided.
182
+ """
177
183
  form_data = {
178
184
  "metadata": json.dumps(metadata or {}),
179
185
  "rules": json.dumps([self._convert_rule(r) for r in (rules or [])]),
@@ -182,6 +188,12 @@ class _MorphikClientLogic:
182
188
  form_data["folder_name"] = folder_name
183
189
  if end_user_id:
184
190
  form_data["end_user_id"] = end_user_id
191
+
192
+ # Only include the flag when caller supplied a specific value to avoid
193
+ # overriding server defaults unintentionally.
194
+ if use_colpali is not None:
195
+ form_data["use_colpali"] = str(use_colpali).lower()
196
+
185
197
  return form_data
186
198
 
187
199
  def _prepare_ingest_files_form_data(
@@ -208,10 +220,15 @@ class _MorphikClientLogic:
208
220
  data = {
209
221
  "metadata": json.dumps(metadata or {}),
210
222
  "rules": json.dumps(converted_rules),
211
- # use_colpali is a query parameter, not a form field
212
223
  "parallel": str(parallel).lower(),
213
224
  }
214
225
 
226
+ # Always carry use_colpali in the body for consistency with single-file
227
+ # ingestion. The API treats missing values as "true" for backward
228
+ # compatibility, hence we only add it when explicitly provided.
229
+ if use_colpali is not None:
230
+ data["use_colpali"] = str(use_colpali).lower()
231
+
215
232
  if folder_name:
216
233
  data["folder_name"] = folder_name
217
234
  if end_user_id:
@@ -234,6 +251,7 @@ class _MorphikClientLogic:
234
251
  prompt_overrides: Optional[Dict],
235
252
  folder_name: Optional[Union[str, List[str]]],
236
253
  end_user_id: Optional[str],
254
+ chat_id: Optional[str] = None,
237
255
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
238
256
  ) -> Dict[str, Any]:
239
257
  """Prepare request for query endpoint"""
@@ -254,6 +272,8 @@ class _MorphikClientLogic:
254
272
  payload["folder_name"] = folder_name
255
273
  if end_user_id:
256
274
  payload["end_user_id"] = end_user_id
275
+ if chat_id:
276
+ payload["chat_id"] = chat_id
257
277
 
258
278
  # Add schema to payload if provided
259
279
  if schema:
@@ -163,14 +163,15 @@ class AsyncFolder:
163
163
  files = {"file": (filename, file_obj)}
164
164
 
165
165
  # Create form data
166
- form_data = self._client._logic._prepare_ingest_file_form_data(metadata, rules, self._name, None)
166
+ form_data = self._client._logic._prepare_ingest_file_form_data(
167
+ metadata, rules, self._name, None, use_colpali
168
+ )
167
169
 
168
170
  response = await self._client._request(
169
171
  "POST",
170
172
  "ingest/file",
171
173
  data=form_data,
172
174
  files=files,
173
- params={"use_colpali": str(use_colpali).lower()},
174
175
  )
175
176
  doc = self._client._logic._parse_document_response(response)
176
177
  doc._client = self._client
@@ -215,7 +216,6 @@ class AsyncFolder:
215
216
  "ingest/files",
216
217
  data=data,
217
218
  files=file_objects,
218
- params={"use_colpali": str(use_colpali).lower()},
219
219
  )
220
220
 
221
221
  if response.get("errors"):
@@ -354,6 +354,7 @@ class AsyncFolder:
354
354
  prompt_overrides: Optional[Union[QueryPromptOverrides, Dict[str, Any]]] = None,
355
355
  additional_folders: Optional[List[str]] = None,
356
356
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
357
+ chat_id: Optional[str] = None,
357
358
  ) -> CompletionResponse:
358
359
  """
359
360
  Generate completion using relevant chunks as context within this folder.
@@ -391,6 +392,7 @@ class AsyncFolder:
391
392
  prompt_overrides,
392
393
  effective_folder,
393
394
  None,
395
+ chat_id,
394
396
  schema,
395
397
  )
396
398
 
@@ -474,9 +476,7 @@ class AsyncFolder:
474
476
  List[FinalChunkResult]: List of chunk results
475
477
  """
476
478
  merged = self._merge_folders(additional_folders)
477
- request = self._client._logic._prepare_batch_get_chunks_request(
478
- sources, merged, None, use_colpali
479
- )
479
+ request = self._client._logic._prepare_batch_get_chunks_request(sources, merged, None, use_colpali)
480
480
  response = await self._client._request("POST", "batch/chunks", data=request)
481
481
  return self._client._logic._parse_chunk_result_list_response(response)
482
482
 
@@ -670,14 +670,14 @@ class AsyncUserScope:
670
670
  # Prepare multipart form data
671
671
  files = {"file": (filename, file_obj)}
672
672
 
673
- # Add metadata and rules
673
+ # Add metadata, rules and scoping information
674
674
  data = {
675
675
  "metadata": json.dumps(metadata or {}),
676
676
  "rules": json.dumps([self._client._convert_rule(r) for r in (rules or [])]),
677
- "end_user_id": self._end_user_id, # Add end user ID here
677
+ "end_user_id": self._end_user_id,
678
+ "use_colpali": str(use_colpali).lower(),
678
679
  }
679
680
 
680
- # Add folder name if scoped to a folder
681
681
  if self._folder_name:
682
682
  data["folder_name"] = self._folder_name
683
683
 
@@ -738,9 +738,9 @@ class AsyncUserScope:
738
738
  data = {
739
739
  "metadata": json.dumps(metadata or {}),
740
740
  "rules": json.dumps(converted_rules),
741
- "use_colpali": str(use_colpali).lower() if use_colpali is not None else None,
742
741
  "parallel": str(parallel).lower(),
743
- "end_user_id": self._end_user_id, # Add end user ID here
742
+ "end_user_id": self._end_user_id,
743
+ "use_colpali": str(use_colpali).lower(),
744
744
  }
745
745
 
746
746
  # Add folder name if scoped to a folder
@@ -752,7 +752,6 @@ class AsyncUserScope:
752
752
  "ingest/files",
753
753
  data=data,
754
754
  files=file_objects,
755
- params={"use_colpali": str(use_colpali).lower()},
756
755
  )
757
756
 
758
757
  if response.get("errors"):
@@ -891,6 +890,7 @@ class AsyncUserScope:
891
890
  prompt_overrides: Optional[Union[QueryPromptOverrides, Dict[str, Any]]] = None,
892
891
  additional_folders: Optional[List[str]] = None,
893
892
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
893
+ chat_id: Optional[str] = None,
894
894
  ) -> CompletionResponse:
895
895
  """
896
896
  Generate completion using relevant chunks as context, scoped to the end user.
@@ -928,6 +928,7 @@ class AsyncUserScope:
928
928
  prompt_overrides,
929
929
  effective_folder,
930
930
  self._end_user_id,
931
+ chat_id,
931
932
  schema,
932
933
  )
933
934
 
@@ -1345,14 +1346,13 @@ class AsyncMorphik:
1345
1346
  files = {"file": (filename, file_obj)}
1346
1347
 
1347
1348
  # Create form data
1348
- form_data = self._logic._prepare_ingest_file_form_data(metadata, rules, None, None)
1349
+ form_data = self._logic._prepare_ingest_file_form_data(metadata, rules, None, None, use_colpali)
1349
1350
 
1350
1351
  response = await self._request(
1351
1352
  "POST",
1352
1353
  "ingest/file",
1353
1354
  data=form_data,
1354
1355
  files=files,
1355
- params={"use_colpali": str(use_colpali).lower()},
1356
1356
  )
1357
1357
  doc = self._logic._parse_document_response(response)
1358
1358
  doc._client = self
@@ -1398,7 +1398,6 @@ class AsyncMorphik:
1398
1398
  "ingest/files",
1399
1399
  data=data,
1400
1400
  files=file_objects,
1401
- params={"use_colpali": str(use_colpali).lower()},
1402
1401
  )
1403
1402
 
1404
1403
  if response.get("errors"):
@@ -1554,6 +1553,7 @@ class AsyncMorphik:
1554
1553
  include_paths: bool = False,
1555
1554
  prompt_overrides: Optional[Union[QueryPromptOverrides, Dict[str, Any]]] = None,
1556
1555
  folder_name: Optional[Union[str, List[str]]] = None,
1556
+ chat_id: Optional[str] = None,
1557
1557
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
1558
1558
  ) -> CompletionResponse:
1559
1559
  """
@@ -1659,6 +1659,7 @@ class AsyncMorphik:
1659
1659
  prompt_overrides,
1660
1660
  effective_folder,
1661
1661
  None,
1662
+ chat_id,
1662
1663
  schema,
1663
1664
  )
1664
1665
 
@@ -1676,6 +1677,45 @@ class AsyncMorphik:
1676
1677
  response = await self._request("POST", "query", data=payload)
1677
1678
  return self._logic._parse_completion_response(response)
1678
1679
 
1680
+ async def agent_query(self, query: str) -> Dict[str, Any]:
1681
+ """
1682
+ Execute an agentic query with tool access and conversation handling.
1683
+
1684
+ The agent can autonomously use various tools to answer complex queries including:
1685
+ - Searching and retrieving relevant documents
1686
+ - Analyzing document content
1687
+ - Performing calculations and data processing
1688
+ - Creating summaries and reports
1689
+ - Managing knowledge graphs
1690
+
1691
+ Args:
1692
+ query: Natural language query for the Morphik agent
1693
+
1694
+ Returns:
1695
+ Dict[str, Any]: Agent response with potential tool execution results and sources
1696
+
1697
+ Example:
1698
+ ```python
1699
+ # Simple query
1700
+ result = await db.agent_query("What are the main trends in our Q3 sales data?")
1701
+ print(result["response"])
1702
+
1703
+ # Complex analysis request
1704
+ result = await db.agent_query(
1705
+ "Analyze all documents from the marketing department, "
1706
+ "identify key performance metrics, and create a summary "
1707
+ "with actionable insights"
1708
+ )
1709
+ print(result["response"])
1710
+
1711
+ # Tool usage is automatic - the agent will decide which tools to use
1712
+ # based on the query requirements
1713
+ ```
1714
+ """
1715
+ request = {"query": query}
1716
+ response = await self._request("POST", "agent", data=request)
1717
+ return response
1718
+
1679
1719
  async def list_documents(
1680
1720
  self,
1681
1721
  skip: int = 0,
@@ -2225,9 +2265,7 @@ class AsyncMorphik:
2225
2265
  print(f"Chunk from {chunk.document_id}, number {chunk.chunk_number}: {chunk.content[:50]}...")
2226
2266
  ```
2227
2267
  """
2228
- request = self._logic._prepare_batch_get_chunks_request(
2229
- sources, folder_name, None, use_colpali
2230
- )
2268
+ request = self._logic._prepare_batch_get_chunks_request(sources, folder_name, None, use_colpali)
2231
2269
  response = await self._request("POST", "batch/chunks", data=request)
2232
2270
  return self._logic._parse_chunk_result_list_response(response)
2233
2271
 
@@ -163,15 +163,16 @@ class Folder:
163
163
  files = {"file": (filename, file_obj)}
164
164
 
165
165
  # Create form data
166
- form_data = self._client._logic._prepare_ingest_file_form_data(metadata, rules, self._name, None)
166
+ form_data = self._client._logic._prepare_ingest_file_form_data(
167
+ metadata, rules, self._name, None, use_colpali
168
+ )
167
169
 
168
- # use_colpali should be a query parameter as defined in the API
170
+ # use_colpali flag is included in multipart form data for consistency
169
171
  response = self._client._request(
170
172
  "POST",
171
173
  "ingest/file",
172
174
  data=form_data,
173
175
  files=files,
174
- params={"use_colpali": str(use_colpali).lower()},
175
176
  )
176
177
  doc = self._client._logic._parse_document_response(response)
177
178
  doc._client = self._client
@@ -216,7 +217,6 @@ class Folder:
216
217
  "ingest/files",
217
218
  data=data,
218
219
  files=file_objects,
219
- params={"use_colpali": str(use_colpali).lower()},
220
220
  )
221
221
 
222
222
  if response.get("errors"):
@@ -367,6 +367,7 @@ class Folder:
367
367
  prompt_overrides: Optional[Union[QueryPromptOverrides, Dict[str, Any]]] = None,
368
368
  additional_folders: Optional[List[str]] = None,
369
369
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
370
+ chat_id: Optional[str] = None,
370
371
  ) -> CompletionResponse:
371
372
  """
372
373
  Generate completion using relevant chunks as context within this folder.
@@ -404,6 +405,7 @@ class Folder:
404
405
  prompt_overrides,
405
406
  effective_folder,
406
407
  None, # end_user_id not supported at this level
408
+ chat_id,
407
409
  schema,
408
410
  )
409
411
 
@@ -488,9 +490,7 @@ class Folder:
488
490
  List[FinalChunkResult]: List of chunk results
489
491
  """
490
492
  merged = self._merge_folders(additional_folders)
491
- request = self._client._logic._prepare_batch_get_chunks_request(
492
- sources, merged, None, use_colpali
493
- )
493
+ request = self._client._logic._prepare_batch_get_chunks_request(sources, merged, None, use_colpali)
494
494
 
495
495
  response = self._client._request("POST", "batch/chunks", data=request)
496
496
  return self._client._logic._parse_chunk_result_list_response(response)
@@ -703,24 +703,22 @@ class UserScope:
703
703
  # Prepare multipart form data
704
704
  files = {"file": (filename, file_obj)}
705
705
 
706
- # Add metadata and rules
706
+ # Add metadata, rules and scoping information
707
707
  form_data = {
708
708
  "metadata": json.dumps(metadata or {}),
709
709
  "rules": json.dumps([self._client._convert_rule(r) for r in (rules or [])]),
710
- "end_user_id": self._end_user_id, # Add end user ID here
710
+ "end_user_id": self._end_user_id,
711
+ "use_colpali": str(use_colpali).lower(),
711
712
  }
712
713
 
713
- # Add folder name if scoped to a folder
714
714
  if self._folder_name:
715
715
  form_data["folder_name"] = self._folder_name
716
716
 
717
- # use_colpali should be a query parameter as defined in the API
718
717
  response = self._client._request(
719
718
  "POST",
720
719
  "ingest/file",
721
720
  data=form_data,
722
721
  files=files,
723
- params={"use_colpali": str(use_colpali).lower()},
724
722
  )
725
723
  doc = self._client._logic._parse_document_response(response)
726
724
  doc._client = self._client
@@ -778,9 +776,9 @@ class UserScope:
778
776
  data = {
779
777
  "metadata": json.dumps(metadata or {}),
780
778
  "rules": json.dumps(converted_rules),
781
- # Remove use_colpali from form data - it should be a query param
782
779
  "parallel": str(parallel).lower(),
783
780
  "end_user_id": self._end_user_id, # Add end user ID here
781
+ "use_colpali": str(use_colpali).lower(),
784
782
  }
785
783
 
786
784
  # Add folder name if scoped to a folder
@@ -792,7 +790,6 @@ class UserScope:
792
790
  "ingest/files",
793
791
  data=data,
794
792
  files=file_objects,
795
- params={"use_colpali": str(use_colpali).lower()},
796
793
  )
797
794
 
798
795
  if response.get("errors"):
@@ -953,6 +950,7 @@ class UserScope:
953
950
  prompt_overrides: Optional[Union[QueryPromptOverrides, Dict[str, Any]]] = None,
954
951
  additional_folders: Optional[List[str]] = None,
955
952
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
953
+ chat_id: Optional[str] = None,
956
954
  ) -> CompletionResponse:
957
955
  """
958
956
  Generate completion using relevant chunks as context as this end user.
@@ -990,6 +988,7 @@ class UserScope:
990
988
  prompt_overrides,
991
989
  effective_folder,
992
990
  self._end_user_id,
991
+ chat_id,
993
992
  schema,
994
993
  )
995
994
 
@@ -1088,9 +1087,7 @@ class UserScope:
1088
1087
  List[FinalChunkResult]: List of chunk results
1089
1088
  """
1090
1089
  merged = self._merge_folders(additional_folders)
1091
- request = self._client._logic._prepare_batch_get_chunks_request(
1092
- sources, merged, None, use_colpali
1093
- )
1090
+ request = self._client._logic._prepare_batch_get_chunks_request(sources, merged, None, use_colpali)
1094
1091
 
1095
1092
  response = self._client._request("POST", "batch/chunks", data=request)
1096
1093
  return self._client._logic._parse_chunk_result_list_response(response)
@@ -1493,7 +1490,7 @@ class Morphik:
1493
1490
  files = {"file": (filename, file_obj)}
1494
1491
 
1495
1492
  # Create form data
1496
- form_data = self._logic._prepare_ingest_file_form_data(metadata, rules, None, None)
1493
+ form_data = self._logic._prepare_ingest_file_form_data(metadata, rules, None, None, use_colpali)
1497
1494
 
1498
1495
  # use_colpali should be a query parameter as defined in the API
1499
1496
  response = self._request(
@@ -1501,7 +1498,6 @@ class Morphik:
1501
1498
  "ingest/file",
1502
1499
  data=form_data,
1503
1500
  files=files,
1504
- params={"use_colpali": str(use_colpali).lower()},
1505
1501
  )
1506
1502
  doc = self._logic._parse_document_response(response)
1507
1503
  doc._client = self
@@ -1548,7 +1544,6 @@ class Morphik:
1548
1544
  "ingest/files",
1549
1545
  data=data,
1550
1546
  files=file_objects,
1551
- params={"use_colpali": str(use_colpali).lower()},
1552
1547
  )
1553
1548
 
1554
1549
  if response.get("errors"):
@@ -1701,6 +1696,7 @@ class Morphik:
1701
1696
  include_paths: bool = False,
1702
1697
  prompt_overrides: Optional[Union[QueryPromptOverrides, Dict[str, Any]]] = None,
1703
1698
  folder_name: Optional[Union[str, List[str]]] = None,
1699
+ chat_id: Optional[str] = None,
1704
1700
  schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
1705
1701
  ) -> CompletionResponse:
1706
1702
  """
@@ -1807,6 +1803,7 @@ class Morphik:
1807
1803
  prompt_overrides,
1808
1804
  folder_name,
1809
1805
  None, # end_user_id not supported at this level
1806
+ chat_id,
1810
1807
  schema,
1811
1808
  )
1812
1809
 
@@ -1824,6 +1821,45 @@ class Morphik:
1824
1821
  response = self._request("POST", "query", data=payload)
1825
1822
  return self._logic._parse_completion_response(response)
1826
1823
 
1824
+ def agent_query(self, query: str) -> Dict[str, Any]:
1825
+ """
1826
+ Execute an agentic query with tool access and conversation handling.
1827
+
1828
+ The agent can autonomously use various tools to answer complex queries including:
1829
+ - Searching and retrieving relevant documents
1830
+ - Analyzing document content
1831
+ - Performing calculations and data processing
1832
+ - Creating summaries and reports
1833
+ - Managing knowledge graphs
1834
+
1835
+ Args:
1836
+ query: Natural language query for the Morphik agent
1837
+
1838
+ Returns:
1839
+ Dict[str, Any]: Agent response with potential tool execution results and sources
1840
+
1841
+ Example:
1842
+ ```python
1843
+ # Simple query
1844
+ result = db.agent_query("What are the main trends in our Q3 sales data?")
1845
+ print(result["response"])
1846
+
1847
+ # Complex analysis request
1848
+ result = db.agent_query(
1849
+ "Analyze all documents from the marketing department, "
1850
+ "identify key performance metrics, and create a summary "
1851
+ "with actionable insights"
1852
+ )
1853
+ print(result["response"])
1854
+
1855
+ # Tool usage is automatic - the agent will decide which tools to use
1856
+ # based on the query requirements
1857
+ ```
1858
+ """
1859
+ request = {"query": query}
1860
+ response = self._request("POST", "agent", data=request)
1861
+ return response
1862
+
1827
1863
  def list_documents(
1828
1864
  self,
1829
1865
  skip: int = 0,
@@ -2368,9 +2404,7 @@ class Morphik:
2368
2404
  print(f"Chunk from {chunk.document_id}, number {chunk.chunk_number}: {chunk.content[:50]}...")
2369
2405
  ```
2370
2406
  """
2371
- request = self._logic._prepare_batch_get_chunks_request(
2372
- sources, folder_name, None, use_colpali
2373
- )
2407
+ request = self._logic._prepare_batch_get_chunks_request(sources, folder_name, None, use_colpali)
2374
2408
  response = self._request("POST", "batch/chunks", data=request)
2375
2409
  return self._logic._parse_chunk_result_list_response(response)
2376
2410
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "morphik"
7
- version = "0.1.9"
7
+ version = "0.2.0"
8
8
  authors = [
9
9
  { name = "Morphik", email = "founders@morphik.ai" },
10
10
  ]
@@ -16,7 +16,9 @@ dependencies = [
16
16
  "pyjwt>=2.0.0",
17
17
  "pydantic>=2.10.3",
18
18
  "requests>=2.32.3",
19
- "pillow>=10.4.0"
19
+ "pillow>=10.4.0",
20
+ "build>=1.2.2.post1",
21
+ "twine>=6.1.0",
20
22
  ]
21
23
 
22
24
  [tool.hatch.build.targets.wheel]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes