Skip to content

feat: Implement two-phase ingestion flow with Docling polling#1572

Merged
ricofurtado merged 6 commits into
mainfrom
decouple-docling-processing-from-langflow
May 14, 2026
Merged

feat: Implement two-phase ingestion flow with Docling polling#1572
ricofurtado merged 6 commits into
mainfrom
decouple-docling-processing-from-langflow

Conversation

@ricofurtado

@ricofurtado ricofurtado commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR updates the ingestion flow so Langflow is triggered only after Docling processing has completed successfully.

The goal is to avoid using Langflow as a long-running coordinator while Docling is still processing documents. Instead, OpenRAG should submit the document to Docling, track the returned task_id, wait for Docling completion, and only then invoke the Langflow ingestion flow.

This improves ingestion reliability and reduces unnecessary Langflow resource usage, especially for large documents, OCR-heavy files, table-heavy PDFs, or cases where Docling processing fails before ingestion can begin.

Problem

Today, Langflow may be started before Docling processing has completed. When this happens, Langflow can remain active while waiting or polling for Docling results.

This creates a few issues:

  • Langflow execution resources can be occupied while Docling is still processing.
  • Large or slow documents can keep Langflow flows running for a long time.
  • Failed Docling tasks may still consume Langflow resources before eventually failing.
  • Ingestion concurrency can be reduced when Langflow is already a bottleneck.
  • Failures such as Docling result 404 Not Found are harder to separate from Langflow ingestion failures.

Since Docling processing and Langflow ingestion are separate phases, Langflow should only run once the document is ready to be ingested.

Proposed Solution

Introduce a Docling-first ingestion flow:

  1. OpenRAG receives the ingestion request.
  2. OpenRAG submits the file to Docling Serve.
  3. Docling Serve returns a task_id.
  4. OpenRAG stores and tracks the task_id.
  5. OpenRAG polls or monitors Docling task completion outside Langflow.
  6. If Docling succeeds, OpenRAG triggers the Langflow ingestion flow.
  7. If Docling fails, times out, expires, or returns 404, OpenRAG marks the ingestion as failed and does not trigger Langflow.

The main design principle is:

Langflow should not be used as the long-running coordinator for Docling processing.

Changes

This PR may include changes in the following areas:

  • Add or update ingestion task state to track Docling lifecycle status.
  • Store the Docling task_id returned by Docling Serve.
  • Add backend logic to poll or check Docling completion outside Langflow.
  • Trigger Langflow only after Docling processing succeeds.
  • Mark ingestion as failed when Docling fails, times out, or returns a missing result.
  • Improve error handling and status reporting for Docling-specific failures.
  • Add configuration for async Docling ingestion behavior.
  • Add logs and metrics for Docling submission, polling, completion, and Langflow trigger events.

Expected Benefits

  • Reduces long-running Langflow executions.
  • Reduces Langflow CPU and memory pressure.
  • Improves ingestion concurrency.
  • Avoids triggering Langflow for failed Docling tasks.
  • Makes failures easier to diagnose by separating Docling conversion failures from Langflow ingestion failures.
  • Improves reliability for large files, OCR-heavy documents, and table-heavy PDFs.
  • Creates a cleaner separation between document conversion and RAG ingestion.

Backward Compatibility

The existing ingestion behavior should remain available behind a feature flag or configuration option.

Suggested flag:

DOCLING_ASYNC_INGESTION_ENABLED=true


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Optional backend two-phase ingestion: submit to conversion service, poll until ready, then continue downstream; per-file phase/status and submission ID are tracked and exposed.
  * Environment-configurable polling controls (enable flag, intervals, backoff, retries, timeouts).

* **Bug Fixes**
  * File task error text now recorded in the primary error field for clearer diagnostics.

* **Tests**
  * Comprehensive unit tests for polling, status checks, and two-phase workflows.

* **Chores**
  * Backend wiring to conditionally construct and provide the polling coordinator.

<!-- review_stack_entry_start -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/langflow-ai/openrag/pull/1572)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

- Added configuration settings for Docling polling intervals and retries.
- Refactored LangflowFileProcessor to support two-phase ingestion with Docling.
- Introduced DoclingPollingService to handle backend polling for Docling task completion.
- Updated LangflowFileService to manage the new two-phase ingestion process.
- Enhanced FileTask model to track Docling task ID and status.
- Created unit tests for DoclingPollingService and LangflowFileService to ensure correct behavior during two-phase ingestion.
- Updated task service to include new fields for tracking ingestion phases and statuses.

Co-authored-by: Copilot <copilot@github.com>
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da579ad7-86f8-44c4-9078-179a4863888a

📥 Commits

Reviewing files that changed from the base of the PR and between 5fa13ba and 4916286.

📒 Files selected for processing (5)
  • src/config/settings.py
  • src/dependencies.py
  • src/models/processors.py
  • src/services/docling_service.py
  • src/services/langflow_file_service.py

Walkthrough

Adds a backend-coordinated two-phase ingestion: submit files to Docling Serve, optionally poll Docling to completion (backoff/timeout/transient handling), then run Langflow ingestion; includes config, per-file state, Docling APIs, a polling orchestrator, DI wiring, and unit tests.

Changes

Two-Phase Docling + Langflow Ingestion

Layer / File(s) Summary
Configuration & State Model
src/config/settings.py, src/models/tasks.py
Environment-configurable Docling polling settings; enums DoclingPhaseStatus, IngestionPhase; FileTask fields docling_task_id, docling_status, phase.
Docling Service Status & Result Methods
src/services/docling_service.py
Adds DoclingTaskState/DoclingStatusSnapshot; check_task_status() maps HTTP/network outcomes to typed snapshots; fetch_task_result() retrieves converted document or raises DoclingServeError.
Polling Service Orchestration
src/services/docling_polling_service.py
New DoclingPollingService.poll_until_ready() with input validation, transient NOT_FOUND budget, exponential backoff (capped by max_interval), max_seconds deadline, and PollOutcome results (SUCCESS/FAILED/EXPIRED/TIMEOUT).
Langflow Two-Phase Workflow
src/services/langflow_file_service.py
submit_to_docling() uploads and returns task_id; upload_and_ingest_file() accepts optional docling_polling_service and file_task, implements DOCLING→(optional poll)→LANGFLOW→COMPLETE phase updates, and skips Langflow on non-success poll outcomes.
Processor Integration & Error Handling
src/models/processors.py
LangflowFileProcessor gains optional docling_polling_service parameter, stores it, passes it into ingestion, and updates file_task during the two-phase flow.
Service Wiring & DI
src/app/container.py, src/dependencies.py
Conditionally constructs DoclingPollingService in container when enabled and Docling client exists; injects into TaskService; adds get_docling_polling_service FastAPI dependency.
Task Status Exposure
src/services/task_service.py
get_task_status() and get_all_tasks() now include per-file phase, docling_status, and docling_task_id in returned payloads.
Unit Tests
tests/unit/test_docling_polling_service.py, tests/unit/test_docling_service_status_check.py, tests/unit/test_langflow_file_service_two_phase.py
Adds tests covering polling behavior (success, failure, expiration, timeout, backoff), Docling status/result handling, and two-phase ingestion + processor wiring (success/failure paths and legacy compatibility).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant LangflowFileService
  participant DoclingPollingService
  participant DoclingService
  participant Langflow
  Client->>LangflowFileService: upload_and_ingest_file(filename, content)
  LangflowFileService->>DoclingService: submit file -> returns task_id
  LangflowFileService->>DoclingPollingService: poll_until_ready(task_id, config)
  DoclingPollingService->>DoclingService: check_task_status(task_id) [repeated]
  DoclingService-->>DoclingPollingService: status snapshot
  DoclingPollingService-->>LangflowFileService: DoclingPollResult (SUCCESS/FAILED/EXPIRED/TIMEOUT)
  alt SUCCESS
    LangflowFileService->>Langflow: run_ingestion_flow(docling_task_id)
    Langflow-->>LangflowFileService: ingestion result
  else non-success
    LangflowFileService-->>Client: raise / return error
  end
Loading

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

  • langflow-ai/openrag#1572: Implements the same Docling→Langflow two-phase ingestion, polling service, state fields, wiring, and tests.

Suggested reviewers:

  • mpawlow
  • lucaseduoli

Suggested labels:
test

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Implement two-phase ingestion flow with Docling polling' accurately and concisely summarizes the primary change—introducing a two-phase ingestion workflow where Docling is processed before Langflow. The title is specific, clear, and directly reflects the main objective across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch decouple-docling-processing-from-langflow

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) tests enhancement 🔵 New feature or request labels May 11, 2026
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/models/processors.py (1)

1-1: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace deprecated typing.Dict with built-in dict generic to clear the Ruff UP035 lint failure.

CI is blocking on UP035 for this file. Remove Dict from the import on line 1 and replace all three usages with built-in generics:

  • Line 490: Optional[Dict[str, Any]]Optional[dict[str, Any]]
  • Line 553: Dict[str, Any]dict[str, Any]
  • Line 616: Optional[Dict[str, Any]]Optional[dict[str, Any]]

Python 3.13 (your minimum version) fully supports built-in generics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/models/processors.py` at line 1, Remove the deprecated typing.Dict import
and switch to built-in generics: delete Dict from the import line (keep Any and
Optional) and replace the three type annotations using Dict with built-in dict
generics — change Optional[Dict[str, Any]] to Optional[dict[str, Any]] (both
occurrences) and change Dict[str, Any] to dict[str, Any]; update the import line
to "from typing import Any, Optional" so annotations compile and satisfy UP035.
🧹 Nitpick comments (2)
src/services/docling_polling_service.py (1)

138-151: 💤 Low value

Potential extra status check after deadline.

After sleeping on line 151, the loop continues to line 82 and calls check_task_status before checking the deadline again on line 139. If the remaining time was very small (e.g., 0.1s), the code will sleep for 0.1s, wake up, make one more status check, and then discover the deadline has passed. This extra check is harmless but slightly wasteful.

Consider moving the deadline check to the top of the loop (before check_task_status) to avoid the extra call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/docling_polling_service.py` around lines 138 - 151, Move the
deadline check so it runs at the start of the polling loop before calling
check_task_status to avoid making an extra status request after sleeping;
specifically, in the polling routine that uses variables deadline, remaining,
start, interval and returns a DoclingPollResult with PollOutcome.TIMEOUT,
compute remaining = deadline - time.monotonic() and if remaining <= 0 log the
timeout and return the timeout DoclingPollResult before invoking
check_task_status (and keep the existing await asyncio.sleep(min(interval,
remaining)) behavior unchanged).
tests/unit/test_docling_service_status_check.py (1)

53-61: 💤 Low value

Consider extracting error message instead of stringifying entire payload.

The test assertion passes as written because "boom" is present in the string representation of the full payload dict. However, the implementation (line 225 in check_task_status) sets detail=str(payload), which stores the entire dict as a string like "{'task_status': 'failure', 'error': 'boom'}". This is fragile and includes unnecessary data. The detail should extract just the error field: detail=payload.get("error", "Unknown error").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_docling_service_status_check.py` around lines 53 - 61, The
test indicates check_task_status currently sets detail=str(payload) which stores
the whole response dict; update the implementation in the check_task_status
function to extract only the error message (e.g., detail = payload.get("error",
"Unknown error")) when task_status == "failure" and keep the state mapping to
DoclingTaskState.FAILED; this ensures snap.detail contains the specific error
string instead of the full payload.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/models/processors.py`:
- Around line 841-849: The code unconditionally instantiates
DoclingPollingService when langflow_file_service.docling_service exists,
breaking the legacy non-polling path; change the creation logic in the processor
so that DoclingPollingService is only constructed when both
langflow_file_service.docling_service is set and the async-ingestion feature
flag/config is enabled (i.e., guard the DoclingPollingService(...) call with the
async-ingestion config), leave docling_polling_service as None otherwise, and
ensure self.docling_polling_service retains that None so downstream callers
receive the non-polling path when the flag is off.

In `@src/services/docling_polling_service.py`:
- Around line 40-44: DoclingPollingService is being instantiated directly in
processors (remove that direct new usage) — instead register and construct it in
the app lifespan and expose it via a dependency provider: add a factory (e.g.,
get_docling_polling_service or create_docling_polling_service) in
dependencies.py that constructs DoclingPollingService using the existing
DoclingService dependency, call that factory from the lifespan block in main.py
to instantiate and store the singleton, and update the processors code to accept
DoclingPollingService via dependency injection (use the new provider) rather
than instantiating DoclingPollingService inline.
- Around line 74-88: There is an indentation inconsistency in the logger.debug
call inside the polling loop: align the first argument of the
logger.debug("Docling polling", task_id=task_id) call to match the other
logger.debug calls in the block (same indentation level as the logger.debug
above and the logger.debug that logs "Snapshot received"); update the
logger.debug invocation in the loop (the one immediately before calling
self.docling_service.check_task_status) so its arguments are indented
consistently with the other logger.debug calls in the method.

In `@src/services/langflow_file_service.py`:
- Around line 538-565: The current flow always calls submit_to_docling even when
docling_polling_service is None; change the control flow in the methods that
handle ingestion (where docling_polling_service, submit_to_docling, and
file_task are used) to branch before calling submit_to_docling: if
docling_polling_service is None, run the legacy Langflow-managed ingestion path
instead of uploading to Docling, and after that legacy path completes set
file_task.docling_status = "SUCCESS" (and persist/update as other branches do)
so tasks don't remain stuck in PROCESSING; ensure the existing polling branch
still calls submit_to_docling and leaves status updates as-is.

---

Outside diff comments:
In `@src/models/processors.py`:
- Line 1: Remove the deprecated typing.Dict import and switch to built-in
generics: delete Dict from the import line (keep Any and Optional) and replace
the three type annotations using Dict with built-in dict generics — change
Optional[Dict[str, Any]] to Optional[dict[str, Any]] (both occurrences) and
change Dict[str, Any] to dict[str, Any]; update the import line to "from typing
import Any, Optional" so annotations compile and satisfy UP035.

---

Nitpick comments:
In `@src/services/docling_polling_service.py`:
- Around line 138-151: Move the deadline check so it runs at the start of the
polling loop before calling check_task_status to avoid making an extra status
request after sleeping; specifically, in the polling routine that uses variables
deadline, remaining, start, interval and returns a DoclingPollResult with
PollOutcome.TIMEOUT, compute remaining = deadline - time.monotonic() and if
remaining <= 0 log the timeout and return the timeout DoclingPollResult before
invoking check_task_status (and keep the existing await
asyncio.sleep(min(interval, remaining)) behavior unchanged).

In `@tests/unit/test_docling_service_status_check.py`:
- Around line 53-61: The test indicates check_task_status currently sets
detail=str(payload) which stores the whole response dict; update the
implementation in the check_task_status function to extract only the error
message (e.g., detail = payload.get("error", "Unknown error")) when task_status
== "failure" and keep the state mapping to DoclingTaskState.FAILED; this ensures
snap.detail contains the specific error string instead of the full payload.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b23665b-a1b5-4725-bff1-9c1e706e47be

📥 Commits

Reviewing files that changed from the base of the PR and between bfc2378 and 6bea2d3.

📒 Files selected for processing (10)
  • src/config/settings.py
  • src/models/processors.py
  • src/models/tasks.py
  • src/services/docling_polling_service.py
  • src/services/docling_service.py
  • src/services/langflow_file_service.py
  • src/services/task_service.py
  • tests/unit/test_docling_polling_service.py
  • tests/unit/test_docling_service_status_check.py
  • tests/unit/test_langflow_file_service_two_phase.py

Comment thread src/models/processors.py Outdated
Comment thread src/services/docling_polling_service.py
Comment thread src/services/docling_polling_service.py Outdated
Comment thread src/services/langflow_file_service.py Outdated
Comment on lines +538 to +565
async def submit_to_docling(self, filename: str, content: bytes) -> str:
"""Upload a file to Docling Serve and return the task_id immediately.

Phase 1 of the two-phase ingestion model. The caller is responsible
for polling Docling (typically via DoclingPollingService) and only
invoking Langflow once Docling reports SUCCESS.
"""
if self.docling_service is None:
raise RuntimeError(
"DoclingService is not configured. Ensure DOCLING_SERVE_URL is set "
"and the service was injected correctly."
)
try:
task_id = await self.docling_service.upload_to_docling_direct_async(
filename, content
)
logger.debug(
"[LF] Docling submission accepted",
extra={"task_id": task_id, "filename": filename},
)
return task_id
except Exception as e:
logger.error(
"[LF] Docling submission failed",
extra={"error": str(e), "filename": filename},
)
raise Exception(f"Docling upload failed: {str(e)}")

@coderabbitai coderabbitai Bot May 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The advertised non-polling fallback is still broken.

The docstring says docling_polling_service=None preserves the old Langflow-managed flow, but Line 614 still submits to Docling unconditionally, so deployments without an injected docling_service will now fail before Langflow starts. On top of that, file_task.docling_status is only set to SUCCESS inside the polling branch, so a successful non-polling run would leave the task stuck in PROCESSING. Please branch the legacy path before submit_to_docling() and finalize the Docling status on successful completion.

Also applies to: 593-615, 661-662, 709-710

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/langflow_file_service.py` around lines 538 - 565, The current
flow always calls submit_to_docling even when docling_polling_service is None;
change the control flow in the methods that handle ingestion (where
docling_polling_service, submit_to_docling, and file_task are used) to branch
before calling submit_to_docling: if docling_polling_service is None, run the
legacy Langflow-managed ingestion path instead of uploading to Docling, and
after that legacy path completes set file_task.docling_status = "SUCCESS" (and
persist/update as other branches do) so tasks don't remain stuck in PROCESSING;
ensure the existing polling branch still calls submit_to_docling and leaves
status updates as-is.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary:

Fix applied in src/services/langflow_file_service.py — after run_ingestion_flow returns successfully, advance docling_status to SUCCESS alongside the existing phase = COMPLETE. Idempotent for the polling path; corrects the legacy path which previously left tasks stuck at PROCESSING.

Test strengthened in tests/unit/test_langflow_file_service_two_phase.py — test_legacy_path_without_polling_service_calls_langflow_directly now passes a file_task and asserts both docling_status == SUCCESS and phase == COMPLETE on completion. This test would have failed before the fix.

Skipped the suggestion to remove the submit_to_docling call in the legacy path. Reason: that call is intentional from PR #1526 from @lucaseduoli it gives Langflow a task_id so its DoclingRemote component skips re-uploading. Removing it would regress that optimization.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 11, 2026
Added ENABLE_BACKEND_DOCLING_POLLING env flag in src/config/settings.py (defaults to true).
Guarded the auto-construction in src/models/processors.py:841-857 so the DoclingPollingService is only built when the flag is on; otherwise self.docling_polling_service stays None and upload_and_ingest_file() falls through to the legacy single-call path.
Added two tests asserting both directions of the flag in tests/unit/test_langflow_file_service_two_phase.py.
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 11, 2026
…n, wired in the same place and same way as every other service. The processor is back to a passive consumer with no knowledge of feature flags or service construction.
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/dependencies.py (1)

20-20: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix import source for AsyncIterator and Sequence to unblock Ruff UP035.

Line 20 currently imports AsyncIterator and Sequence from typing, which violates Ruff UP035. These should come from collections.abc instead.

Proposed fix
-from typing import AsyncIterator, Optional, Sequence
+from collections.abc import AsyncIterator, Sequence
+from typing import Optional
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dependencies.py` at line 20, Update the import line so AsyncIterator and
Sequence are imported from collections.abc instead of typing: remove
AsyncIterator and Sequence from typing import and add them to imports from
collections.abc while keeping Optional from typing; adjust the import
statement(s) in src/dependencies.py accordingly so references to AsyncIterator
and Sequence in this module resolve from collections.abc and satisfy Ruff UP035.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/dependencies.py`:
- Line 20: Update the import line so AsyncIterator and Sequence are imported
from collections.abc instead of typing: remove AsyncIterator and Sequence from
typing import and add them to imports from collections.abc while keeping
Optional from typing; adjust the import statement(s) in src/dependencies.py
accordingly so references to AsyncIterator and Sequence in this module resolve
from collections.abc and satisfy Ruff UP035.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7c2af3eb-d393-498a-ac98-b53dd3dddc92

📥 Commits

Reviewing files that changed from the base of the PR and between 3520def and 8a3c93a.

📒 Files selected for processing (5)
  • src/app/container.py
  • src/dependencies.py
  • src/models/processors.py
  • src/services/task_service.py
  • tests/unit/test_langflow_file_service_two_phase.py

@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 11, 2026

@lucaseduoli lucaseduoli left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! great work. Tested functionally and it works too.

@ricofurtado
ricofurtado requested a review from lucaseduoli May 14, 2026 16:30
@ricofurtado
ricofurtado enabled auto-merge (squash) May 14, 2026 16:34
@ricofurtado
ricofurtado merged commit 5598705 into main May 14, 2026
6 of 11 checks passed
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 14, 2026
@github-actions
github-actions Bot deleted the decouple-docling-processing-from-langflow branch May 14, 2026 16:59
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels May 14, 2026
@mpawlow
mpawlow removed their request for review May 14, 2026 18:49
ricofurtado added a commit that referenced this pull request May 23, 2026
* feat: Implement two-phase ingestion flow with Docling polling

- Added configuration settings for Docling polling intervals and retries.
- Refactored LangflowFileProcessor to support two-phase ingestion with Docling.
- Introduced DoclingPollingService to handle backend polling for Docling task completion.
- Updated LangflowFileService to manage the new two-phase ingestion process.
- Enhanced FileTask model to track Docling task ID and status.
- Created unit tests for DoclingPollingService and LangflowFileService to ensure correct behavior during two-phase ingestion.
- Updated task service to include new fields for tracking ingestion phases and statuses.

Co-authored-by: Copilot <copilot@github.com>

* fix: Fix summary:
Added ENABLE_BACKEND_DOCLING_POLLING env flag in src/config/settings.py (defaults to true).
Guarded the auto-construction in src/models/processors.py:841-857 so the DoclingPollingService is only built when the flag is on; otherwise self.docling_polling_service stays None and upload_and_ingest_file() falls through to the legacy single-call path.
Added two tests asserting both directions of the flag in tests/unit/test_langflow_file_service_two_phase.py.

* Architectural improvement: The polling service is now a true singleton, wired in the same place and same way as every other service. The processor is back to a passive consumer with no knowledge of feature flags or service construction.

* fix: Ensure docling_status is set to SUCCESS in legacy path for coherent status fields

* style: ruff format (auto)

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) enhancement 🔵 New feature or request tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants