pulse-python-sdk 1.0.6__tar.gz → 1.0.7__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.
Files changed (129) hide show
  1. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/PKG-INFO +1 -1
  2. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/pyproject.toml +1 -1
  3. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/client.py +113 -2
  4. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/client_wrapper.py +2 -2
  5. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/raw_client.py +171 -2
  6. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/schema_config.py +8 -3
  7. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/single_schema_response.py +10 -0
  8. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/README.md +0 -0
  9. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/__init__.py +0 -0
  10. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/batch/__init__.py +0 -0
  11. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/batch/client.py +0 -0
  12. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/batch/raw_client.py +0 -0
  13. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/__init__.py +0 -0
  14. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/api_error.py +0 -0
  15. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/datetime_utils.py +0 -0
  16. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/file.py +0 -0
  17. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/force_multipart.py +0 -0
  18. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_client.py +0 -0
  19. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_response.py +0 -0
  20. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_sse/__init__.py +0 -0
  21. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_sse/_api.py +0 -0
  22. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_sse/_decoders.py +0 -0
  23. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_sse/_exceptions.py +0 -0
  24. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/http_sse/_models.py +0 -0
  25. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/jsonable_encoder.py +0 -0
  26. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/pydantic_utilities.py +0 -0
  27. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/query_encoder.py +0 -0
  28. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/remove_none_from_dict.py +0 -0
  29. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/request_options.py +0 -0
  30. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/serialization.py +0 -0
  31. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/core/unchecked_base_model.py +0 -0
  32. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/environment.py +0 -0
  33. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/__init__.py +0 -0
  34. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/bad_request_error.py +0 -0
  35. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/forbidden_error.py +0 -0
  36. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/internal_server_error.py +0 -0
  37. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/not_found_error.py +0 -0
  38. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/too_many_requests_error.py +0 -0
  39. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/errors/unauthorized_error.py +0 -0
  40. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/jobs/__init__.py +0 -0
  41. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/jobs/client.py +0 -0
  42. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/jobs/raw_client.py +0 -0
  43. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/py.typed +0 -0
  44. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/__init__.py +0 -0
  45. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/async_submission_response.py +0 -0
  46. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/async_submission_response_status.py +0 -0
  47. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_extract_response.py +0 -0
  48. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_extract_response_status.py +0 -0
  49. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_extraction_ids.py +0 -0
  50. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_input_source.py +0 -0
  51. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_output_destination.py +0 -0
  52. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_schema_response.py +0 -0
  53. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_schema_response_status.py +0 -0
  54. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_split_response.py +0 -0
  55. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_split_response_status.py +0 -0
  56. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_tables_response.py +0 -0
  57. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/batch_tables_response_status.py +0 -0
  58. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_extensions.py +0 -0
  59. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_extensions_alt_outputs.py +0 -0
  60. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_extensions_chunking.py +0 -0
  61. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_extensions_chunking_chunk_types_item.py +0 -0
  62. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_figure_processing.py +0 -0
  63. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_model.py +0 -0
  64. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_schema.py +0 -0
  65. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_storage.py +0 -0
  66. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_request_structured_output.py +0 -0
  67. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_async_submission_response.py +0 -0
  68. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input.py +0 -0
  69. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_extensions.py +0 -0
  70. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_extensions_alt_outputs.py +0 -0
  71. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_extensions_chunking.py +0 -0
  72. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_extensions_chunking_chunk_types_item.py +0 -0
  73. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_figure_processing.py +0 -0
  74. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_model.py +0 -0
  75. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_schema.py +0 -0
  76. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_storage.py +0 -0
  77. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_input_structured_output.py +0 -0
  78. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options.py +0 -0
  79. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_extensions.py +0 -0
  80. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_extensions_alt_outputs.py +0 -0
  81. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_extensions_chunking.py +0 -0
  82. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_extensions_chunking_chunk_types_item.py +0 -0
  83. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_figure_processing.py +0 -0
  84. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_model.py +0 -0
  85. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_schema.py +0 -0
  86. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_storage.py +0 -0
  87. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_options_structured_output.py +0 -0
  88. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_extensions.py +0 -0
  89. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_extensions_alt_outputs.py +0 -0
  90. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_extensions_chunking.py +0 -0
  91. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_extensions_chunking_chunk_types_item.py +0 -0
  92. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_figure_processing.py +0 -0
  93. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_model.py +0 -0
  94. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_schema.py +0 -0
  95. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_storage.py +0 -0
  96. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_request_structured_output.py +0 -0
  97. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response.py +0 -0
  98. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_chunks.py +0 -0
  99. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_extensions.py +0 -0
  100. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_extensions_alt_outputs.py +0 -0
  101. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_extensions_alt_outputs_wlbb.py +0 -0
  102. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_extensions_alt_outputs_wlbb_words_item.py +0 -0
  103. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_extensions_chunking.py +0 -0
  104. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_extensions_footnote_references_item.py +0 -0
  105. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_response_plan_info.py +0 -0
  106. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/extract_source.py +0 -0
  107. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/job_cancellation_response.py +0 -0
  108. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/job_status.py +0 -0
  109. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/job_status_response.py +0 -0
  110. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/schema_response.py +0 -0
  111. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/split_config.py +0 -0
  112. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/split_output.py +0 -0
  113. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/split_response.py +0 -0
  114. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/split_schema_response.py +0 -0
  115. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/structured_output_config.py +0 -0
  116. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/structured_output_result.py +0 -0
  117. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/tables_config.py +0 -0
  118. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/tables_config_table_format.py +0 -0
  119. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/tables_response.py +0 -0
  120. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/tables_response_tables_output.py +0 -0
  121. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/tables_response_tables_output_tables_item.py +0 -0
  122. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/topic_definition.py +0 -0
  123. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/types/topic_schema_config.py +0 -0
  124. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/version.py +0 -0
  125. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/webhooks/__init__.py +0 -0
  126. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/webhooks/client.py +0 -0
  127. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/webhooks/raw_client.py +0 -0
  128. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/webhooks/types/__init__.py +0 -0
  129. {pulse_python_sdk-1.0.6 → pulse_python_sdk-1.0.7}/src/pulse/webhooks/types/create_webhook_link_response.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pulse-python-sdk
