datalab-python-sdk 0.4.0__py3-none-any.whl → 0.5.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datalab-python-sdk
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: SDK for the Datalab document intelligence API
5
5
  Author-email: Datalab Team <hi@datalab.to>
6
6
  License-Expression: MIT
@@ -0,0 +1,12 @@
1
+ datalab_sdk/__init__.py,sha256=3knlmAjdZBwfVqppayHc9YxBmG47KCtRMxK1K3-OyWI,1746
2
+ datalab_sdk/cli.py,sha256=NELgNZstpBrhPoQHqsI67kNRYew_nJpq23_ENsWvmGY,38859
3
+ datalab_sdk/client.py,sha256=BQoWuHDoEUJYHFSojzdsNEB5sc90Cj53YYavHZUTLg4,105118
4
+ datalab_sdk/exceptions.py,sha256=MDfNd-l152OUVhZSfEsE95McJq-IuOsrEsEQ4CRMbEI,785
5
+ datalab_sdk/mimetypes.py,sha256=a4ULJmHxoR_RMsD0j51hx0t2Q7In_PzNm7UlWX07eEE,932
6
+ datalab_sdk/models.py,sha256=vuC94U1c76ooLShrnO_7lzzfBjiJkHcsSZceYJJ8ggQ,22427
7
+ datalab_sdk/settings.py,sha256=BoojrRcKE7T04s-vqCPFzKqopV_qqTrAtCCsjHKk3FU,365
8
+ datalab_python_sdk-0.5.0.dist-info/METADATA,sha256=PNRCAWHCTcyU4wCisIwljO6h6qj9EYAB0okbQcc6r00,2238
9
+ datalab_python_sdk-0.5.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
10
+ datalab_python_sdk-0.5.0.dist-info/entry_points.txt,sha256=rqWxO21UV57JxxyeF20iBTxHpzBYOA69hCHIqwdn-Ok,48
11
+ datalab_python_sdk-0.5.0.dist-info/licenses/LICENSE,sha256=XGjT-Uz6t7UZzr3JCZfilsGcZEgMm_1aONcRunohxWg,1064
12
+ datalab_python_sdk-0.5.0.dist-info/RECORD,,
datalab_sdk/__init__.py CHANGED
@@ -15,6 +15,7 @@ from .models import (
15
15
  ConvertOptions,
16
16
  ExtractOptions,
17
17
  SegmentOptions,
18
+ CustomProcessorOptions,
18
19
  CustomPipelineOptions,
19
20
  TrackChangesOptions,
20
21
  OCROptions,
@@ -25,6 +26,14 @@ from .models import (
25
26
  WorkflowExecution,
26
27
  InputConfig,
27
28
  UploadedFileMetadata,
29
+ ExtractionSchema,
30
+ PipelineProcessor,
31
+ PipelineConfig,
32
+ PipelineVersion,
33
+ PipelineExecution,
34
+ PipelineExecutionStepResult,
35
+ CustomProcessor,
36
+ CustomProcessorVersion,
28
37
  )
29
38
  from .settings import settings
30
39
 
@@ -42,6 +51,7 @@ __all__ = [
42
51
  "ConvertOptions",
43
52
  "ExtractOptions",
44
53
  "SegmentOptions",
54
+ "CustomProcessorOptions",
45
55
  "CustomPipelineOptions",
46
56
  "TrackChangesOptions",
47
57
  "OCROptions",
@@ -52,4 +62,12 @@ __all__ = [
52
62
  "WorkflowExecution",
53
63
  "InputConfig",
54
64
  "UploadedFileMetadata",
65
+ "ExtractionSchema",
66
+ "PipelineProcessor",
67
+ "PipelineConfig",
68
+ "PipelineVersion",
69
+ "PipelineExecution",
70
+ "PipelineExecutionStepResult",
71
+ "CustomProcessor",
72
+ "CustomProcessorVersion",
55
73
  ]
datalab_sdk/client.py CHANGED
@@ -36,6 +36,7 @@ from datalab_sdk.models import (
36
36
  ConvertOptions,
37
37
  ExtractOptions,
38
38
  SegmentOptions,
39
+ CustomProcessorOptions,
39
40
  CustomPipelineOptions,
40
41
  TrackChangesOptions,
41
42
  OCROptions,
@@ -46,6 +47,14 @@ from datalab_sdk.models import (
46
47
  WorkflowExecution,
47
48
  InputConfig,
48
49
  UploadedFileMetadata,
50
+ ExtractionSchema,
51
+ PipelineProcessor,
52
+ PipelineConfig,
53
+ PipelineVersion,
54
+ PipelineExecution,
55
+ PipelineExecutionStepResult,
56
+ CustomProcessor,
57
+ CustomProcessorVersion,
49
58
  )
50
59
  from datalab_sdk.settings import settings
51
60
 
@@ -512,15 +521,15 @@ class AsyncDatalabClient:
512
521
  poll_interval: int = 1,
513
522
  ) -> Union[ConversionResult, FileResult]:
514
523
  """
515
- Extract structured data from a document using a JSON schema
524
+ Extract structured data from a document using a JSON schema or saved extraction schema
516
525
 
517
- Provide a file for end-to-end processing, or set checkpoint_id in options
518
- (from a previous convert() call with save_checkpoint=True) to skip re-parsing.
526
+ Provide a page_schema for inline extraction, or a schema_id to use a saved
527
+ extraction schema. These are mutually exclusive.
519
528
 
520
529
  Args:
521
530
  file_path: Path to the file to extract from
522
531
  file_url: URL of the file to extract from
523
- options: Extraction options (must include page_schema)
532
+ options: Extraction options (must include page_schema or schema_id)
524
533
  save_output: Optional path to save output files
525
534
  stream_response_to: Optional path to stream raw JSON response to disk
526
535
  max_polls: Maximum number of polling attempts
@@ -536,7 +545,17 @@ class AsyncDatalabClient:
536
545
  raise ValueError(f"Directory does not exist: {resolved_stream_response_to.parent}")
537
546
 
538
547
  if options is None:
539
- raise ValueError("options must be provided with page_schema")
548
+ raise ValueError("options must be provided with page_schema or schema_id")
549
+
550
+ has_page_schema = bool(options.page_schema)
551
+ has_schema_id = bool(options.schema_id)
552
+
553
+ if has_page_schema and has_schema_id:
554
+ raise ValueError("page_schema and schema_id are mutually exclusive. Provide one or the other.")
555
+ if not has_page_schema and not has_schema_id:
556
+ raise ValueError("Either page_schema or schema_id must be provided in options.")
557
+ if options.schema_version is not None and not has_schema_id:
558
+ raise ValueError("schema_version can only be used with schema_id.")
540
559
 
541
560
  has_file = file_path is not None or file_url is not None
542
561
  has_checkpoint = options.checkpoint_id is not None
@@ -635,23 +654,23 @@ class AsyncDatalabClient:
635
654
 
636
655
  return result
637
656
 
638
- async def run_custom_pipeline(
657
+ async def run_custom_processor(
639
658
  self,
640
659
  file_path: Optional[Union[str, Path]] = None,
641
660
  file_url: Optional[str] = None,
642
- options: Optional[CustomPipelineOptions] = None,
661
+ options: Optional[CustomProcessorOptions] = None,
643
662
  save_output: Optional[Union[str, Path]] = None,
644
663
  stream_response_to: Optional[Union[str, Path]] = None,
645
664
  max_polls: int = 300,
646
665
  poll_interval: int = 1,
647
666
  ) -> Union[ConversionResult, FileResult]:
648
667
  """
649
- Execute a custom pipeline configuration
668
+ Execute a custom processor on a document
650
669
 
651
670
  Args:
652
671
  file_path: Path to the file to process
653
672
  file_url: URL of the file to process
654
- options: Custom pipeline options (must include pipeline_id)
673
+ options: Custom processor options (must include pipeline_id)
655
674
  save_output: Optional path to save output files
656
675
  stream_response_to: Optional path to stream raw JSON response to disk
657
676
  max_polls: Maximum number of polling attempts
@@ -670,7 +689,7 @@ class AsyncDatalabClient:
670
689
  raise ValueError("options must be provided with pipeline_id")
671
690
 
672
691
  result_data = await self._submit_and_poll(
673
- "/api/v1/custom-pipeline",
692
+ "/api/v1/custom-processor",
674
693
  data=self.get_form_params(
675
694
  file_path=file_path, file_url=file_url, options=options
676
695
  ),
@@ -691,6 +710,36 @@ class AsyncDatalabClient:
691
710
 
692
711
  return result
693
712
 
713
+ async def run_custom_pipeline(
714
+ self,
715
+ file_path: Optional[Union[str, Path]] = None,
716
+ file_url: Optional[str] = None,
717
+ options: Optional[CustomProcessorOptions] = None,
718
+ save_output: Optional[Union[str, Path]] = None,
719
+ stream_response_to: Optional[Union[str, Path]] = None,
720
+ max_polls: int = 300,
721
+ poll_interval: int = 1,
722
+ ) -> Union[ConversionResult, FileResult]:
723
+ """Execute a custom processor on a document
724
+
725
+ .. deprecated::
726
+ Use run_custom_processor() instead.
727
+ """
728
+ warnings.warn(
729
+ "run_custom_pipeline() is deprecated. Use run_custom_processor() instead.",
730
+ DeprecationWarning,
731
+ stacklevel=2,
732
+ )
733
+ return await self.run_custom_processor(
734
+ file_path=file_path,
735
+ file_url=file_url,
736
+ options=options,
737
+ save_output=save_output,
738
+ stream_response_to=stream_response_to,
739
+ max_polls=max_polls,
740
+ poll_interval=poll_interval,
741
+ )
742
+
694
743
  async def track_changes(
695
744
  self,
696
745
  file_path: Optional[Union[str, Path]] = None,
@@ -1567,6 +1616,546 @@ class AsyncDatalabClient:
1567
1616
  "message": response.get("message", f"File {file_id} deleted successfully"),
1568
1617
  }
1569
1618
 
1619
+ # --- Extraction Schema methods ---
1620
+
1621
+ async def create_extraction_schema(
1622
+ self,
1623
+ name: str,
1624
+ schema_json: Dict[str, Any],
1625
+ description: Optional[str] = None,
1626
+ ) -> ExtractionSchema:
1627
+ """
1628
+ Create a new extraction schema
1629
+
1630
+ Args:
1631
+ name: Name for the schema (max 200 characters)
1632
+ schema_json: JSON schema for extraction (must contain 'properties' key)
1633
+ description: Optional description
1634
+ """
1635
+ payload: Dict[str, Any] = {"name": name, "schema_json": schema_json}
1636
+ if description is not None:
1637
+ payload["description"] = description
1638
+
1639
+ response = await self._make_request("POST", "/api/v1/extraction_schemas", json=payload)
1640
+ return self._build_extraction_schema(response)
1641
+
1642
+ async def list_extraction_schemas(
1643
+ self,
1644
+ limit: int = 50,
1645
+ offset: int = 0,
1646
+ include_archived: bool = False,
1647
+ ) -> Dict[str, Any]:
1648
+ """
1649
+ List extraction schemas for the authenticated user's team
1650
+
1651
+ Args:
1652
+ limit: Maximum number of schemas to return (default: 50, max: 200)
1653
+ offset: Offset for pagination (default: 0)
1654
+ include_archived: Include archived schemas (default: False)
1655
+ """
1656
+ params = f"limit={limit}&offset={offset}&include_archived={str(include_archived).lower()}"
1657
+ response = await self._make_request("GET", f"/api/v1/extraction_schemas?{params}")
1658
+ return {
1659
+ "schemas": [self._build_extraction_schema(s) for s in response.get("schemas", [])],
1660
+ "total": response.get("total", 0),
1661
+ }
1662
+
1663
+ async def get_extraction_schema(self, schema_id: str) -> ExtractionSchema:
1664
+ """
1665
+ Get an extraction schema by its schema_id
1666
+
1667
+ Args:
1668
+ schema_id: Schema ID string (e.g. sch_k8Hx9mP2nQ4v)
1669
+ """
1670
+ response = await self._make_request("GET", f"/api/v1/extraction_schemas/{schema_id}")
1671
+ return self._build_extraction_schema(response)
1672
+
1673
+ async def update_extraction_schema(
1674
+ self,
1675
+ schema_id: str,
1676
+ name: Optional[str] = None,
1677
+ description: Optional[str] = None,
1678
+ schema_json: Optional[Dict[str, Any]] = None,
1679
+ archived: Optional[bool] = None,
1680
+ create_new_version: bool = False,
1681
+ ) -> ExtractionSchema:
1682
+ """
1683
+ Update an extraction schema
1684
+
1685
+ Args:
1686
+ schema_id: Schema ID string (e.g. sch_k8Hx9mP2nQ4v)
1687
+ name: New name (max 200 characters)
1688
+ description: New description
1689
+ schema_json: New JSON schema (must contain 'properties' key)
1690
+ archived: Set archived status
1691
+ create_new_version: If True, bump version and save current state to history
1692
+ """
1693
+ payload: Dict[str, Any] = {}
1694
+ if name is not None:
1695
+ payload["name"] = name
1696
+ if description is not None:
1697
+ payload["description"] = description
1698
+ if schema_json is not None:
1699
+ payload["schema_json"] = schema_json
1700
+ if archived is not None:
1701
+ payload["archived"] = archived
1702
+ if create_new_version:
1703
+ payload["create_new_version"] = True
1704
+
1705
+ response = await self._make_request("PUT", f"/api/v1/extraction_schemas/{schema_id}", json=payload)
1706
+ return self._build_extraction_schema(response)
1707
+
1708
+ async def delete_extraction_schema(self, schema_id: str) -> ExtractionSchema:
1709
+ """
1710
+ Delete (archive) an extraction schema
1711
+
1712
+ Args:
1713
+ schema_id: Schema ID string (e.g. sch_k8Hx9mP2nQ4v)
1714
+ """
1715
+ response = await self._make_request("DELETE", f"/api/v1/extraction_schemas/{schema_id}")
1716
+ return self._build_extraction_schema(response)
1717
+
1718
+ @staticmethod
1719
+ def _build_extraction_schema(data: Dict[str, Any]) -> ExtractionSchema:
1720
+ return ExtractionSchema(
1721
+ id=data.get("id"),
1722
+ schema_id=data["schema_id"],
1723
+ name=data["name"],
1724
+ description=data.get("description"),
1725
+ schema_json=data["schema_json"],
1726
+ version=data.get("version", 1),
1727
+ version_history=data.get("version_history"),
1728
+ archived=data.get("archived", False),
1729
+ created=data.get("created"),
1730
+ updated=data.get("updated"),
1731
+ )
1732
+
1733
+ # --- Pipeline CRUD methods ---
1734
+
1735
+ async def create_pipeline(
1736
+ self,
1737
+ steps: list[PipelineProcessor],
1738
+ ) -> PipelineConfig:
1739
+ """
1740
+ Create a new pipeline
1741
+
1742
+ Args:
1743
+ steps: Ordered list of PipelineProcessor objects
1744
+ """
1745
+ payload = {"steps": [s.to_dict() for s in steps]}
1746
+ response = await self._make_request("POST", "/api/v1/pipelines", json=payload)
1747
+ return self._build_pipeline_config(response)
1748
+
1749
+ async def list_pipelines(
1750
+ self,
1751
+ saved_only: bool = True,
1752
+ include_archived: bool = False,
1753
+ limit: int = 50,
1754
+ offset: int = 0,
1755
+ ) -> Dict[str, Any]:
1756
+ """
1757
+ List pipelines for the authenticated user's team
1758
+
1759
+ Args:
1760
+ saved_only: Only return saved pipelines (default: True)
1761
+ include_archived: Include archived pipelines (default: False)
1762
+ limit: Maximum number to return (default: 50, max: 200)
1763
+ offset: Offset for pagination (default: 0)
1764
+ """
1765
+ params = (
1766
+ f"saved_only={str(saved_only).lower()}"
1767
+ f"&include_archived={str(include_archived).lower()}"
1768
+ f"&limit={limit}&offset={offset}"
1769
+ )
1770
+ response = await self._make_request("GET", f"/api/v1/pipelines?{params}")
1771
+ return {
1772
+ "pipelines": [self._build_pipeline_config(p) for p in response.get("pipelines", [])],
1773
+ "total": response.get("total", 0),
1774
+ }
1775
+
1776
+ async def get_pipeline(self, pipeline_id: str) -> PipelineConfig:
1777
+ """
1778
+ Get a pipeline by its pipeline_id
1779
+
1780
+ Args:
1781
+ pipeline_id: Pipeline ID string (e.g. pl_k8Hx9mP2nQ4v)
1782
+ """
1783
+ response = await self._make_request("GET", f"/api/v1/pipelines/{pipeline_id}")
1784
+ return self._build_pipeline_config(response)
1785
+
1786
+ async def update_pipeline(
1787
+ self,
1788
+ pipeline_id: str,
1789
+ steps: list[PipelineProcessor],
1790
+ ) -> PipelineConfig:
1791
+ """
1792
+ Update pipeline steps (auto-save path for draft edits)
1793
+
1794
+ Args:
1795
+ pipeline_id: Pipeline ID string
1796
+ steps: New ordered list of PipelineProcessor objects
1797
+ """
1798
+ payload = {"steps": [s.to_dict() for s in steps]}
1799
+ response = await self._make_request("PUT", f"/api/v1/pipelines/{pipeline_id}", json=payload)
1800
+ return self._build_pipeline_config(response)
1801
+
1802
+ async def save_pipeline(
1803
+ self,
1804
+ pipeline_id: str,
1805
+ name: str = "",
1806
+ ) -> PipelineConfig:
1807
+ """
1808
+ Name and promote a pipeline to saved status
1809
+
1810
+ Args:
1811
+ pipeline_id: Pipeline ID string
1812
+ name: Display name (auto-generated if empty)
1813
+ """
1814
+ response = await self._make_request(
1815
+ "PUT", f"/api/v1/pipelines/{pipeline_id}/save", json={"name": name}
1816
+ )
1817
+ return self._build_pipeline_config(response)
1818
+
1819
+ async def archive_pipeline(self, pipeline_id: str) -> Dict[str, Any]:
1820
+ """Archive a pipeline, hiding it from the default list"""
1821
+ return await self._make_request("POST", f"/api/v1/pipelines/{pipeline_id}/archive")
1822
+
1823
+ async def unarchive_pipeline(self, pipeline_id: str) -> Dict[str, Any]:
1824
+ """Unarchive a pipeline, restoring it to the default list"""
1825
+ return await self._make_request("POST", f"/api/v1/pipelines/{pipeline_id}/unarchive")
1826
+
1827
+ # --- Pipeline Versioning methods ---
1828
+
1829
+ async def create_pipeline_version(
1830
+ self,
1831
+ pipeline_id: str,
1832
+ description: Optional[str] = None,
1833
+ ) -> PipelineVersion:
1834
+ """
1835
+ Create a new version snapshot of the pipeline's current steps
1836
+
1837
+ Args:
1838
+ pipeline_id: Pipeline ID string
1839
+ description: Optional description for this version
1840
+ """
1841
+ payload: Dict[str, Any] = {}
1842
+ if description is not None:
1843
+ payload["description"] = description
1844
+ response = await self._make_request(
1845
+ "POST", f"/api/v1/pipelines/{pipeline_id}/versions", json=payload
1846
+ )
1847
+ return PipelineVersion(
1848
+ id=response.get("id"),
1849
+ version=response["version"],
1850
+ steps=response.get("steps", []),
1851
+ description=response.get("description"),
1852
+ created=response.get("created"),
1853
+ )
1854
+
1855
+ async def list_pipeline_versions(self, pipeline_id: str) -> Dict[str, Any]:
1856
+ """List all versions of a pipeline, newest first"""
1857
+ response = await self._make_request("GET", f"/api/v1/pipelines/{pipeline_id}/versions")
1858
+ return {
1859
+ "versions": [
1860
+ PipelineVersion(
1861
+ id=v.get("id"),
1862
+ version=v["version"],
1863
+ steps=v.get("steps", []),
1864
+ description=v.get("description"),
1865
+ created=v.get("created"),
1866
+ )
1867
+ for v in response.get("versions", [])
1868
+ ],
1869
+ "total": response.get("total", 0),
1870
+ }
1871
+
1872
+ async def discard_pipeline_draft(
1873
+ self,
1874
+ pipeline_id: str,
1875
+ version: Optional[int] = None,
1876
+ ) -> PipelineConfig:
1877
+ """
1878
+ Discard draft changes and revert to a published version
1879
+
1880
+ Args:
1881
+ pipeline_id: Pipeline ID string
1882
+ version: Version to revert to (default: active published version)
1883
+ """
1884
+ payload: Dict[str, Any] = {}
1885
+ if version is not None:
1886
+ payload["version"] = version
1887
+ response = await self._make_request(
1888
+ "POST", f"/api/v1/pipelines/{pipeline_id}/discard", json=payload
1889
+ )
1890
+ return self._build_pipeline_config(response)
1891
+
1892
+ async def get_pipeline_rate(self, pipeline_id: str) -> Dict[str, Any]:
1893
+ """
1894
+ Get the per-page rate for a pipeline
1895
+
1896
+ Returns dict with rate_per_1000_pages_cents and rate_breakdown.
1897
+ """
1898
+ return await self._make_request("GET", f"/api/v1/pipelines/{pipeline_id}/rate")
1899
+
1900
+ # --- Pipeline Execution methods ---
1901
+
1902
+ async def run_pipeline(
1903
+ self,
1904
+ pipeline_id: str,
1905
+ file_path: Optional[Union[str, Path]] = None,
1906
+ file_url: Optional[str] = None,
1907
+ page_range: Optional[str] = None,
1908
+ output_format: Optional[str] = None,
1909
+ run_evals: bool = False,
1910
+ skip_cache: bool = False,
1911
+ webhook_url: Optional[str] = None,
1912
+ version: Optional[int] = None,
1913
+ max_polls: int = 1,
1914
+ poll_interval: int = 1,
1915
+ ) -> PipelineExecution:
1916
+ """
1917
+ Execute a pipeline on a file
1918
+
1919
+ Args:
1920
+ pipeline_id: Pipeline ID (pl_XXXXX)
1921
+ file_path: Path to the file to process
1922
+ file_url: URL of the file to process
1923
+ page_range: Page range to process (e.g. '0,2-4')
1924
+ output_format: Output format (json, html, markdown, chunks)
1925
+ run_evals: Whether to run evaluation steps
1926
+ skip_cache: Skip executor cache
1927
+ webhook_url: URL to POST when complete
1928
+ version: Pipeline version to execute (0=draft, omit=active)
1929
+ max_polls: Maximum polling attempts after submission (default: 1)
1930
+ poll_interval: Seconds between polls
1931
+ """
1932
+ form_data = self.get_form_params(file_path=file_path, file_url=file_url)
1933
+ if page_range is not None:
1934
+ form_data.add_field("page_range", page_range)
1935
+ if output_format is not None:
1936
+ form_data.add_field("output_format", output_format)
1937
+ if run_evals:
1938
+ form_data.add_field("run_evals", str(run_evals))
1939
+ if skip_cache:
1940
+ form_data.add_field("skip_cache", str(skip_cache))
1941
+ if webhook_url is not None:
1942
+ form_data.add_field("webhook_url", webhook_url)
1943
+ if version is not None:
1944
+ form_data.add_field("version", str(version))
1945
+
1946
+ response = await self._submit_with_retry(
1947
+ f"/api/v1/pipelines/{pipeline_id}/run", data=form_data
1948
+ )
1949
+ execution = self._build_pipeline_execution(response)
1950
+
1951
+ # Poll if requested
1952
+ if max_polls > 1 and execution.status not in ("completed", "completed_with_errors", "failed"):
1953
+ return await self.get_pipeline_execution(
1954
+ execution.execution_id, max_polls=max_polls - 1, poll_interval=poll_interval
1955
+ )
1956
+ return execution
1957
+
1958
+ async def get_pipeline_execution(
1959
+ self,
1960
+ execution_id: str,
1961
+ max_polls: int = 1,
1962
+ poll_interval: int = 1,
1963
+ ) -> PipelineExecution:
1964
+ """
1965
+ Get the status of a pipeline execution, optionally polling until completion
1966
+
1967
+ Args:
1968
+ execution_id: Execution ID (pex_XXXXX)
1969
+ max_polls: Maximum polling attempts (default: 1 for single check)
1970
+ poll_interval: Seconds between polls
1971
+ """
1972
+ for i in range(max_polls):
1973
+ response = await self._make_request(
1974
+ "GET", f"/api/v1/pipelines/executions/{execution_id}"
1975
+ )
1976
+ execution = self._build_pipeline_execution(response)
1977
+
1978
+ if execution.status in ("completed", "completed_with_errors", "failed"):
1979
+ return execution
1980
+
1981
+ if i < max_polls - 1:
1982
+ await asyncio.sleep(poll_interval)
1983
+
1984
+ return execution
1985
+
1986
+ async def list_pipeline_executions(
1987
+ self,
1988
+ pipeline_id: str,
1989
+ limit: int = 20,
1990
+ offset: int = 0,
1991
+ ) -> Dict[str, Any]:
1992
+ """List recent executions for a pipeline"""
1993
+ response = await self._make_request(
1994
+ "GET", f"/api/v1/pipelines/{pipeline_id}/executions?limit={limit}&offset={offset}"
1995
+ )
1996
+ return {
1997
+ "executions": [
1998
+ self._build_pipeline_execution(e) for e in response.get("executions", [])
1999
+ ],
2000
+ "total": response.get("total", 0),
2001
+ }
2002
+
2003
+ async def get_step_result(
2004
+ self,
2005
+ execution_id: str,
2006
+ step_index: int,
2007
+ ) -> Dict[str, Any]:
2008
+ """
2009
+ Fetch the result of a specific pipeline execution step
2010
+
2011
+ Args:
2012
+ execution_id: Execution ID (pex_XXXXX)
2013
+ step_index: Zero-based step index
2014
+ """
2015
+ return await self._make_request(
2016
+ "GET", f"/api/v1/pipelines/executions/{execution_id}/steps/{step_index}/result"
2017
+ )
2018
+
2019
+ @staticmethod
2020
+ def _build_pipeline_config(data: Dict[str, Any]) -> PipelineConfig:
2021
+ return PipelineConfig(
2022
+ id=data.get("id"),
2023
+ pipeline_id=data["pipeline_id"],
2024
+ name=data.get("name"),
2025
+ steps=data.get("steps", []),
2026
+ is_saved=data.get("is_saved", False),
2027
+ archived=data.get("archived", False),
2028
+ active_version=data.get("active_version", 0),
2029
+ created=data.get("created"),
2030
+ updated=data.get("updated"),
2031
+ )
2032
+
2033
+ @staticmethod
2034
+ def _build_pipeline_execution(data: Dict[str, Any]) -> PipelineExecution:
2035
+ steps = [
2036
+ PipelineExecutionStepResult(
2037
+ step_index=s["step_index"],
2038
+ step_type=s["step_type"],
2039
+ status=s["status"],
2040
+ lookup_key=s.get("lookup_key"),
2041
+ result_url=s.get("result_url"),
2042
+ started_at=s.get("started_at"),
2043
+ finished_at=s.get("finished_at"),
2044
+ error_message=s.get("error_message"),
2045
+ checkpoint_id=s.get("checkpoint_id"),
2046
+ )
2047
+ for s in data.get("steps", [])
2048
+ ]
2049
+ return PipelineExecution(
2050
+ execution_id=data["execution_id"],
2051
+ pipeline_id=data.get("pipeline_id", ""),
2052
+ pipeline_version=data.get("pipeline_version", 0),
2053
+ status=data.get("status", "pending"),
2054
+ steps=steps,
2055
+ started_at=data.get("started_at"),
2056
+ completed_at=data.get("completed_at"),
2057
+ created=data.get("created"),
2058
+ config_snapshot=data.get("config_snapshot"),
2059
+ input_config=data.get("input_config"),
2060
+ rate_breakdown=data.get("rate_breakdown"),
2061
+ )
2062
+
2063
+ # --- Custom Processor Management methods ---
2064
+
2065
+ async def list_custom_processors(
2066
+ self,
2067
+ limit: int = 50,
2068
+ offset: int = 0,
2069
+ ) -> Dict[str, Any]:
2070
+ """
2071
+ List custom processors for the authenticated user's team
2072
+
2073
+ Args:
2074
+ limit: Maximum number to return (default: 50)
2075
+ offset: Offset for pagination (default: 0)
2076
+ """
2077
+ response = await self._make_request(
2078
+ "GET", f"/api/v1/custom_processors?limit={limit}&offset={offset}"
2079
+ )
2080
+ processors = [
2081
+ CustomProcessor(
2082
+ processor_id=p["processor_id"],
2083
+ name=p.get("name"),
2084
+ status=p.get("status", ""),
2085
+ success=p.get("success"),
2086
+ active_version=p.get("active_version", 0),
2087
+ max_version=p.get("max_version", 0),
2088
+ iteration_in_progress=p.get("iteration_in_progress", False),
2089
+ pipeline_id=p.get("pipeline_id"),
2090
+ created_at=p.get("created_at"),
2091
+ completed_at=p.get("completed_at"),
2092
+ error_message=p.get("error_message"),
2093
+ eval_rubric_id=p.get("eval_rubric_id"),
2094
+ )
2095
+ for p in response.get("pipelines", [])
2096
+ ]
2097
+ return {"processors": processors}
2098
+
2099
+ async def get_custom_processor_status(self, lookup_key: str) -> Dict[str, Any]:
2100
+ """
2101
+ Check the status of a custom processor request
2102
+
2103
+ Args:
2104
+ lookup_key: The lookup key returned when the processor was submitted
2105
+ """
2106
+ return await self._make_request("GET", f"/api/v1/custom_processors/{lookup_key}")
2107
+
2108
+ async def list_custom_processor_versions(self, processor_id: str) -> Dict[str, Any]:
2109
+ """
2110
+ List versions of a custom processor
2111
+
2112
+ Args:
2113
+ processor_id: Processor ID (cp_XXXXX)
2114
+ """
2115
+ response = await self._make_request(
2116
+ "GET", f"/api/v1/custom_processors/{processor_id}/versions"
2117
+ )
2118
+ versions = [
2119
+ CustomProcessorVersion(
2120
+ version=v["version"],
2121
+ request_description=v.get("request_description", ""),
2122
+ created_at=v.get("created_at"),
2123
+ runtime=v.get("runtime"),
2124
+ is_active=v.get("is_active", False),
2125
+ )
2126
+ for v in response.get("versions", [])
2127
+ ]
2128
+ return {"versions": versions}
2129
+
2130
+ async def set_active_processor_version(
2131
+ self,
2132
+ processor_id: str,
2133
+ version: int,
2134
+ ) -> Dict[str, Any]:
2135
+ """
2136
+ Set the active version of a custom processor
2137
+
2138
+ Args:
2139
+ processor_id: Processor ID (cp_XXXXX)
2140
+ version: Version number to activate
2141
+ """
2142
+ form_data = aiohttp.FormData()
2143
+ form_data.add_field("version", str(version))
2144
+ return await self._submit_with_retry(
2145
+ f"/api/v1/custom_processors/{processor_id}/set_active", data=form_data
2146
+ )
2147
+
2148
+ async def archive_custom_processor(self, processor_id: str) -> Dict[str, Any]:
2149
+ """
2150
+ Archive a custom processor
2151
+
2152
+ Args:
2153
+ processor_id: Processor ID (cp_XXXXX)
2154
+ """
2155
+ return await self._make_request(
2156
+ "POST", f"/api/v1/custom_processors/{processor_id}/archive"
2157
+ )
2158
+
1570
2159
 
1571
2160
  class DatalabClient:
1572
2161
  """Synchronous wrapper around AsyncDatalabClient"""
@@ -1646,12 +2235,12 @@ class DatalabClient:
1646
2235
  poll_interval: int = 1,
1647
2236
  ) -> Union[ConversionResult, FileResult]:
1648
2237
  """
1649
- Extract structured data from a document using a JSON schema (sync version)
2238
+ Extract structured data using a JSON schema or saved extraction schema (sync version)
1650
2239
 
1651
2240
  Args:
1652
2241
  file_path: Path to the file to extract from
1653
2242
  file_url: URL of the file to extract from
1654
- options: Extraction options (must include page_schema)
2243
+ options: Extraction options (must include page_schema or schema_id)
1655
2244
  save_output: Optional path to save output files
1656
2245
  stream_response_to: Optional path to stream raw JSON response to disk
1657
2246
  max_polls: Maximum number of polling attempts
@@ -1703,30 +2292,30 @@ class DatalabClient:
1703
2292
  )
1704
2293
  )
1705
2294
 
1706
- def run_custom_pipeline(
2295
+ def run_custom_processor(
1707
2296
  self,
1708
2297
  file_path: Optional[Union[str, Path]] = None,
1709
2298
  file_url: Optional[str] = None,
1710
- options: Optional[CustomPipelineOptions] = None,
2299
+ options: Optional[CustomProcessorOptions] = None,
1711
2300
  save_output: Optional[Union[str, Path]] = None,
1712
2301
  stream_response_to: Optional[Union[str, Path]] = None,
1713
2302
  max_polls: int = 300,
1714
2303
  poll_interval: int = 1,
1715
2304
  ) -> Union[ConversionResult, FileResult]:
1716
2305
  """
1717
- Execute a custom pipeline configuration (sync version)
2306
+ Execute a custom processor on a document (sync version)
1718
2307
 
1719
2308
  Args:
1720
2309
  file_path: Path to the file to process
1721
2310
  file_url: URL of the file to process
1722
- options: Custom pipeline options (must include pipeline_id)
2311
+ options: Custom processor options (must include pipeline_id)
1723
2312
  save_output: Optional path to save output files
1724
2313
  stream_response_to: Optional path to stream raw JSON response to disk
1725
2314
  max_polls: Maximum number of polling attempts
1726
2315
  poll_interval: Seconds between polling attempts
1727
2316
  """
1728
2317
  return self._run_async(
1729
- self._async_client.run_custom_pipeline(
2318
+ self._async_client.run_custom_processor(
1730
2319
  file_path=file_path,
1731
2320
  file_url=file_url,
1732
2321
  options=options,
@@ -1737,6 +2326,36 @@ class DatalabClient:
1737
2326
  )
1738
2327
  )
1739
2328
 
2329
+ def run_custom_pipeline(
2330
+ self,
2331
+ file_path: Optional[Union[str, Path]] = None,
2332
+ file_url: Optional[str] = None,
2333
+ options: Optional[CustomProcessorOptions] = None,
2334
+ save_output: Optional[Union[str, Path]] = None,
2335
+ stream_response_to: Optional[Union[str, Path]] = None,
2336
+ max_polls: int = 300,
2337
+ poll_interval: int = 1,
2338
+ ) -> Union[ConversionResult, FileResult]:
2339
+ """Execute a custom processor on a document (sync version)
2340
+
2341
+ .. deprecated::
2342
+ Use run_custom_processor() instead.
2343
+ """
2344
+ warnings.warn(
2345
+ "run_custom_pipeline() is deprecated. Use run_custom_processor() instead.",
2346
+ DeprecationWarning,
2347
+ stacklevel=2,
2348
+ )
2349
+ return self.run_custom_processor(
2350
+ file_path=file_path,
2351
+ file_url=file_url,
2352
+ options=options,
2353
+ save_output=save_output,
2354
+ stream_response_to=stream_response_to,
2355
+ max_polls=max_polls,
2356
+ poll_interval=poll_interval,
2357
+ )
2358
+
1740
2359
  def track_changes(
1741
2360
  self,
1742
2361
  file_path: Optional[Union[str, Path]] = None,
@@ -2051,3 +2670,208 @@ class DatalabClient:
2051
2670
  return self._run_async(
2052
2671
  self._async_client.delete_workflow(workflow_id=workflow_id)
2053
2672
  )
2673
+
2674
+ # --- Extraction Schema methods (sync) ---
2675
+
2676
+ def create_extraction_schema(
2677
+ self, name: str, schema_json: Dict[str, Any], description: Optional[str] = None,
2678
+ ) -> ExtractionSchema:
2679
+ """Create a new extraction schema (sync version)"""
2680
+ return self._run_async(
2681
+ self._async_client.create_extraction_schema(
2682
+ name=name, schema_json=schema_json, description=description,
2683
+ )
2684
+ )
2685
+
2686
+ def list_extraction_schemas(
2687
+ self, limit: int = 50, offset: int = 0, include_archived: bool = False,
2688
+ ) -> Dict[str, Any]:
2689
+ """List extraction schemas (sync version)"""
2690
+ return self._run_async(
2691
+ self._async_client.list_extraction_schemas(
2692
+ limit=limit, offset=offset, include_archived=include_archived,
2693
+ )
2694
+ )
2695
+
2696
+ def get_extraction_schema(self, schema_id: str) -> ExtractionSchema:
2697
+ """Get an extraction schema by ID (sync version)"""
2698
+ return self._run_async(self._async_client.get_extraction_schema(schema_id=schema_id))
2699
+
2700
+ def update_extraction_schema(
2701
+ self,
2702
+ schema_id: str,
2703
+ name: Optional[str] = None,
2704
+ description: Optional[str] = None,
2705
+ schema_json: Optional[Dict[str, Any]] = None,
2706
+ archived: Optional[bool] = None,
2707
+ create_new_version: bool = False,
2708
+ ) -> ExtractionSchema:
2709
+ """Update an extraction schema (sync version)"""
2710
+ return self._run_async(
2711
+ self._async_client.update_extraction_schema(
2712
+ schema_id=schema_id, name=name, description=description,
2713
+ schema_json=schema_json, archived=archived,
2714
+ create_new_version=create_new_version,
2715
+ )
2716
+ )
2717
+
2718
+ def delete_extraction_schema(self, schema_id: str) -> ExtractionSchema:
2719
+ """Delete (archive) an extraction schema (sync version)"""
2720
+ return self._run_async(self._async_client.delete_extraction_schema(schema_id=schema_id))
2721
+
2722
+ # --- Pipeline methods (sync) ---
2723
+
2724
+ def create_pipeline(self, steps: list[PipelineProcessor]) -> PipelineConfig:
2725
+ """Create a new pipeline (sync version)"""
2726
+ return self._run_async(self._async_client.create_pipeline(steps=steps))
2727
+
2728
+ def list_pipelines(
2729
+ self, saved_only: bool = True, include_archived: bool = False,
2730
+ limit: int = 50, offset: int = 0,
2731
+ ) -> Dict[str, Any]:
2732
+ """List pipelines (sync version)"""
2733
+ return self._run_async(
2734
+ self._async_client.list_pipelines(
2735
+ saved_only=saved_only, include_archived=include_archived,
2736
+ limit=limit, offset=offset,
2737
+ )
2738
+ )
2739
+
2740
+ def get_pipeline(self, pipeline_id: str) -> PipelineConfig:
2741
+ """Get a pipeline by ID (sync version)"""
2742
+ return self._run_async(self._async_client.get_pipeline(pipeline_id=pipeline_id))
2743
+
2744
+ def update_pipeline(self, pipeline_id: str, steps: list[PipelineProcessor]) -> PipelineConfig:
2745
+ """Update pipeline steps (sync version)"""
2746
+ return self._run_async(
2747
+ self._async_client.update_pipeline(pipeline_id=pipeline_id, steps=steps)
2748
+ )
2749
+
2750
+ def save_pipeline(self, pipeline_id: str, name: str = "") -> PipelineConfig:
2751
+ """Save and name a pipeline (sync version)"""
2752
+ return self._run_async(
2753
+ self._async_client.save_pipeline(pipeline_id=pipeline_id, name=name)
2754
+ )
2755
+
2756
+ def archive_pipeline(self, pipeline_id: str) -> Dict[str, Any]:
2757
+ """Archive a pipeline (sync version)"""
2758
+ return self._run_async(self._async_client.archive_pipeline(pipeline_id=pipeline_id))
2759
+
2760
+ def unarchive_pipeline(self, pipeline_id: str) -> Dict[str, Any]:
2761
+ """Unarchive a pipeline (sync version)"""
2762
+ return self._run_async(self._async_client.unarchive_pipeline(pipeline_id=pipeline_id))
2763
+
2764
+ def create_pipeline_version(
2765
+ self, pipeline_id: str, description: Optional[str] = None,
2766
+ ) -> PipelineVersion:
2767
+ """Create a pipeline version snapshot (sync version)"""
2768
+ return self._run_async(
2769
+ self._async_client.create_pipeline_version(
2770
+ pipeline_id=pipeline_id, description=description,
2771
+ )
2772
+ )
2773
+
2774
+ def list_pipeline_versions(self, pipeline_id: str) -> Dict[str, Any]:
2775
+ """List pipeline versions (sync version)"""
2776
+ return self._run_async(self._async_client.list_pipeline_versions(pipeline_id=pipeline_id))
2777
+
2778
+ def discard_pipeline_draft(
2779
+ self, pipeline_id: str, version: Optional[int] = None,
2780
+ ) -> PipelineConfig:
2781
+ """Discard draft and revert to a published version (sync version)"""
2782
+ return self._run_async(
2783
+ self._async_client.discard_pipeline_draft(
2784
+ pipeline_id=pipeline_id, version=version,
2785
+ )
2786
+ )
2787
+
2788
+ def get_pipeline_rate(self, pipeline_id: str) -> Dict[str, Any]:
2789
+ """Get pipeline per-page rate (sync version)"""
2790
+ return self._run_async(self._async_client.get_pipeline_rate(pipeline_id=pipeline_id))
2791
+
2792
+ def run_pipeline(
2793
+ self,
2794
+ pipeline_id: str,
2795
+ file_path: Optional[Union[str, Path]] = None,
2796
+ file_url: Optional[str] = None,
2797
+ page_range: Optional[str] = None,
2798
+ output_format: Optional[str] = None,
2799
+ run_evals: bool = False,
2800
+ skip_cache: bool = False,
2801
+ webhook_url: Optional[str] = None,
2802
+ version: Optional[int] = None,
2803
+ max_polls: int = 1,
2804
+ poll_interval: int = 1,
2805
+ ) -> PipelineExecution:
2806
+ """Execute a pipeline on a file (sync version)"""
2807
+ return self._run_async(
2808
+ self._async_client.run_pipeline(
2809
+ pipeline_id=pipeline_id, file_path=file_path, file_url=file_url,
2810
+ page_range=page_range, output_format=output_format,
2811
+ run_evals=run_evals, skip_cache=skip_cache,
2812
+ webhook_url=webhook_url, version=version,
2813
+ max_polls=max_polls, poll_interval=poll_interval,
2814
+ )
2815
+ )
2816
+
2817
+ def get_pipeline_execution(
2818
+ self, execution_id: str, max_polls: int = 1, poll_interval: int = 1,
2819
+ ) -> PipelineExecution:
2820
+ """Get pipeline execution status (sync version)"""
2821
+ return self._run_async(
2822
+ self._async_client.get_pipeline_execution(
2823
+ execution_id=execution_id, max_polls=max_polls, poll_interval=poll_interval,
2824
+ )
2825
+ )
2826
+
2827
+ def list_pipeline_executions(
2828
+ self, pipeline_id: str, limit: int = 20, offset: int = 0,
2829
+ ) -> Dict[str, Any]:
2830
+ """List pipeline executions (sync version)"""
2831
+ return self._run_async(
2832
+ self._async_client.list_pipeline_executions(
2833
+ pipeline_id=pipeline_id, limit=limit, offset=offset,
2834
+ )
2835
+ )
2836
+
2837
+ def get_step_result(self, execution_id: str, step_index: int) -> Dict[str, Any]:
2838
+ """Get a pipeline execution step result (sync version)"""
2839
+ return self._run_async(
2840
+ self._async_client.get_step_result(
2841
+ execution_id=execution_id, step_index=step_index,
2842
+ )
2843
+ )
2844
+
2845
+ # --- Custom Processor Management methods (sync) ---
2846
+
2847
+ def list_custom_processors(self, limit: int = 50, offset: int = 0) -> Dict[str, Any]:
2848
+ """List custom processors (sync version)"""
2849
+ return self._run_async(
2850
+ self._async_client.list_custom_processors(limit=limit, offset=offset)
2851
+ )
2852
+
2853
+ def get_custom_processor_status(self, lookup_key: str) -> Dict[str, Any]:
2854
+ """Check custom processor request status (sync version)"""
2855
+ return self._run_async(
2856
+ self._async_client.get_custom_processor_status(lookup_key=lookup_key)
2857
+ )
2858
+
2859
+ def list_custom_processor_versions(self, processor_id: str) -> Dict[str, Any]:
2860
+ """List custom processor versions (sync version)"""
2861
+ return self._run_async(
2862
+ self._async_client.list_custom_processor_versions(processor_id=processor_id)
2863
+ )
2864
+
2865
+ def set_active_processor_version(self, processor_id: str, version: int) -> Dict[str, Any]:
2866
+ """Set active processor version (sync version)"""
2867
+ return self._run_async(
2868
+ self._async_client.set_active_processor_version(
2869
+ processor_id=processor_id, version=version,
2870
+ )
2871
+ )
2872
+
2873
+ def archive_custom_processor(self, processor_id: str) -> Dict[str, Any]:
2874
+ """Archive a custom processor (sync version)"""
2875
+ return self._run_async(
2876
+ self._async_client.archive_custom_processor(processor_id=processor_id)
2877
+ )
datalab_sdk/models.py CHANGED
@@ -53,6 +53,7 @@ class ConvertOptions(ProcessingOptions):
53
53
  add_block_ids: bool = False # add block IDs to HTML output
54
54
  include_markdown_in_chunks: bool = False # include markdown field in chunks/JSON output
55
55
  token_efficient_markdown: bool = False # optimize markdown for LLM token usage
56
+ eval_rubric_id: Optional[int] = None # run evaluation rubric after conversion
56
57
 
57
58
  def to_form_data(self) -> Dict[str, Any]:
58
59
  """Convert to form data format for API requests"""
@@ -78,13 +79,23 @@ class ConvertOptions(ProcessingOptions):
78
79
  class ExtractOptions(ProcessingOptions):
79
80
  """Options for structured data extraction via /extract endpoint"""
80
81
 
81
- page_schema: str = "" # Required - JSON schema with 'properties' key
82
+ page_schema: str = "" # JSON schema with 'properties' key. Mutually exclusive with schema_id.
83
+ schema_id: Optional[str] = None # ID of a saved extraction schema (e.g. sch_k8Hx9mP2nQ4v). Mutually exclusive with page_schema.
84
+ schema_version: Optional[int] = None # Version of the schema. Only valid with schema_id.
82
85
  checkpoint_id: Optional[str] = None # From previous /convert with save_checkpoint=true
83
86
  mode: str = "fast" # fast, balanced, accurate
84
87
  output_format: str = "markdown" # markdown, json, html, chunks
85
88
  save_checkpoint: bool = False
86
89
  webhook_url: Optional[str] = None
87
90
 
91
+ def to_form_data(self) -> Dict[str, Any]:
92
+ """Convert to form data format for API requests"""
93
+ form_data = super().to_form_data()
94
+ # When using schema_id, suppress the empty default page_schema
95
+ if self.schema_id and not self.page_schema:
96
+ form_data.pop("page_schema", None)
97
+ return form_data
98
+
88
99
 
89
100
  @dataclass
90
101
  class SegmentOptions(ProcessingOptions):
@@ -98,15 +109,34 @@ class SegmentOptions(ProcessingOptions):
98
109
 
99
110
 
100
111
  @dataclass
101
- class CustomPipelineOptions(ProcessingOptions):
102
- """Options for running a custom pipeline via /custom-pipeline endpoint"""
112
+ class CustomProcessorOptions(ProcessingOptions):
113
+ """Options for running a custom processor via /custom-processor endpoint"""
103
114
 
104
- pipeline_id: str = "" # Required - custom pipeline ID (cp_XXXXX format)
105
- run_eval: bool = False # Run evaluation rules defined for the pipeline
115
+ pipeline_id: str = "" # Required - custom processor ID (cp_XXXXX format)
116
+ version: Optional[int] = None # Specify processor version to run (default: active version)
117
+ run_eval: bool = False # Run evaluation rules defined for the processor
106
118
  mode: str = "fast" # fast, balanced, accurate
107
119
  output_format: str = "markdown" # markdown, json, html, chunks
120
+ paginate: bool = False # Separate pages with horizontal rules containing page numbers
121
+ add_block_ids: bool = False # Add data-block-id attributes to HTML elements
122
+ include_markdown_in_chunks: bool = False # Include markdown field in chunks/JSON output
123
+ disable_image_extraction: bool = False # Disable image extraction from the document
124
+ disable_image_captions: bool = False # Disable synthetic image captions/descriptions
108
125
  webhook_url: Optional[str] = None
109
126
 
127
+ @property
128
+ def processor_id(self) -> str:
129
+ """Alias for pipeline_id (the API wire format uses pipeline_id)"""
130
+ return self.pipeline_id
131
+
132
+ @processor_id.setter
133
+ def processor_id(self, value: str):
134
+ self.pipeline_id = value
135
+
136
+
137
+ # Backward-compatible alias
138
+ CustomPipelineOptions = CustomProcessorOptions
139
+
110
140
 
111
141
  @dataclass
112
142
  class TrackChangesOptions(ProcessingOptions):
@@ -509,3 +539,137 @@ class FileResult:
509
539
  status: str
510
540
  output_path: Path
511
541
  error: Optional[str] = None
542
+
543
+
544
+ # --- Extraction Schema models ---
545
+
546
+
547
+ @dataclass
548
+ class ExtractionSchema:
549
+ """Represents a saved extraction schema for structured extraction"""
550
+
551
+ schema_id: str
552
+ name: str
553
+ schema_json: Dict[str, Any]
554
+ id: Optional[int] = None
555
+ description: Optional[str] = None
556
+ version: int = 1
557
+ version_history: Optional[List[Dict[str, Any]]] = None
558
+ archived: bool = False
559
+ created: Optional[str] = None
560
+ updated: Optional[str] = None
561
+
562
+
563
+ # --- Pipeline models ---
564
+
565
+
566
+ @dataclass
567
+ class PipelineProcessor:
568
+ """Configuration for a single processor within a pipeline"""
569
+
570
+ type: str # convert, extract, segment, custom
571
+ settings: Dict[str, Any] = field(default_factory=dict)
572
+ custom_processor_id: Optional[str] = None # For custom steps (cp_XXXXX)
573
+ eval_rubric_id: Optional[int] = None
574
+
575
+ def to_dict(self) -> Dict[str, Any]:
576
+ """Convert to dictionary for API requests"""
577
+ data: Dict[str, Any] = {
578
+ "type": self.type,
579
+ "settings": self.settings,
580
+ }
581
+ if self.custom_processor_id is not None:
582
+ data["custom_processor_id"] = self.custom_processor_id
583
+ if self.eval_rubric_id is not None:
584
+ data["eval_rubric_id"] = self.eval_rubric_id
585
+ return data
586
+
587
+
588
+ @dataclass
589
+ class PipelineConfig:
590
+ """Represents a pipeline definition"""
591
+
592
+ pipeline_id: str # pl_XXXXX
593
+ steps: List[Dict[str, Any]]
594
+ name: Optional[str] = None
595
+ is_saved: bool = False
596
+ archived: bool = False
597
+ active_version: int = 0
598
+ id: Optional[int] = None
599
+ created: Optional[str] = None
600
+ updated: Optional[str] = None
601
+
602
+
603
+ @dataclass
604
+ class PipelineVersion:
605
+ """Immutable version snapshot of a pipeline"""
606
+
607
+ version: int
608
+ steps: List[Dict[str, Any]]
609
+ description: Optional[str] = None
610
+ id: Optional[int] = None
611
+ created: Optional[str] = None
612
+
613
+
614
+ @dataclass
615
+ class PipelineExecutionStepResult:
616
+ """Status of a single step within a pipeline execution"""
617
+
618
+ step_index: int
619
+ step_type: str
620
+ status: str # pending, dispatched, running, completed, failed
621
+ lookup_key: Optional[str] = None
622
+ result_url: Optional[str] = None
623
+ started_at: Optional[str] = None
624
+ finished_at: Optional[str] = None
625
+ error_message: Optional[str] = None
626
+ checkpoint_id: Optional[str] = None
627
+
628
+
629
+ @dataclass
630
+ class PipelineExecution:
631
+ """Result from pipeline execution"""
632
+
633
+ execution_id: str # pex_XXXXX
634
+ pipeline_id: str # pl_XXXXX
635
+ pipeline_version: int
636
+ status: str # pending, running, completed, completed_with_errors, failed
637
+ steps: List[PipelineExecutionStepResult] = field(default_factory=list)
638
+ started_at: Optional[str] = None
639
+ completed_at: Optional[str] = None
640
+ created: Optional[str] = None
641
+ config_snapshot: Optional[Dict[str, Any]] = None
642
+ input_config: Optional[Dict[str, Any]] = None
643
+ rate_breakdown: Optional[Dict[str, Any]] = None
644
+
645
+
646
+ # --- Custom Processor models ---
647
+
648
+
649
+ @dataclass
650
+ class CustomProcessor:
651
+ """Represents a custom processor (formerly custom pipeline)"""
652
+
653
+ processor_id: str # cp_XXXXX
654
+ status: str # processing, completed, failed
655
+ name: Optional[str] = None
656
+ success: Optional[bool] = None
657
+ active_version: int = 0
658
+ max_version: int = 0
659
+ iteration_in_progress: bool = False
660
+ pipeline_id: Optional[str] = None # auto-created workspace pipeline (pl_XXXXX)
661
+ created_at: Optional[str] = None
662
+ completed_at: Optional[str] = None
663
+ error_message: Optional[str] = None
664
+ eval_rubric_id: Optional[int] = None
665
+
666
+
667
+ @dataclass
668
+ class CustomProcessorVersion:
669
+ """Version information for a custom processor"""
670
+
671
+ version: int
672
+ request_description: str = ""
673
+ created_at: Optional[str] = None
674
+ runtime: Optional[float] = None
675
+ is_active: bool = False
@@ -1,12 +0,0 @@
1
- datalab_sdk/__init__.py,sha256=kK3JxJILvUTpYe6pFLMRHI19lmQL6pSY9p7urNy6-WM,1290
2
- datalab_sdk/cli.py,sha256=NELgNZstpBrhPoQHqsI67kNRYew_nJpq23_ENsWvmGY,38859
3
- datalab_sdk/client.py,sha256=qZyfFsalkeR715tGmMGTILtRtFsD0ILjjbQ8JoCN7LE,73975
4
- datalab_sdk/exceptions.py,sha256=MDfNd-l152OUVhZSfEsE95McJq-IuOsrEsEQ4CRMbEI,785
5
- datalab_sdk/mimetypes.py,sha256=a4ULJmHxoR_RMsD0j51hx0t2Q7In_PzNm7UlWX07eEE,932
6
- datalab_sdk/models.py,sha256=-pxDfTG-8jmp8DG-o1waJsaIk6li6iO3MR7yDwFGZ_8,17108
7
- datalab_sdk/settings.py,sha256=BoojrRcKE7T04s-vqCPFzKqopV_qqTrAtCCsjHKk3FU,365
8
- datalab_python_sdk-0.4.0.dist-info/METADATA,sha256=jFyvrY1HwLRDl2_nRpUH9aV3k7FoUvtU9Dbacgp-l9w,2238
9
- datalab_python_sdk-0.4.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
10
- datalab_python_sdk-0.4.0.dist-info/entry_points.txt,sha256=rqWxO21UV57JxxyeF20iBTxHpzBYOA69hCHIqwdn-Ok,48
11
- datalab_python_sdk-0.4.0.dist-info/licenses/LICENSE,sha256=XGjT-Uz6t7UZzr3JCZfilsGcZEgMm_1aONcRunohxWg,1064
12
- datalab_python_sdk-0.4.0.dist-info/RECORD,,