|
5 | 5 | # the root directory of this source tree. |
6 | 6 |
|
7 | 7 | import os |
8 | | -import tempfile |
9 | 8 | import time |
10 | 9 | import uuid |
11 | | -from pathlib import Path |
| 10 | +from io import BytesIO |
12 | 11 | from typing import Any |
13 | 12 |
|
14 | 13 | import httpx |
15 | 14 | from docling.datamodel.base_models import OutputFormat |
16 | 15 | from docling.datamodel.service.options import ConvertDocumentsOptions |
17 | 16 | from docling.service_client import AsyncDoclingServiceClient, ChunkerKind |
| 17 | +from docling_core.types.io import DocumentStream |
18 | 18 | from fastapi import UploadFile |
19 | 19 |
|
20 | 20 | from ogx.log import get_logger |
@@ -222,37 +222,33 @@ async def _convert_no_chunk_async( |
222 | 222 | document_metadata: dict[str, Any], |
223 | 223 | ) -> list[Chunk]: |
224 | 224 | """Convert file using async endpoints with AsyncDoclingServiceClient.""" |
225 | | - # AsyncDoclingServiceClient requires a file path via temp file |
226 | | - with tempfile.NamedTemporaryFile() as tmp: |
227 | | - tmp.write(content) |
228 | | - tmp_path = Path(tmp.name) |
229 | | - |
230 | | - async with AsyncDoclingServiceClient( |
231 | | - url=self.config.base_url, |
232 | | - api_key=self.config.api_key.get_secret_value() if self.config.api_key else "", |
233 | | - job_timeout=300.0, |
234 | | - ) as client: |
235 | | - job = await client.submit( |
236 | | - source=tmp_path, |
237 | | - options=ConvertDocumentsOptions(to_formats=[OutputFormat.MARKDOWN]), |
238 | | - ) |
239 | | - result = await job.result() |
240 | | - |
241 | | - # Handle both local docling-serve (ConversionResult with .document) |
242 | | - # and IBM SaaS (PresignedUrlConvertResponse with .documents and presigned URLs) |
243 | | - md_content = "" |
244 | | - if hasattr(result, "documents"): |
245 | | - # IBM SaaS: PresignedUrlConvertResponse with presigned URLs |
246 | | - if result.documents and result.documents[0].artifacts: |
247 | | - artifact = result.documents[0].artifacts[0] |
248 | | - # Download markdown from presigned URL |
249 | | - async with httpx.AsyncClient() as http_client: |
250 | | - response = await http_client.get(str(artifact.uri)) |
251 | | - response.raise_for_status() |
252 | | - md_content = response.text |
253 | | - elif hasattr(result, "document"): |
254 | | - # Local docling-serve: ConversionResult with direct document |
255 | | - md_content = result.document.export_to_markdown() if result.document else "" |
| 225 | + source = DocumentStream(name=filename, stream=BytesIO(content)) |
| 226 | + async with AsyncDoclingServiceClient( |
| 227 | + url=self.config.base_url, |
| 228 | + api_key=self.config.api_key.get_secret_value() if self.config.api_key else "", |
| 229 | + job_timeout=300.0, |
| 230 | + ) as client: |
| 231 | + job = await client.submit( |
| 232 | + source=source, |
| 233 | + options=ConvertDocumentsOptions(to_formats=[OutputFormat.MARKDOWN]), |
| 234 | + ) |
| 235 | + result = await job.result() |
| 236 | + |
| 237 | + # Handle both local docling-serve (ConversionResult with .document) |
| 238 | + # and IBM SaaS (PresignedUrlConvertResponse with .documents and presigned URLs) |
| 239 | + md_content = "" |
| 240 | + if hasattr(result, "documents"): |
| 241 | + # IBM SaaS: PresignedUrlConvertResponse with presigned URLs |
| 242 | + if result.documents and result.documents[0].artifacts: |
| 243 | + artifact = result.documents[0].artifacts[0] |
| 244 | + # Download markdown from presigned URL |
| 245 | + async with httpx.AsyncClient() as http_client: |
| 246 | + response = await http_client.get(str(artifact.uri)) |
| 247 | + response.raise_for_status() |
| 248 | + md_content = response.text |
| 249 | + elif hasattr(result, "document"): |
| 250 | + # Local docling-serve: ConversionResult with direct document |
| 251 | + md_content = result.document.export_to_markdown() if result.document else "" |
256 | 252 |
|
257 | 253 | if not md_content or not md_content.strip(): |
258 | 254 | return [] |
@@ -374,39 +370,35 @@ async def _convert_and_chunk_async( |
374 | 370 | document_metadata: dict[str, Any], |
375 | 371 | ) -> list[Chunk]: |
376 | 372 | """Convert and chunk file using async endpoints with AsyncDoclingServiceClient.""" |
377 | | - # AsyncDoclingServiceClient requires a file path via temp file |
378 | | - with tempfile.NamedTemporaryFile() as tmp: |
379 | | - tmp.write(content) |
380 | | - tmp_path = Path(tmp.name) |
381 | | - |
382 | | - async with AsyncDoclingServiceClient( |
383 | | - url=self.config.base_url, |
384 | | - api_key=self.config.api_key.get_secret_value() if self.config.api_key else "", |
385 | | - job_timeout=300.0, |
386 | | - ) as client: |
387 | | - try: |
388 | | - job = await client.submit_chunk( |
389 | | - source=tmp_path, |
390 | | - chunker=ChunkerKind.HYBRID, |
391 | | - options=ConvertDocumentsOptions(), |
392 | | - ) |
393 | | - response = await job.result() |
394 | | - except httpx.HTTPStatusError as e: |
395 | | - # Chunking endpoint not supported (e.g., IBM Docling SaaS) |
396 | | - if e.response.status_code in (404, 405): |
397 | | - raise InvalidParameterError( |
398 | | - param_name="chunking_strategy", |
399 | | - value=chunking_strategy.model_dump() if chunking_strategy else None, |
400 | | - constraint=( |
401 | | - "Chunking is not supported by this Docling instance. " |
402 | | - "This is a known limitation of IBM Docling SaaS. " |
403 | | - "Either remove 'chunking_strategy' from your request, " |
404 | | - "or configure OGX to use local docling-serve for chunking support." |
405 | | - ), |
406 | | - ) from e |
407 | | - raise |
| 373 | + source = DocumentStream(name=filename, stream=BytesIO(content)) |
| 374 | + async with AsyncDoclingServiceClient( |
| 375 | + url=self.config.base_url, |
| 376 | + api_key=self.config.api_key.get_secret_value() if self.config.api_key else "", |
| 377 | + job_timeout=300.0, |
| 378 | + ) as client: |
| 379 | + try: |
| 380 | + job = await client.submit_chunk( |
| 381 | + source=source, |
| 382 | + chunker=ChunkerKind.HYBRID, |
| 383 | + options=ConvertDocumentsOptions(), |
| 384 | + ) |
| 385 | + response = await job.result() |
| 386 | + except httpx.HTTPStatusError as e: |
| 387 | + # Chunking endpoint not supported (e.g., IBM Docling SaaS) |
| 388 | + if e.response.status_code in (404, 405): |
| 389 | + raise InvalidParameterError( |
| 390 | + param_name="chunking_strategy", |
| 391 | + value=chunking_strategy.model_dump() if chunking_strategy else None, |
| 392 | + constraint=( |
| 393 | + "Chunking is not supported by this Docling instance. " |
| 394 | + "This is a known limitation of IBM Docling SaaS. " |
| 395 | + "Either remove 'chunking_strategy' from your request, " |
| 396 | + "or configure OGX to use local docling-serve for chunking support." |
| 397 | + ), |
| 398 | + ) from e |
| 399 | + raise |
408 | 400 |
|
409 | | - raw_chunks = response.chunks if response.chunks else [] |
| 401 | + raw_chunks = response.chunks if response.chunks else [] |
410 | 402 |
|
411 | 403 | if not raw_chunks: |
412 | 404 | return [] |
|
0 commit comments