3
- Version: 1.0.6
3
+ Version: 1.0.7
4
4
  Summary:
5
5
  Requires-Python: >=3.8,<4.0
6
6
  Classifier: Intended Audience :: Developers
@@ -4,7 +4,7 @@ dynamic = ["version"]
4
4
 
5
5
  [tool.poetry]
6
6
  name = "pulse-python-sdk"
7
- version = "1.0.6"
7
+ version = "1.0.7"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = []
@@ -460,6 +460,7 @@ class Pulse:
460
460
  self,
461
461
  *,
462
462
  extraction_id: typing.Optional[str] = OMIT,
463
+ extraction_ids: typing.Optional[typing.Sequence[str]] = OMIT,
463
464
  split_id: typing.Optional[str] = OMIT,
464
465
  schema_config: typing.Optional[SchemaConfig] = OMIT,
465
466
  schema_config_id: typing.Optional[str] = OMIT,
@@ -474,10 +475,20 @@ class Pulse:
474
475
  **Single mode** — Provide `extraction_id` + `schema_config` (or
475
476
  `schema_config_id`) to apply one schema to the entire document.
476
477
 
478
+ **Multi-extraction mode** — Provide a batch extract ID as `extraction_id`
479
+ (auto-detected) or an explicit `extraction_ids` list. The content from all
480
+ extractions is combined and the schema is applied to the composite. Citations
481
+ use `extraction_id-bb_id` format to disambiguate across source documents.
482
+
477
483
  **Split mode** — Provide `split_id` + `split_schema_config` to apply
478
484
  different schemas to different page groups from a prior `/split` call.
479
485
  Each topic can have its own schema, prompt, and effort setting.
480
486
 
487
+ **Excel template mode** — Provide `excel_template` (base64 .xlsx) in
488
+ `schema_config` instead of `input_schema`. The schema is auto-generated
489
+ from the template's column headers, and a filled copy is returned as
490
+ `excel_output_url`.
491
+
481
492
  Creates a versioned schema record that can be retrieved later.
482
493
  Set `async: true` to return immediately with a job_id for polling.
483
494
 
@@ -488,7 +499,10 @@ class Pulse:
488
499
  Parameters
489
500
  ----------
490
501
  extraction_id : typing.Optional[str]
491
- ID of saved extraction to apply the schema to. Use for single-mode schema extraction.
502
+ ID of a saved extraction OR a batch extract job. When a batch extract ID is provided, the system auto-detects it and combines all completed child extractions into a single schema application.
503
+
504
+ extraction_ids : typing.Optional[typing.Sequence[str]]
505
+ Explicit list of extraction IDs to combine. The markdown and bounding boxes from all extractions are merged and the schema is applied to the composite content. Citations use `extraction_id-bb_id` format to disambiguate across source documents.
492
506
 
493
507
  split_id : typing.Optional[str]
494
508
  ID of saved split (from a prior `/split` call). Use for split-mode schema extraction.
@@ -524,6 +538,7 @@ class Pulse:
524
538
  """
525
539
  _response = self._raw_client.schema(
526
540
  extraction_id=extraction_id,
541
+ extraction_ids=extraction_ids,
527
542
  split_id=split_id,
528
543
  schema_config=schema_config,
529
544
  schema_config_id=schema_config_id,
@@ -533,6 +548,42 @@ class Pulse:
533
548
  )
534
549
  return _response.data
535
550
 
551
+ def download_schema_excel(
552
+ self, schema_id: str, *, request_options: typing.Optional[RequestOptions] = None
553
+ ) -> typing.Iterator[bytes]:
554
+ """
555
+ Download the filled Excel template produced by a schema extraction that
556
+ used `excel_template` in its `schema_config`. Requires the same API key
557
+ authentication as other endpoints. The caller must belong to the org
558
+ that owns the underlying extraction.
559
+
560
+ Parameters
561
+ ----------
562
+ schema_id : str
563
+ The schema ID returned from a prior `POST /schema` call.
564
+
565
+ request_options : typing.Optional[RequestOptions]
566
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
567
+
568
+ Returns
569
+ -------
570
+ typing.Iterator[bytes]
571
+ Filled Excel file
572
+
573
+ Examples
574
+ --------
575
+ from pulse import Pulse
576
+
577
+ client = Pulse(
578
+ api_key="YOUR_API_KEY",
579
+ )
580
+ client.download_schema_excel(
581
+ schema_id="schemaId",
582
+ )
583
+ """
584
+ with self._raw_client.download_schema_excel(schema_id, request_options=request_options) as r:
585
+ yield from r.data
586
+
536
587
  def tables(
537
588
  self,
538
589
  *,
@@ -1057,6 +1108,7 @@ class AsyncPulse:
1057
1108
  self,
1058
1109
  *,
1059
1110
  extraction_id: typing.Optional[str] = OMIT,
1111
+ extraction_ids: typing.Optional[typing.Sequence[str]] = OMIT,
1060
1112
  split_id: typing.Optional[str] = OMIT,
1061
1113
  schema_config: typing.Optional[SchemaConfig] = OMIT,
1062
1114
  schema_config_id: typing.Optional[str] = OMIT,
@@ -1071,10 +1123,20 @@ class AsyncPulse:
1071
1123
  **Single mode** — Provide `extraction_id` + `schema_config` (or
1072
1124
  `schema_config_id`) to apply one schema to the entire document.
1073
1125
 
1126
+ **Multi-extraction mode** — Provide a batch extract ID as `extraction_id`
1127
+ (auto-detected) or an explicit `extraction_ids` list. The content from all
1128
+ extractions is combined and the schema is applied to the composite. Citations
1129
+ use `extraction_id-bb_id` format to disambiguate across source documents.
1130
+
1074
1131
  **Split mode** — Provide `split_id` + `split_schema_config` to apply
1075
1132
  different schemas to different page groups from a prior `/split` call.
1076
1133
  Each topic can have its own schema, prompt, and effort setting.
1077
1134
 
1135
+ **Excel template mode** — Provide `excel_template` (base64 .xlsx) in
1136
+ `schema_config` instead of `input_schema`. The schema is auto-generated
1137
+ from the template's column headers, and a filled copy is returned as
1138
+ `excel_output_url`.
1139
+
1078
1140
  Creates a versioned schema record that can be retrieved later.
1079
1141
  Set `async: true` to return immediately with a job_id for polling.
1080
1142
 
@@ -1085,7 +1147,10 @@ class AsyncPulse:
1085
1147
  Parameters
1086
1148
  ----------
1087
1149
  extraction_id : typing.Optional[str]
1088
- ID of saved extraction to apply the schema to. Use for single-mode schema extraction.
1150
+ ID of a saved extraction OR a batch extract job. When a batch extract ID is provided, the system auto-detects it and combines all completed child extractions into a single schema application.
1151
+
1152
+ extraction_ids : typing.Optional[typing.Sequence[str]]
1153
+ Explicit list of extraction IDs to combine. The markdown and bounding boxes from all extractions are merged and the schema is applied to the composite content. Citations use `extraction_id-bb_id` format to disambiguate across source documents.
1089
1154
 
1090
1155
  split_id : typing.Optional[str]
1091
1156
  ID of saved split (from a prior `/split` call). Use for split-mode schema extraction.
@@ -1129,6 +1194,7 @@ class AsyncPulse:
1129
1194
  """
