diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/model/api/ai/AiWorkflowOutcome.java b/app/proprietary/src/main/java/stirling/software/proprietary/model/api/ai/AiWorkflowOutcome.java index 2bed56f0f0..a7239e8f90 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/model/api/ai/AiWorkflowOutcome.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/model/api/ai/AiWorkflowOutcome.java @@ -21,8 +21,7 @@ public enum AiWorkflowOutcome { COMPLETED("completed"), UNSUPPORTED_CAPABILITY("unsupported_capability"), CANNOT_CONTINUE("cannot_continue"), - GENERATE_FILE("generate_file"), - CONVERT_MARKDOWN("convert_markdown"); + GENERATE_FILE("generate_file"); private final String value; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/service/AiWorkflowService.java b/app/proprietary/src/main/java/stirling/software/proprietary/service/AiWorkflowService.java index 4e6c515318..d3b2d21a00 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/service/AiWorkflowService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/service/AiWorkflowService.java @@ -68,7 +68,6 @@ import tools.jackson.databind.ObjectMapper; public class AiWorkflowService { private static final String DOCUMENTS_ENDPOINT = "/api/v1/documents"; - private static final String PDF_TO_MARKDOWN_ENDPOINT = "/api/v1/convert/pdf/markdown"; private final CustomPDFDocumentFactory pdfDocumentFactory; private final AiEngineClient aiEngineClient; @@ -196,7 +195,6 @@ public class AiWorkflowService { return switch (response.getOutcome()) { case NEED_CONTENT -> onNeedContent(response, filesById, request, listener); case NEED_INGEST -> onNeedIngest(response, filesById, request, listener); - case CONVERT_MARKDOWN -> onConvertMarkdown(response, filesById, listener); case TOOL_CALL -> onToolCall(response, filesById, listener); case PLAN -> onPlan(response, filesById, request, listener); case ANSWER -> onAnswer(response, filesById, request, listener); @@ -333,72 +331,6 @@ public class AiWorkflowService { return new WorkflowState.Pending(nextRequest); } - /** - * Deterministically convert each requested PDF to Markdown via the {@code - * /convert/pdf/markdown} endpoint (backed by {@code PdfMarkdownConverter}) and return the - * {@code .md} file(s) as a completed result. No AI resume — the conversion output is the final - * answer. - */ - private WorkflowState onConvertMarkdown( - AiWorkflowResponse response, - Map filesById, - ProgressListener listener) { - List filesToConvert = response.getFilesToIngest(); - if (filesToConvert == null || filesToConvert.isEmpty()) { - return new WorkflowState.Terminal( - cannotContinue( - "AI engine requested markdown conversion without listing any files.")); - } - - try { - List resultFiles = new ArrayList<>(); - List inputNames = new ArrayList<>(); - for (int i = 0; i < filesToConvert.size(); i++) { - AiFile file = filesToConvert.get(i); - MultipartFile multipartFile = filesById.get(file.getId()); - if (multipartFile == null) { - return new WorkflowState.Terminal( - cannotContinue( - "AI engine requested markdown conversion for unknown file: " - + file.getName())); - } - listener.onProgress( - AiWorkflowProgressEvent.executingTool( - PDF_TO_MARKDOWN_ENDPOINT, i + 1, filesToConvert.size())); - Resource input = toResource(multipartFile); - PipelineDefinition definition = - new PipelineDefinition( - "convert-markdown", - List.of(new PipelineStep(PDF_TO_MARKDOWN_ENDPOINT, Map.of())), - null); - PolicyExecutionResult result = - policyExecutor.execute( - definition, - PolicyInputs.of(List.of(input)), - PolicyProgressListener.NOOP); - resultFiles.addAll(result.files()); - inputNames.add(multipartFile.getOriginalFilename()); - } - return new WorkflowState.Terminal( - buildCompletedResponse(null, resultFiles, inputNames, null)); - } catch (InternalApiTimeoutException e) { - log.error("PDF to Markdown conversion timed out: {}", e.getMessage()); - return new WorkflowState.Terminal( - cannotContinue(toolTimeoutMessage(PDF_TO_MARKDOWN_ENDPOINT, e))); - } catch (Exception e) { - AiWorkflowResponse limit = paygLimitResponseOrNull(e); - if (limit != null) { - log.info( - "AI markdown conversion blocked by downstream entitlement gate ({})", - limit.getErrorCode()); - return new WorkflowState.Terminal(limit); - } - log.error("Failed to convert PDF to Markdown: {}", e.getMessage(), e); - return new WorkflowState.Terminal( - cannotContinue(toolFailureMessage(PDF_TO_MARKDOWN_ENDPOINT, e))); - } - } - private Resource toResource(MultipartFile file) throws IOException { TempFile tempFile = tempFileManager.createManagedTempFile("ai-workflow"); file.transferTo(tempFile.getPath()); diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceMoreTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceMoreTest.java index cc8388b8c2..7c44a40a5d 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceMoreTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceMoreTest.java @@ -221,34 +221,6 @@ class AiWorkflowServiceMoreTest { } } - @Nested - @DisplayName("convert_markdown guards") - class ConvertMarkdownGuards { - - @Test - @DisplayName("no files listed yields CANNOT_CONTINUE") - void noFiles() throws IOException { - stubOrchestrator("{\"outcome\":\"convert_markdown\",\"filesToIngest\":[]}"); - AiWorkflowResponse result = service.orchestrate(requestFor(pdf("a.pdf", "x"), "to md")); - assertThat(result.getOutcome()).isEqualTo(AiWorkflowOutcome.CANNOT_CONTINUE); - } - - @Test - @DisplayName("unknown file id yields CANNOT_CONTINUE") - void unknownFile() throws IOException { - when(fileIdStrategy.idFor(any())).thenReturn("real-id"); - stubOrchestrator( - """ - {"outcome":"convert_markdown", - "filesToIngest":[{"id":"other-id","name":"other.pdf"}]} - """); - AiWorkflowResponse result = - service.orchestrate(requestFor(pdf("real.pdf", "x"), "to md")); - assertThat(result.getOutcome()).isEqualTo(AiWorkflowOutcome.CANNOT_CONTINUE); - assertThat(result.getReason()).contains("other.pdf"); - } - } - @Nested @DisplayName("plan guards and errors") class PlanGuardsAndErrors { diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceTest.java index 9e611cf32e..7dc174c041 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/service/AiWorkflowServiceTest.java @@ -78,6 +78,7 @@ class AiWorkflowServiceTest { private static final String SPLIT_ENDPOINT = "/api/v1/general/split-pages"; private static final String MERGE_ENDPOINT = "/api/v1/general/merge-pdfs"; private static final String COMPRESS_ENDPOINT = "/api/v1/misc/compress-pdf"; + private static final String MARKDOWN_ENDPOINT = "/api/v1/convert/pdf/markdown"; @Mock private CustomPDFDocumentFactory pdfDocumentFactory; @Mock private AiEngineClient aiEngineClient; @@ -440,23 +441,23 @@ class AiWorkflowServiceTest { } @Test - void convertMarkdownRunsDeterministicConversionAndReturnsMdFile() throws IOException { + void planWithMarkdownStepReturnsMdFile() throws IOException { + // PDF→Markdown is a normal tool the edit agent emits as a plan step (no bespoke + // outcome); the plan executor runs the converter and returns the .md file. MockMultipartFile input = pdf("multi-column-test_lorem.pdf", "pdf-bytes"); - when(fileIdStrategy.idFor(any())).thenReturn("doc-1"); stubOrchestrator( """ { - "outcome":"convert_markdown", - "reason":"PDF to Markdown requested.", - "filesToIngest":[{"id":"doc-1","name":"multi-column-test_lorem.pdf"}] + "outcome":"plan", + "summary":"Convert to Markdown", + "steps":[{"tool":"%s","parameters":{}}] } - """); - when(toolMetadataService.shouldUnpackZipResponse("/api/v1/convert/pdf/markdown")) - .thenReturn(false); - stubEndpoint( - "/api/v1/convert/pdf/markdown", - pdfResource("# Title", "multi-column-test_lorem.md")); - AtomicInteger ids = stubFileStorage(); + """ + .formatted(MARKDOWN_ENDPOINT)); + when(toolMetadataService.isMultiInput(anyString())).thenReturn(false); + when(toolMetadataService.shouldUnpackZipResponse(anyString())).thenReturn(false); + stubEndpoint(MARKDOWN_ENDPOINT, pdfResource("# Title", "multi-column-test_lorem.md")); + stubFileStorage(); AiWorkflowResponse result = service.orchestrate(requestFor(input, "convert to markdown")); @@ -464,8 +465,7 @@ class AiWorkflowServiceTest { assertEquals(1, result.getResultFiles().size()); // Extension changes (pdf -> md), so the converter's response filename wins. assertEquals("multi-column-test_lorem.md", result.getResultFiles().get(0).getFileName()); - assertEquals(1, ids.get()); - verify(internalApiClient, times(1)).post(eq("/api/v1/convert/pdf/markdown"), any()); + verify(internalApiClient, times(1)).post(eq(MARKDOWN_ENDPOINT), any()); } @Test diff --git a/engine/scripts/generate_tool_models.py b/engine/scripts/generate_tool_models.py index 9bc1458fea..dfe461fcca 100644 --- a/engine/scripts/generate_tool_models.py +++ b/engine/scripts/generate_tool_models.py @@ -59,6 +59,36 @@ class ToolDiscovery: "/api/v1/convert/", ) + # Endpoints under the allowed prefixes that are NOT edit-agent operations. A listed + # path and everything nested under it is dropped. Several kinds live here: + EXCLUDED_PATHS = ( + # 1. Cert-signing family: needs certificate/key files the agent can't supply, plus + # interactive session and hardware-token management. The whole subtree is dropped. + "/api/v1/security/cert-sign", + # 2. Interactive PDF text-editor endpoints, not one-shot operations. + "/api/v1/convert/pdf/text-editor", + "/api/v1/convert/text-editor/pdf", + # 3. Introspection / query endpoints that return metadata, a listing, or a + # verification verdict rather than a transformed document, so they belong to + # the question path, not the edit agent. (decompress is a dev-only stream op.) + "/api/v1/security/get-info-on-pdf", + "/api/v1/security/verify-pdf", + "/api/v1/security/validate-signature", + "/api/v1/misc/list-attachments", + "/api/v1/misc/show-javascript", + "/api/v1/misc/decompress-pdf", + "/api/v1/general/extract-bookmarks", + # 4. Require a secondary file (image, overlay PDF, attachments) on top of the input + # PDF. The agent only ever supplies the input PDF(s), so these can never run. + # (add-stamp / add-watermark stay: their text mode needs no extra file.) + "/api/v1/misc/add-image", + "/api/v1/misc/add-attachments", + "/api/v1/general/overlay-pdfs", + ) + + def _is_excluded(self, path: str) -> bool: + return any(path == p or path.startswith(p + "/") for p in self.EXCLUDED_PATHS) + def __init__(self, spec: dict[str, Any]): resource = Resource.from_contents(spec, default_specification=DRAFT202012) self.resolver = Registry().with_resource("", resource).resolver() @@ -73,17 +103,15 @@ class ToolDiscovery: for path, path_item in sorted(self.spec.get("paths", {}).items()): if "{" in path or not any(path.startswith(p) for p in self.ALLOWED_PATH_PREFIXES): continue + if self._is_excluded(path): + continue body_schema = self._get_request_body_schema(path_item) or {} query_props = self._get_query_parameters(path_item) body_props = body_schema.get("properties") or {} # Body properties win on name collision — body is the canonical param source # for the existing tools; query params are additive. properties = {**query_props, **body_props} - if not properties: - continue clean_props = self._filter_properties(properties) - if not clean_props: - continue enum_name = _deduplicate(_path_to_enum_name(path), used_enum) class_name = _deduplicate(_path_to_class_name(path), used_class) diff --git a/engine/src/stirling/agents/orchestrator.py b/engine/src/stirling/agents/orchestrator.py index c73d9dab32..e07e771e34 100644 --- a/engine/src/stirling/agents/orchestrator.py +++ b/engine/src/stirling/agents/orchestrator.py @@ -15,7 +15,6 @@ from stirling.agents.pdf_review import PdfReviewAgent from stirling.agents.user_spec import UserSpecAgent from stirling.contracts import ( AgentDraftWorkflowResponse, - ConvertMarkdownResponse, ExtractedTextArtifact, OrchestratorRequest, OrchestratorResponse, @@ -48,7 +47,7 @@ class OrchestratorAgent: ToolOutput( self.delegate_pdf_edit, name="delegate_pdf_edit", - description="Delegate requests for PDF modifications and return the PDF edit result.", + description="Delegate requests to modify or convert PDFs and return the PDF edit result.", ), ToolOutput( self.delegate_pdf_question, @@ -71,13 +70,6 @@ class OrchestratorAgent: " feedback')." ), ), - ToolOutput( - self.delegate_pdf_ingest, - name="delegate_pdf_ingest", - description=( - "Delegate requests to convert a PDF to Markdown or extract its content as readable text." - ), - ), ToolOutput( self.delegate_pdf_create, name="delegate_pdf_create", @@ -98,7 +90,7 @@ class OrchestratorAgent: system_prompt=( "You are the top-level orchestrator. " "Choose exactly one output function that best handles the request. " - "Use delegate_pdf_edit for any requested modification of one or more PDFs. " + "Use delegate_pdf_edit for any request to modify or convert one or more PDFs. " "Use delegate_pdf_question for questions about the contents of the attached PDFs. " "Use delegate_user_spec for requests to create or define an agent spec. " "Use delegate_pdf_review when the user wants the PDF returned with review" @@ -106,8 +98,6 @@ class OrchestratorAgent: " 'leave feedback on the PDF'. " "Use delegate_pdf_create when the user wants to generate a new document from" " scratch with no input file — invoices, reports, letters, contracts, etc. " - "Use delegate_pdf_ingest for any request to convert a PDF to Markdown " - "or extract its content as readable text. " "Use unsupported_capability when the user asks about the assistant itself " "or when none of the other outputs fit; supply a helpful message." ), @@ -177,13 +167,6 @@ class OrchestratorAgent: async def _run_agent_draft(self, request: OrchestratorRequest) -> AgentDraftWorkflowResponse: return await UserSpecAgent(self.runtime).orchestrate(request) - async def delegate_pdf_ingest(self, ctx: RunContext[OrchestratorDeps]) -> ConvertMarkdownResponse: - request = ctx.deps.request - return ConvertMarkdownResponse( - reason="PDF to Markdown requested — Java converts deterministically.", - files_to_ingest=request.files, - ) - async def delegate_pdf_review(self, ctx: RunContext[OrchestratorDeps]) -> PdfReviewOrchestrateResponse: return await self._run_pdf_review(ctx.deps.request) diff --git a/engine/src/stirling/contracts/__init__.py b/engine/src/stirling/contracts/__init__.py index 6c8d99d120..77ce40301e 100644 --- a/engine/src/stirling/contracts/__init__.py +++ b/engine/src/stirling/contracts/__init__.py @@ -13,7 +13,6 @@ from .common import ( AiFile, ArtifactKind, ConversationMessage, - ConvertMarkdownResponse, ExtractedFileText, GenerateFileResponse, MathAuditorToolReportArtifact, @@ -163,7 +162,6 @@ __all__ = [ "NeedContentFileRequest", "NeedContentResponse", "NeedIngestResponse", - "ConvertMarkdownResponse", "NextExecutionAction", "OrchestratorRequest", "OrchestratorResponse", diff --git a/engine/src/stirling/contracts/common.py b/engine/src/stirling/contracts/common.py index 8f35c9ceb4..d9105bad4e 100644 --- a/engine/src/stirling/contracts/common.py +++ b/engine/src/stirling/contracts/common.py @@ -62,7 +62,6 @@ class WorkflowOutcome(StrEnum): CANNOT_CONTINUE = "cannot_continue" UNSUPPORTED_CAPABILITY = "unsupported_capability" GENERATE_FILE = "generate_file" - CONVERT_MARKDOWN = "convert_markdown" class ArtifactKind(StrEnum): @@ -184,19 +183,6 @@ class NeedIngestResponse(ApiModel): content_types: list[PdfContentType] = Field(default_factory=list) -class ConvertMarkdownResponse(ApiModel): - """Terminal signal: convert the listed files to Markdown deterministically. - - This is a deterministic, non-AI conversion. Java runs the PDF→Markdown converter - (``PdfMarkdownConverter``) on each file and returns the resulting ``.md`` file(s) as a - completed result. There is no resume turn — the conversion output is the final answer. - """ - - outcome: Literal[WorkflowOutcome.CONVERT_MARKDOWN] = WorkflowOutcome.CONVERT_MARKDOWN - reason: str - files_to_ingest: list[AiFile] - - class ToolOperationStep(ApiModel): kind: Literal[StepKind.TOOL] = StepKind.TOOL tool: AnyToolId diff --git a/engine/src/stirling/contracts/orchestrator.py b/engine/src/stirling/contracts/orchestrator.py index 8b916ccaff..2d85853c51 100644 --- a/engine/src/stirling/contracts/orchestrator.py +++ b/engine/src/stirling/contracts/orchestrator.py @@ -11,7 +11,6 @@ from .common import ( AiFile, ArtifactKind, ConversationMessage, - ConvertMarkdownResponse, ExtractedFileText, GenerateFileResponse, NeedContentResponse, @@ -61,7 +60,6 @@ type OrchestratorResponse = Annotated[ | GenerateFileResponse | NeedContentResponse | NeedIngestResponse - | ConvertMarkdownResponse | AgentDraftResponse | NextExecutionAction | UnsupportedCapabilityResponse, diff --git a/engine/src/stirling/models/tool_models.py b/engine/src/stirling/models/tool_models.py index 2591d947a8..1827730283 100644 --- a/engine/src/stirling/models/tool_models.py +++ b/engine/src/stirling/models/tool_models.py @@ -11,13 +11,6 @@ from pydantic import Field, RootModel, SecretStr from stirling.models.base import ApiModel -class AddAttachmentsParams(ApiModel): - attachments: list[bytes] = Field(..., description="The image file to be overlaid onto the PDF.") - convert_to_pdf_a3b: bool = Field( - False, description="Convert the resulting PDF to PDF/A-3b format after adding attachments" - ) - - class AddCommentsParams(ApiModel): comments: str = Field( ..., @@ -28,12 +21,6 @@ class AddCommentsParams(ApiModel): ) -class AddImageParams(ApiModel): - every_page: bool = Field(False, description="Whether to overlay the image onto every page of the PDF.") - x: float = Field(0, description="The x-coordinate at which to place the top-left corner of the image.") - y: float = Field(0, description="The y-coordinate at which to place the top-left corner of the image.") - - class CustomMargin(StrEnum): """ Custom margin: small/medium/large/x-large @@ -312,50 +299,6 @@ class CbzToPdfParams(ApiModel): optimize_for_ebook: bool = Field(False, description="Optimize the output PDF for ebook reading using Ghostscript") -class CertType(StrEnum): - """ - The type of the digital certificate. WINDOWS_STORE and PKCS11 are hardware-backed and only available in the desktop app. - """ - - pem = "PEM" - pkcs12 = "PKCS12" - pfx = "PFX" - jks = "JKS" - server = "SERVER" - windows_store = "WINDOWS_STORE" - pkcs11 = "PKCS11" - - -class CertSignParams(ApiModel): - alias: str | None = Field( - None, - description="The alias of the certificate to sign with. Required for WINDOWS_STORE and recommended for PKCS11 tokens holding multiple certificates.", - ) - cert_type: CertType = Field( - ..., - description="The type of the digital certificate. WINDOWS_STORE and PKCS11 are hardware-backed and only available in the desktop app.", - ) - location: str = Field("SPDF", description="The location where the PDF is signed") - name: str = Field("SPDF", description="The name of the signer") - page_number: int = Field( - 1, - description="The page number where the signature should be visible. This is required if showSignature is set to true", - ) - password: SecretStr | None = Field( - None, description="The password for the keystore / private key, or the token PIN for PKCS11" - ) - pkcs11_library_path: str | None = Field( - None, - description="Absolute path to the PKCS#11 driver library (required for PKCS11 type). Must be an allowed driver - a detected one or configured via STIRLING_PKCS11_LIBRARIES.", - ) - pkcs11_slot: int | None = Field( - None, description="Optional PKCS#11 slot index. When omitted the first slot with a token is used." - ) - reason: str = Field("Signed by SPDF", description="The reason for signing the PDF") - show_logo: bool = Field(True, description="Whether to visually show a signature logo along with the signature") - show_signature: bool = Field(False, description="Whether to visually show the signature in the PDF file") - - class LineArtEdgeLevel(IntEnum): """ Edge detection strength to use for line art conversion (1-3). This maps to ImageMagick's -edge radius. @@ -525,6 +468,10 @@ class EmlToPdfParams(ApiModel): ) +class ExtractAttachmentsParams(ApiModel): + pass + + class ExtractImageScansParams(ApiModel): angle_threshold: int = Field(5, description="The angle threshold for the image scan extraction") border_size: int = Field(1, description="The border size for the image scan extraction") @@ -547,6 +494,10 @@ class ExtractImagesParams(ApiModel): format: Format = Field(Format.png, description="The output image format e.g., 'png', 'jpeg', or 'gif'") +class FileToPdfParams(ApiModel): + pass + + class FlattenParams(ApiModel): flatten_only_forms: bool = Field( False, description="True to flatten only the forms, false to flatten full PDF (Convert page to image)" @@ -602,6 +553,10 @@ class ImgToPdfParams(ApiModel): ) +class MarkdownToPdfParams(ApiModel): + pass + + class SortType(StrEnum): """ The type of sorting to be applied on the input files before merging. @@ -750,41 +705,6 @@ class OcrPdfParams(ApiModel): sidecar: bool | None = Field(None, description="Include OCR text in a sidecar text file if set to true") -class OverlayMode(StrEnum): - """ - The mode of overlaying: 'SequentialOverlay' for sequential application, 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay' for fixed repetition based on provided counts - """ - - sequential_overlay = "SequentialOverlay" - interleaved_overlay = "InterleavedOverlay" - fixed_repeat_overlay = "FixedRepeatOverlay" - - -class OverlayPosition(Enum): - """ - Overlay position 0 is Foregound, 1 is Background - """ - - number_0 = 0 - number_1 = 1 - - -class OverlayPdfsParams(ApiModel): - counts: list[int] | None = Field( - None, - description="An array of integers specifying the number of times each corresponding overlay file should be applied in the 'FixedRepeatOverlay' mode. This should match the length of the overlayFiles array.", - ) - overlay_files: list[bytes] = Field( - ..., - description="An array of PDF files to be used as overlays on the base PDF. The order in these files is applied based on the selected mode.", - ) - overlay_mode: OverlayMode = Field( - ..., - description="The mode of overlaying: 'SequentialOverlay' for sequential application, 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay' for fixed repetition based on provided counts", - ) - overlay_position: OverlayPosition = Field(..., description="Overlay position 0 is Foregound, 1 is Background") - - class PdfToCbrParams(ApiModel): dpi: int = Field(..., description="The DPI (Dots Per Inch) for rendering PDF pages as images", examples=[150]) @@ -841,6 +761,12 @@ class PdfToEpubParams(ApiModel): ) +class PdfToHtmlParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ + + class ImageFormat(StrEnum): """ The output image format @@ -877,6 +803,12 @@ class PdfToImgParams(ApiModel): ) +class PdfToMarkdownParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ + + class OutputFormat1(StrEnum): """ The output format type (PDF/A or PDF/X) @@ -912,13 +844,11 @@ class PdfToPresentationParams(ApiModel): output_format: OutputFormat2 = Field(..., description="The output Presentation format") -class PdfToTextEditorParams(ApiModel): +class PdfToSinglePageParams(ApiModel): """ Either upload a file or provide a server-side file ID """ - lightweight: bool = False - class OutputFormat3(StrEnum): """ @@ -979,10 +909,10 @@ class PdfToXlsxParams(ApiModel): ) -class Pkcs11CertificatesParams(ApiModel): - library_path: str | None = None - pin: str | None = None - slot: int | None = None +class PdfToXmlParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ class CustomMode(StrEnum): @@ -1061,6 +991,18 @@ class RemoveBlanksParams(ApiModel): ) +class RemoveCertSignParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ + + +class RemoveImagePdfParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ + + class RemovePagesParams(ApiModel): page_numbers: str = Field( "all", @@ -1077,6 +1019,12 @@ class RenameAttachmentParams(ApiModel): new_name: str = Field(..., description="The new name for the attachment") +class RepairParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ + + class HighContrastColorCombination(StrEnum): """ If HIGH_CONTRAST_COLOR option selected, then pick the default color option for text and background. @@ -1237,27 +1185,6 @@ class ScannerEffectParams(ApiModel): yellowish: bool | None = Field(None, description="Simulate yellowed paper", examples=[False]) -class WorkflowType(StrEnum): - signing = "SIGNING" - review = "REVIEW" - approval = "APPROVAL" - - -class Request(ApiModel): - document_name: str | None = None - due_date: str | None = None - message: str | None = None - owner_email: str | None = None - participant_emails: list[str] | None = None - participant_user_ids: list[int] | None = None - workflow_metadata: str | None = None - workflow_type: WorkflowType | None = None - - -class SessionsParams(ApiModel): - request: Request | None = None - - class SplitBySizeOrCountParams(ApiModel): split_type: int = Field( 0, description="Determines the type of split: 0 for size, 1 for page count, 2 for document count" @@ -1360,6 +1287,12 @@ class TimestampPdfParams(ApiModel): ) +class UnlockPdfFormsParams(ApiModel): + """ + Either upload a file or provide a server-side file ID + """ + + class Trapped(StrEnum): """ The trapped status of the document @@ -1399,11 +1332,6 @@ class UrlToPdfParams(ApiModel): url_input: str = Field(..., description="The input URL to be converted to a PDF file") -class ValidateCertificateParams(ApiModel): - cert_type: str | None = None - password: str | None = None - - class OutputFormat6(StrEnum): """ Target vector format extension @@ -1462,20 +1390,24 @@ class Model( | CbzToPdfParams | EbookToPdfParams | EmlToPdfParams + | FileToPdfParams | HtmlToPdfParams | ImgToPdfParams + | MarkdownToPdfParams | PdfToCbrParams | PdfToCbzParams | PdfToCsvParams | PdfToEpubParams + | PdfToHtmlParams | PdfToImgParams + | PdfToMarkdownParams | PdfToPdfaParams | PdfToPresentationParams | PdfToTextParams - | PdfToTextEditorParams | PdfToVectorParams | PdfToWordParams | PdfToXlsxParams + | PdfToXmlParams | SvgToPdfParams | UrlToPdfParams | VectorToPdfParams @@ -1485,8 +1417,9 @@ class Model( | EditTextParams | MergePdfsParams | MultiPageLayoutParams - | OverlayPdfsParams + | PdfToSinglePageParams | RearrangePagesParams + | RemoveImagePdfParams | RemovePagesParams | RotatePdfParams | ScalePagesParams @@ -1495,33 +1428,31 @@ class Model( | SplitPagesParams | SplitPdfByChaptersParams | SplitPdfBySectionsParams - | AddAttachmentsParams | AddCommentsParams - | AddImageParams | AddPageNumbersParams | AddStampParams | AutoRenameParams | AutoSplitPdfParams | CompressPdfParams | DeleteAttachmentParams + | ExtractAttachmentsParams | ExtractImageScansParams | ExtractImagesParams | FlattenParams | OcrPdfParams | RemoveBlanksParams | RenameAttachmentParams + | RepairParams | ReplaceInvertPdfParams | ScannerEffectParams + | UnlockPdfFormsParams | UpdateMetadataParams | AddPasswordParams | AddWatermarkParams | AutoRedactParams - | CertSignParams - | Pkcs11CertificatesParams - | SessionsParams - | ValidateCertificateParams | RedactParams | RedactExecuteParams + | RemoveCertSignParams | RemovePasswordParams | SanitizePdfParams | TimestampPdfParams @@ -1532,20 +1463,24 @@ class Model( | CbzToPdfParams | EbookToPdfParams | EmlToPdfParams + | FileToPdfParams | HtmlToPdfParams | ImgToPdfParams + | MarkdownToPdfParams | PdfToCbrParams | PdfToCbzParams | PdfToCsvParams | PdfToEpubParams + | PdfToHtmlParams | PdfToImgParams + | PdfToMarkdownParams | PdfToPdfaParams | PdfToPresentationParams | PdfToTextParams - | PdfToTextEditorParams | PdfToVectorParams | PdfToWordParams | PdfToXlsxParams + | PdfToXmlParams | SvgToPdfParams | UrlToPdfParams | VectorToPdfParams @@ -1555,8 +1490,9 @@ class Model( | EditTextParams | MergePdfsParams | MultiPageLayoutParams - | OverlayPdfsParams + | PdfToSinglePageParams | RearrangePagesParams + | RemoveImagePdfParams | RemovePagesParams | RotatePdfParams | ScalePagesParams @@ -1565,33 +1501,31 @@ class Model( | SplitPagesParams | SplitPdfByChaptersParams | SplitPdfBySectionsParams - | AddAttachmentsParams | AddCommentsParams - | AddImageParams | AddPageNumbersParams | AddStampParams | AutoRenameParams | AutoSplitPdfParams | CompressPdfParams | DeleteAttachmentParams + | ExtractAttachmentsParams | ExtractImageScansParams | ExtractImagesParams | FlattenParams | OcrPdfParams | RemoveBlanksParams | RenameAttachmentParams + | RepairParams | ReplaceInvertPdfParams | ScannerEffectParams + | UnlockPdfFormsParams | UpdateMetadataParams | AddPasswordParams | AddWatermarkParams | AutoRedactParams - | CertSignParams - | Pkcs11CertificatesParams - | SessionsParams - | ValidateCertificateParams | RedactParams | RedactExecuteParams + | RemoveCertSignParams | RemovePasswordParams | SanitizePdfParams | TimestampPdfParams @@ -1603,20 +1537,24 @@ type ParamToolModel = ( | CbzToPdfParams | EbookToPdfParams | EmlToPdfParams + | FileToPdfParams | HtmlToPdfParams | ImgToPdfParams + | MarkdownToPdfParams | PdfToCbrParams | PdfToCbzParams | PdfToCsvParams | PdfToEpubParams + | PdfToHtmlParams | PdfToImgParams + | PdfToMarkdownParams | PdfToPdfaParams | PdfToPresentationParams | PdfToTextParams - | PdfToTextEditorParams | PdfToVectorParams | PdfToWordParams | PdfToXlsxParams + | PdfToXmlParams | SvgToPdfParams | UrlToPdfParams | VectorToPdfParams @@ -1626,8 +1564,9 @@ type ParamToolModel = ( | EditTextParams | MergePdfsParams | MultiPageLayoutParams - | OverlayPdfsParams + | PdfToSinglePageParams | RearrangePagesParams + | RemoveImagePdfParams | RemovePagesParams | RotatePdfParams | ScalePagesParams @@ -1636,33 +1575,31 @@ type ParamToolModel = ( | SplitPagesParams | SplitPdfByChaptersParams | SplitPdfBySectionsParams - | AddAttachmentsParams | AddCommentsParams - | AddImageParams | AddPageNumbersParams | AddStampParams | AutoRenameParams | AutoSplitPdfParams | CompressPdfParams | DeleteAttachmentParams + | ExtractAttachmentsParams | ExtractImageScansParams | ExtractImagesParams | FlattenParams | OcrPdfParams | RemoveBlanksParams | RenameAttachmentParams + | RepairParams | ReplaceInvertPdfParams | ScannerEffectParams + | UnlockPdfFormsParams | UpdateMetadataParams | AddPasswordParams | AddWatermarkParams | AutoRedactParams - | CertSignParams - | Pkcs11CertificatesParams - | SessionsParams - | ValidateCertificateParams | RedactParams | RedactExecuteParams + | RemoveCertSignParams | RemovePasswordParams | SanitizePdfParams | TimestampPdfParams @@ -1675,20 +1612,24 @@ class ToolEndpoint(StrEnum): CBZ_TO_PDF = "/api/v1/convert/cbz/pdf" EBOOK_TO_PDF = "/api/v1/convert/ebook/pdf" EML_TO_PDF = "/api/v1/convert/eml/pdf" + FILE_TO_PDF = "/api/v1/convert/file/pdf" HTML_TO_PDF = "/api/v1/convert/html/pdf" IMG_TO_PDF = "/api/v1/convert/img/pdf" + MARKDOWN_TO_PDF = "/api/v1/convert/markdown/pdf" PDF_TO_CBR = "/api/v1/convert/pdf/cbr" PDF_TO_CBZ = "/api/v1/convert/pdf/cbz" PDF_TO_CSV = "/api/v1/convert/pdf/csv" PDF_TO_EPUB = "/api/v1/convert/pdf/epub" + PDF_TO_HTML = "/api/v1/convert/pdf/html" PDF_TO_IMG = "/api/v1/convert/pdf/img" + PDF_TO_MARKDOWN = "/api/v1/convert/pdf/markdown" PDF_TO_PDFA = "/api/v1/convert/pdf/pdfa" PDF_TO_PRESENTATION = "/api/v1/convert/pdf/presentation" PDF_TO_TEXT = "/api/v1/convert/pdf/text" - PDF_TO_TEXT_EDITOR = "/api/v1/convert/pdf/text-editor" PDF_TO_VECTOR = "/api/v1/convert/pdf/vector" PDF_TO_WORD = "/api/v1/convert/pdf/word" PDF_TO_XLSX = "/api/v1/convert/pdf/xlsx" + PDF_TO_XML = "/api/v1/convert/pdf/xml" SVG_TO_PDF = "/api/v1/convert/svg/pdf" URL_TO_PDF = "/api/v1/convert/url/pdf" VECTOR_TO_PDF = "/api/v1/convert/vector/pdf" @@ -1698,8 +1639,9 @@ class ToolEndpoint(StrEnum): EDIT_TEXT = "/api/v1/general/edit-text" MERGE_PDFS = "/api/v1/general/merge-pdfs" MULTI_PAGE_LAYOUT = "/api/v1/general/multi-page-layout" - OVERLAY_PDFS = "/api/v1/general/overlay-pdfs" + PDF_TO_SINGLE_PAGE = "/api/v1/general/pdf-to-single-page" REARRANGE_PAGES = "/api/v1/general/rearrange-pages" + REMOVE_IMAGE_PDF = "/api/v1/general/remove-image-pdf" REMOVE_PAGES = "/api/v1/general/remove-pages" ROTATE_PDF = "/api/v1/general/rotate-pdf" SCALE_PAGES = "/api/v1/general/scale-pages" @@ -1708,33 +1650,31 @@ class ToolEndpoint(StrEnum): SPLIT_PAGES = "/api/v1/general/split-pages" SPLIT_PDF_BY_CHAPTERS = "/api/v1/general/split-pdf-by-chapters" SPLIT_PDF_BY_SECTIONS = "/api/v1/general/split-pdf-by-sections" - ADD_ATTACHMENTS = "/api/v1/misc/add-attachments" ADD_COMMENTS = "/api/v1/misc/add-comments" - ADD_IMAGE = "/api/v1/misc/add-image" ADD_PAGE_NUMBERS = "/api/v1/misc/add-page-numbers" ADD_STAMP = "/api/v1/misc/add-stamp" AUTO_RENAME = "/api/v1/misc/auto-rename" AUTO_SPLIT_PDF = "/api/v1/misc/auto-split-pdf" COMPRESS_PDF = "/api/v1/misc/compress-pdf" DELETE_ATTACHMENT = "/api/v1/misc/delete-attachment" + EXTRACT_ATTACHMENTS = "/api/v1/misc/extract-attachments" EXTRACT_IMAGE_SCANS = "/api/v1/misc/extract-image-scans" EXTRACT_IMAGES = "/api/v1/misc/extract-images" FLATTEN = "/api/v1/misc/flatten" OCR_PDF = "/api/v1/misc/ocr-pdf" REMOVE_BLANKS = "/api/v1/misc/remove-blanks" RENAME_ATTACHMENT = "/api/v1/misc/rename-attachment" + REPAIR = "/api/v1/misc/repair" REPLACE_INVERT_PDF = "/api/v1/misc/replace-invert-pdf" SCANNER_EFFECT = "/api/v1/misc/scanner-effect" + UNLOCK_PDF_FORMS = "/api/v1/misc/unlock-pdf-forms" UPDATE_METADATA = "/api/v1/misc/update-metadata" ADD_PASSWORD = "/api/v1/security/add-password" ADD_WATERMARK = "/api/v1/security/add-watermark" AUTO_REDACT = "/api/v1/security/auto-redact" - CERT_SIGN = "/api/v1/security/cert-sign" - PKCS11_CERTIFICATES = "/api/v1/security/cert-sign/hardware/pkcs11-certificates" - SESSIONS = "/api/v1/security/cert-sign/sessions" - VALIDATE_CERTIFICATE = "/api/v1/security/cert-sign/validate-certificate" REDACT = "/api/v1/security/redact" REDACT_EXECUTE = "/api/v1/security/redact-execute" + REMOVE_CERT_SIGN = "/api/v1/security/remove-cert-sign" REMOVE_PASSWORD = "/api/v1/security/remove-password" SANITIZE_PDF = "/api/v1/security/sanitize-pdf" TIMESTAMP_PDF = "/api/v1/security/timestamp-pdf" @@ -1745,20 +1685,24 @@ OPERATIONS: dict[ToolEndpoint, ParamToolModelType] = { ToolEndpoint.CBZ_TO_PDF: CbzToPdfParams, ToolEndpoint.EBOOK_TO_PDF: EbookToPdfParams, ToolEndpoint.EML_TO_PDF: EmlToPdfParams, + ToolEndpoint.FILE_TO_PDF: FileToPdfParams, ToolEndpoint.HTML_TO_PDF: HtmlToPdfParams, ToolEndpoint.IMG_TO_PDF: ImgToPdfParams, + ToolEndpoint.MARKDOWN_TO_PDF: MarkdownToPdfParams, ToolEndpoint.PDF_TO_CBR: PdfToCbrParams, ToolEndpoint.PDF_TO_CBZ: PdfToCbzParams, ToolEndpoint.PDF_TO_CSV: PdfToCsvParams, ToolEndpoint.PDF_TO_EPUB: PdfToEpubParams, + ToolEndpoint.PDF_TO_HTML: PdfToHtmlParams, ToolEndpoint.PDF_TO_IMG: PdfToImgParams, + ToolEndpoint.PDF_TO_MARKDOWN: PdfToMarkdownParams, ToolEndpoint.PDF_TO_PDFA: PdfToPdfaParams, ToolEndpoint.PDF_TO_PRESENTATION: PdfToPresentationParams, ToolEndpoint.PDF_TO_TEXT: PdfToTextParams, - ToolEndpoint.PDF_TO_TEXT_EDITOR: PdfToTextEditorParams, ToolEndpoint.PDF_TO_VECTOR: PdfToVectorParams, ToolEndpoint.PDF_TO_WORD: PdfToWordParams, ToolEndpoint.PDF_TO_XLSX: PdfToXlsxParams, + ToolEndpoint.PDF_TO_XML: PdfToXmlParams, ToolEndpoint.SVG_TO_PDF: SvgToPdfParams, ToolEndpoint.URL_TO_PDF: UrlToPdfParams, ToolEndpoint.VECTOR_TO_PDF: VectorToPdfParams, @@ -1768,8 +1712,9 @@ OPERATIONS: dict[ToolEndpoint, ParamToolModelType] = { ToolEndpoint.EDIT_TEXT: EditTextParams, ToolEndpoint.MERGE_PDFS: MergePdfsParams, ToolEndpoint.MULTI_PAGE_LAYOUT: MultiPageLayoutParams, - ToolEndpoint.OVERLAY_PDFS: OverlayPdfsParams, + ToolEndpoint.PDF_TO_SINGLE_PAGE: PdfToSinglePageParams, ToolEndpoint.REARRANGE_PAGES: RearrangePagesParams, + ToolEndpoint.REMOVE_IMAGE_PDF: RemoveImagePdfParams, ToolEndpoint.REMOVE_PAGES: RemovePagesParams, ToolEndpoint.ROTATE_PDF: RotatePdfParams, ToolEndpoint.SCALE_PAGES: ScalePagesParams, @@ -1778,33 +1723,31 @@ OPERATIONS: dict[ToolEndpoint, ParamToolModelType] = { ToolEndpoint.SPLIT_PAGES: SplitPagesParams, ToolEndpoint.SPLIT_PDF_BY_CHAPTERS: SplitPdfByChaptersParams, ToolEndpoint.SPLIT_PDF_BY_SECTIONS: SplitPdfBySectionsParams, - ToolEndpoint.ADD_ATTACHMENTS: AddAttachmentsParams, ToolEndpoint.ADD_COMMENTS: AddCommentsParams, - ToolEndpoint.ADD_IMAGE: AddImageParams, ToolEndpoint.ADD_PAGE_NUMBERS: AddPageNumbersParams, ToolEndpoint.ADD_STAMP: AddStampParams, ToolEndpoint.AUTO_RENAME: AutoRenameParams, ToolEndpoint.AUTO_SPLIT_PDF: AutoSplitPdfParams, ToolEndpoint.COMPRESS_PDF: CompressPdfParams, ToolEndpoint.DELETE_ATTACHMENT: DeleteAttachmentParams, + ToolEndpoint.EXTRACT_ATTACHMENTS: ExtractAttachmentsParams, ToolEndpoint.EXTRACT_IMAGE_SCANS: ExtractImageScansParams, ToolEndpoint.EXTRACT_IMAGES: ExtractImagesParams, ToolEndpoint.FLATTEN: FlattenParams, ToolEndpoint.OCR_PDF: OcrPdfParams, ToolEndpoint.REMOVE_BLANKS: RemoveBlanksParams, ToolEndpoint.RENAME_ATTACHMENT: RenameAttachmentParams, + ToolEndpoint.REPAIR: RepairParams, ToolEndpoint.REPLACE_INVERT_PDF: ReplaceInvertPdfParams, ToolEndpoint.SCANNER_EFFECT: ScannerEffectParams, + ToolEndpoint.UNLOCK_PDF_FORMS: UnlockPdfFormsParams, ToolEndpoint.UPDATE_METADATA: UpdateMetadataParams, ToolEndpoint.ADD_PASSWORD: AddPasswordParams, ToolEndpoint.ADD_WATERMARK: AddWatermarkParams, ToolEndpoint.AUTO_REDACT: AutoRedactParams, - ToolEndpoint.CERT_SIGN: CertSignParams, - ToolEndpoint.PKCS11_CERTIFICATES: Pkcs11CertificatesParams, - ToolEndpoint.SESSIONS: SessionsParams, - ToolEndpoint.VALIDATE_CERTIFICATE: ValidateCertificateParams, ToolEndpoint.REDACT: RedactParams, ToolEndpoint.REDACT_EXECUTE: RedactExecuteParams, + ToolEndpoint.REMOVE_CERT_SIGN: RemoveCertSignParams, ToolEndpoint.REMOVE_PASSWORD: RemovePasswordParams, ToolEndpoint.SANITIZE_PDF: SanitizePdfParams, ToolEndpoint.TIMESTAMP_PDF: TimestampPdfParams,