1130
1195
  _response = await self._raw_client.schema(
1131
1196
  extraction_id=extraction_id,
1197
+ extraction_ids=extraction_ids,
1132
1198
  split_id=split_id,
1133
1199
  schema_config=schema_config,
1134
1200
  schema_config_id=schema_config_id,
@@ -1138,6 +1204,51 @@ class AsyncPulse:
1138
1204
  )
1139
1205
  return _response.data
1140
1206
 
1207
+ async def download_schema_excel(
1208
+ self, schema_id: str, *, request_options: typing.Optional[RequestOptions] = None
1209
+ ) -> typing.AsyncIterator[bytes]:
1210
+ """
1211
+ Download the filled Excel template produced by a schema extraction that
1212
+ used `excel_template` in its `schema_config`. Requires the same API key
1213
+ authentication as other endpoints. The caller must belong to the org
1214
+ that owns the underlying extraction.
1215
+
1216
+ Parameters
1217
+ ----------
1218
+ schema_id : str
1219
+ The schema ID returned from a prior `POST /schema` call.
1220
+
1221
+ request_options : typing.Optional[RequestOptions]
1222
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
1223
+
1224
+ Returns
1225
+ -------
1226
+ typing.AsyncIterator[bytes]
1227
+ Filled Excel file
1228
+
1229
+ Examples
1230
+ --------
1231
+ import asyncio
1232
+
1233
+ from pulse import AsyncPulse
1234
+
1235
+ client = AsyncPulse(
1236
+ api_key="YOUR_API_KEY",
1237
+ )
1238
+
1239
+
1240
+ async def main() -> None:
1241
+ await client.download_schema_excel(
1242
+ schema_id="schemaId",
1243
+ )
1244
+
1245
+
1246
+ asyncio.run(main())
1247
+ """
1248
+ async with self._raw_client.download_schema_excel(schema_id, request_options=request_options) as r:
1249
+ async for _chunk in r.data:
1250
+ yield _chunk
1251
+
1141
1252
  async def tables(
1142
1253
  self,
1143
1254
  *,
@@ -22,10 +22,10 @@ class BaseClientWrapper:
22
22
 
23
23
  def get_headers(self) -> typing.Dict[str, str]:
24
24
  headers: typing.Dict[str, str] = {
25
- "User-Agent": "pulse-python-sdk/1.0.6",
25
+ "User-Agent": "pulse-python-sdk/1.0.7",
26
26
  "X-Fern-Language": "Python",
27
27
  "X-Fern-SDK-Name": "pulse-python-sdk",
28
- "X-Fern-SDK-Version": "1.0.6",
28
+ "X-Fern-SDK-Version": "1.0.7",
29
29
  **(self.get_custom_headers() or {}),
30
30
  }
31
31
  headers["x-api-key"] = self.api_key
@@ -1,5 +1,6 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
+ import contextlib
3
4
  import typing
4
5
  from json.decoder import JSONDecodeError
5
6
 
@@ -7,6 +8,7 @@ from . import core
7
8
  from .core.api_error import ApiError
8
9
  from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
9
10
  from .core.http_response import AsyncHttpResponse, HttpResponse
11
+ from .core.jsonable_encoder import jsonable_encoder
10
12
  from .core.request_options import RequestOptions
11
13
  from .core.serialization import convert_and_respect_annotation_metadata
12
14
  from .core.unchecked_base_model import construct_type
@@ -538,6 +540,7 @@ class RawPulse:
538
540
  self,
539
541
  *,
540
542
  extraction_id: typing.Optional[str] = OMIT,
543
+ extraction_ids: typing.Optional[typing.Sequence[str]] = OMIT,
541
544
  split_id: typing.Optional[str] = OMIT,
542
545
  schema_config: typing.Optional[SchemaConfig] = OMIT,
543
546
  schema_config_id: typing.Optional[str] = OMIT,
@@ -552,10 +555,20 @@ class RawPulse:
552
555
  **Single mode** — Provide `extraction_id` + `schema_config` (or
553
556
  `schema_config_id`) to apply one schema to the entire document.
554
557
 
558
+ **Multi-extraction mode** — Provide a batch extract ID as `extraction_id`
559
+ (auto-detected) or an explicit `extraction_ids` list. The content from all
560
+ extractions is combined and the schema is applied to the composite. Citations
561
+ use `extraction_id-bb_id` format to disambiguate across source documents.
562
+
555
563
  **Split mode** — Provide `split_id` + `split_schema_config` to apply
556
564
  different schemas to different page groups from a prior `/split` call.
557
565
  Each topic can have its own schema, prompt, and effort setting.
558
566
 
567
+ **Excel template mode** — Provide `excel_template` (base64 .xlsx) in
568
+ `schema_config` instead of `input_schema`. The schema is auto-generated
569
+ from the template's column headers, and a filled copy is returned as
570
+ `excel_output_url`.
571
+
559
572
  Creates a versioned schema record that can be retrieved later.
560
573
  Set `async: true` to return immediately with a job_id for polling.
561
574
 
@@ -566,7 +579,10 @@ class RawPulse:
566
579
  Parameters
567
580
  ----------
568
581
  extraction_id : typing.Optional[str]
569
- ID of saved extraction to apply the schema to. Use for single-mode schema extraction.
582
+ ID of a saved extraction OR a batch extract job. When a batch extract ID is provided, the system auto-detects it and combines all completed child extractions into a single schema application.
583
+
584
+ extraction_ids : typing.Optional[typing.Sequence[str]]
585
+ Explicit list of extraction IDs to combine. The markdown and bounding boxes from all extractions are merged and the schema is applied to the composite content. Citations use `extraction_id-bb_id` format to disambiguate across source documents.
570
586
 
571
587
  split_id : typing.Optional[str]
572
588
  ID of saved split (from a prior `/split` call). Use for split-mode schema extraction.
@@ -596,6 +612,7 @@ class RawPulse:
596
612
  method="POST",
597
613
  json={
598
614
  "extraction_id": extraction_id,
615
+ "extraction_ids": extraction_ids,
599
616
  "split_id": split_id,
600
617
  "schema_config": convert_and_respect_annotation_metadata(
601
618
  object_=schema_config, annotation=SchemaConfig, direction="write"
@@ -682,6 +699,74 @@ class RawPulse:
682
699
  raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
683
700
  raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
684
701
 
702
+ @contextlib.contextmanager
703
+ def download_schema_excel(
704
+ self, schema_id: str, *, request_options: typing.Optional[RequestOptions] = None
705
+ ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
706
+ """
707
+ Download the filled Excel template produced by a schema extraction that
708
+ used `excel_template` in its `schema_config`. Requires the same API key
709
+ authentication as other endpoints. The caller must belong to the org
710
+ that owns the underlying extraction.
711
+
712
+ Parameters
713
+ ----------
714
+ schema_id : str
715
+ The schema ID returned from a prior `POST /schema` call.
716
+
717
+ request_options : typing.Optional[RequestOptions]
718
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
719
+
720
+ Returns
721
+ -------
722
+ typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
723
+ Filled Excel file
724
+ """
725
+ with self._client_wrapper.httpx_client.stream(
726
+ f"schema/{jsonable_encoder(schema_id)}/excel",
727
+ method="GET",
728
+ request_options=request_options,
729
+ ) as _response:
730
+
731
+ def _stream() -> HttpResponse[typing.Iterator[bytes]]:
732
+ try:
733
+ if 200 <= _response.status_code < 300:
734
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
735
+ return HttpResponse(
736
+ response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
737
+ )
738
+ _response.read()
739
+ if _response.status_code == 401:
740
+ raise UnauthorizedError(
741
+ headers=dict(_response.headers),
742
+ body=typing.cast(
743
+ typing.Any,
744
+ construct_type(
745
+ type_=typing.Any, # type: ignore
746
+ object_=_response.json(),
747
+ ),
748
+ ),
749
+ )
750
+ if _response.status_code == 404:
751
+ raise NotFoundError(
752
+ headers=dict(_response.headers),
753
+ body=typing.cast(
754
+ typing.Any,
755
+ construct_type(
756
+ type_=typing.Any, # type: ignore
757
+ object_=_response.json(),
758
+ ),
759
+ ),
760
+ )
761
+ _response_json = _response.json()
762
+ except JSONDecodeError:
763
+ raise ApiError(
764
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
765
+ )
766
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
767
+
768
+ yield _stream()
769
+
685
770
  def tables(
686
771
  self,
687
772
  *,
@@ -1317,6 +1402,7 @@ class AsyncRawPulse:
1317
1402
  self,
1318
1403
  *,
1319
1404
  extraction_id: typing.Optional[str] = OMIT,
1405
+ extraction_ids: typing.Optional[typing.Sequence[str]] = OMIT,
1320
1406
  split_id: typing.Optional[str] = OMIT,
1321
1407
  schema_config: typing.Optional[SchemaConfig] = OMIT,
1322
1408
  schema_config_id: typing.Optional[str] = OMIT,
@@ -1331,10 +1417,20 @@ class AsyncRawPulse:
1331
1417
  **Single mode** — Provide `extraction_id` + `schema_config` (or
1332
1418
  `schema_config_id`) to apply one schema to the entire document.
1333
1419
 
1420
+ **Multi-extraction mode** — Provide a batch extract ID as `extraction_id`
1421
+ (auto-detected) or an explicit `extraction_ids` list. The content from all
1422
+ extractions is combined and the schema is applied to the composite. Citations
1423
+ use `extraction_id-bb_id` format to disambiguate across source documents.
1424
+
1334
1425
  **Split mode** — Provide `split_id` + `split_schema_config` to apply
1335
1426
  different schemas to different page groups from a prior `/split` call.
1336
1427
  Each topic can have its own schema, prompt, and effort setting.
1337
1428
 
1429
+ **Excel template mode** — Provide `excel_template` (base64 .xlsx) in
1430
+ `schema_config` instead of `input_schema`. The schema is auto-generated
1431
+ from the template's column headers, and a filled copy is returned as
1432
+ `excel_output_url`.
1433
+
1338
1434
  Creates a versioned schema record that can be retrieved later.
1339
1435
  Set `async: true` to return immediately with a job_id for polling.
1340
1436
 
@@ -1345,7 +1441,10 @@ class AsyncRawPulse:
1345
1441
  Parameters
1346
1442
  ----------
1347
1443
  extraction_id : typing.Optional[str]
1348
- ID of saved extraction to apply the schema to. Use for single-mode schema extraction.
1444
+ ID of a saved extraction OR a batch extract job. When a batch extract ID is provided, the system auto-detects it and combines all completed child extractions into a single schema application.
1445
+
1446
+ extraction_ids : typing.Optional[typing.Sequence[str]]
1447
+ Explicit list of extraction IDs to combine. The markdown and bounding boxes from all extractions are merged and the schema is applied to the composite content. Citations use `extraction_id-bb_id` format to disambiguate across source documents.
1349
1448
 
1350
1449
  split_id : typing.Optional[str]
1351
1450
  ID of saved split (from a prior `/split` call). Use for split-mode schema extraction.
@@ -1375,6 +1474,7 @@ class AsyncRawPulse:
1375
1474
  method="POST",
1376
1475
  json={
1377
1476
  "extraction_id": extraction_id,
1477
+ "extraction_ids": extraction_ids,
1378
1478
  "split_id": split_id,
1379
1479
  "schema_config": convert_and_respect_annotation_metadata(
1380
1480
  object_=schema_config, annotation=SchemaConfig, direction="write"
@@ -1461,6 +1561,75 @@ class AsyncRawPulse:
1461
1561
  raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1462
1562
  raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1463
1563
 
1564
+ @contextlib.asynccontextmanager
1565
+ async def download_schema_excel(
1566
+ self, schema_id: str, *, request_options: typing.Optional[RequestOptions] = None
1567
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
1568
+ """
1569
+ Download the filled Excel template produced by a schema extraction that
1570
+ used `excel_template` in its `schema_config`. Requires the same API key
1571
+ authentication as other endpoints. The caller must belong to the org
1572
+ that owns the underlying extraction.
1573
+
1574
+ Parameters
1575
+ ----------
1576
+ schema_id : str
1577
+ The schema ID returned from a prior `POST /schema` call.
1578
+
1579
+ request_options : typing.Optional[RequestOptions]
1580
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
1581
+
1582
+ Returns
1583
+ -------
1584
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
1585
+ Filled Excel file
1586
+ """
1587
+ async with self._client_wrapper.httpx_client.stream(
1588
+ f"schema/{jsonable_encoder(schema_id)}/excel",
1589
+ method="GET",
1590
+ request_options=request_options,
1591
+ ) as _response:
1592
+
1593
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
1594
+ try:
1595
+ if 200 <= _response.status_code < 300:
1596
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
1597
+ return AsyncHttpResponse(
1598
+ response=_response,
1599
+ data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
1600
+ )
1601
+ await _response.aread()
1602
+ if _response.status_code == 401:
1603
+ raise UnauthorizedError(
1604
+ headers=dict(_response.headers),
1605
+ body=typing.cast(
1606
+ typing.Any,
1607
+ construct_type(
1608
+ type_=typing.Any, # type: ignore
1609
+ object_=_response.json(),
1610
+ ),
1611
+ ),
1612
+ )
1613
+ if _response.status_code == 404:
1614
+ raise NotFoundError(
1615
+ headers=dict(_response.headers),
1616
+ body=typing.cast(
1617
+ typing.Any,
1618
+ construct_type(
1619
+ type_=typing.Any, # type: ignore
1620
+ object_=_response.json(),
1621
+ ),
1622
+ ),
1623
+ )
1624
+ _response_json = _response.json()
1625
+ except JSONDecodeError:
1626
+ raise ApiError(
1627
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
1628
+ )
1629
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1630
+
1631
+ yield await _stream()
1632
+
1464
1633
  async def tables(
1465
1634
  self,
1466
1635
  *,
@@ -9,12 +9,17 @@ from ..core.unchecked_base_model import UncheckedBaseModel
9
9
 
10
10
  class SchemaConfig(UncheckedBaseModel):
11
11
  """
12
- Inline schema configuration.
12
+ Inline schema configuration. Provide `input_schema` (JSON Schema) OR `excel_template` (base64 .xlsx) — not both. When `excel_template` is provided, the JSON Schema is auto-generated from the spreadsheet's column headers.
13
13
  """
14
14
 
15
- input_schema: typing.Dict[str, typing.Any] = pydantic.Field()
15
+ input_schema: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None)
16
16
  """
17
- JSON Schema defining the structured data to extract.
17
+ JSON Schema defining the structured data to extract. Required unless `excel_template` is provided.
18
+ """
19
+
20
+ excel_template: typing.Optional[str] = pydantic.Field(default=None)
21
+ """
22
+ Base64-encoded Excel template (.xlsx). When provided, the template's column headers are used to auto-generate the JSON Schema and a filled copy of the template is returned in the response as `excel_output_url`. Mutually exclusive with `input_schema`.
18
23
  """
19
24
 
20
25
  schema_prompt: typing.Optional[str] = pydantic.Field(default=None)
@@ -28,6 +28,16 @@ class SingleSchemaResponse(UncheckedBaseModel):
28
28
  Extracted values and citations.
29
29
  """
30
30
 
31
+ extraction_ids: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
32
+ """
33
+ Present when multiple extractions were combined (via batch extract auto-detection or explicit `extraction_ids` input). Lists all source extraction IDs that contributed to the result.
34
+ """
35
+
36
+ excel_output_url: typing.Optional[str] = pydantic.Field(default=None)
37
+ """
38
+ API path to download the filled Excel template (e.g. `/schema/{schema_id}/excel`). Requires the same API key authentication. Only present when `excel_template` was provided in the request.
39
+ """
40
+
31
41
  if IS_PYDANTIC_V2:
32
42
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
33
43
  else: