Skip to content

feat: deployment-profile based pre-flight checks - #14393

Open
dkaushik94 wants to merge 2 commits into
release-1.12.0from
runtime-fail-loud-checks
Open

feat: deployment-profile based pre-flight checks#14393
dkaushik94 wants to merge 2 commits into
release-1.12.0from
runtime-fail-loud-checks

Conversation

@dkaushik94

@dkaushik94 dkaushik94 commented Aug 3, 2026

Copy link
Copy Markdown
Member

Production profile pre-flight checks for infra services

Adds a --deployment-profile (dev | prod) flag that, when set to prod, runs a set of fail-loud infrastructure checks before any worker is spawned. A misconfigured production deployment now aborts cleanly with actionable remediation instead of coming up half-working.

Motivation

Today Langflow will happily boot in production with local filesystem storage, an auto-generated per-boot secret key, or a missing pgvector database — failures that only surface later as broken file persistence, invalidated JWTs across replicas, or runtime errors. This adds an explicit, opt-in production gate that surfaces every misconfiguration up front, in a single pass.

What's included

  • New deployment_profile setting (src/lfx/.../settings/groups/server.py) — dev (default) or prod, settable via LANGFLOW_DEPLOYMENT_PROFILE or --deployment-profile. Normalizing validator accepts PROD, prod, etc.
  • New preflight module (langflow/cli/preflight.py) running two tiers of checks:
    • Required (abort boot on failure): database connectivity, external file storage, operator-supplied encryption secret key, and pgVector (reachable + vector extension installed).
    • Degraded (warn only): telemetry, cache (Redis), and shared job queue — surface reduced capability (e.g. single-instance cache semantics) but let boot continue.
  • Storage readiness probes — new StorageReadiness dataclass + check_readiness() on StorageService (local writability sentinel) and S3StorageService (credential resolution + head_bucket, distinguishing missing-creds / bucket-missing / access-denied / unreachable). Mirrored in both langflow and lfx.
  • Two enforcement points, run-once semantics:
    • CLI run executes preflight in the parent process before Gunicorn forks workers, so a bad config exits non-zero and no worker starts.
    • FastAPI lifespan runs the same check as a safety net for CLI-bypassing entrypoints (make backend, uvicorn --factory, raw Gunicorn), before services/migrations initialize.
    • A LANGFLOW_PREFLIGHT_COMPLETED env sentinel (inherited across fork) ensures the checks run exactly once per process tree.

Design notes

  • Every probe is fork-safe: sync engines are disposed, and cache/queue/storage services are built as throwaway instances (never registered on the global service manager) and torn down before returning, so no open socket is inherited by forked workers.
  • All checks run before any abort — the operator sees every problem in one pass instead of fix-one / restart / hit-the-next.
  • dev (the default) is a complete no-op.

Tests

  • src/backend/tests/unit/test_cli_preflight.py (296 lines) covering profile normalization, per-check pass/fail/degraded outcomes, run-once sentinel behavior, and abort semantics.

Files

File Change
cli/preflight.py new preflight module (+511)
cli/progress.py progress indicator support (+88)
__main__.py --deployment-profile flag + pre-fork invocation
main.py lifespan safety-net + PreflightAbortError handling
storage/service.py, storage/s3.py StorageReadiness + check_readiness()
lfx/.../settings/groups/server.py deployment_profile setting + validator
lfx/.../storage/service.py mirrored StorageReadiness
tests/unit/test_cli_preflight.py new test suite (+296)

Summary by CodeRabbit

  • New Features

    • Added development and production deployment profiles, configurable through the CLI or environment variables.
    • Production startup now validates database, storage, encryption, and vector search readiness.
    • Added readiness checks for local and S3-compatible storage.
    • Added clear remediation details for failed or degraded checks.
  • Bug Fixes

    • Improved command-line progress output for interactive and captured environments.
    • Production startup now exits safely when required infrastructure checks fail.
  • Tests

    • Added comprehensive coverage for profile validation, readiness checks, startup behavior, and failure handling.

Testing:

1. Fastest smoke test (no infra needed)

Dev = nothing happens:

uv run langflow run --backend-only

Expect: normal boot, no "production preflight" section.

Prod = fails loud and aborts (default sqlite DB passes; storage/secret/pgvector fail):

uv run langflow run --backend-only --deployment-profile prod

Expect: a "Deployment profile: prod" section, red ✗ for File storage / Encryption secret key /
Vector backend, a "Preflight failed — N checks did not pass. Aborting boot." summary, and the
process exits without starting the server.


2. Exercise each check in isolation (fast, no full boot)

This runs just the probes and prints their status:

uv run python -c "
import asyncio
from langflow.cli import preflight as p
from langflow.services.deps import get_settings_service
ss = get_settings_service()
async def go():
    for c in p.ALL_CHECKS:
        r = await p._probe_safely(c, ss)
        print(f'{r.status:4}  {c.label}: {r.detail}')
asyncio.run(go())
"

Prefix env vars to flip individual checks (examples below).

Check Make it GREEN (ok) Make it RED (fail) WARN
Database default sqlite, or a reachable LANGFLOW_DATABASE_URL LANGFLOW_DATABASE_URL=postgresql://u:p@127.0.0.1:1/x
File storage LANGFLOW_STORAGE_TYPE=s3 + real bucket/creds (see §5) LANGFLOW_STORAGE_TYPE=local (mandated external in prod)
Secret key LANGFLOW_SECRET_KEY=$(python3 -c "print('x'*40)") unset it
pgVector reachable DSN + vector extension (see §5) unset PGVECTOR_CONNECTION_STRING
Telemetry default LANGFLOW_DO_NOT_TRACK=true
Cache LANGFLOW_CACHE_TYPE=redis + Redis up LANGFLOW_CACHE_TYPE=redis + Redis down unset (in-memory default)
Queue LANGFLOW_JOB_QUEUE_TYPE=redis + Redis up LANGFLOW_JOB_QUEUE_TYPE=redis + Redis down unset (asyncio default)

Cache/queue behavior (the key new rule)

# defaults -> WARN, boot continues (falls back to in-memory / asyncio)
uv run langflow run --backend-only --deployment-profile prod

# redis selected but NOT running -> FAIL, aborts boot
LANGFLOW_CACHE_TYPE=redis LANGFLOW_JOB_QUEUE_TYPE=redis \
  uv run langflow run --backend-only --deployment-profile prod

Expect in the second case: red ✗ under Degraded services for Cache and Shared queue, and the
remediation tells you to fix the backend or unset the flag to boot degraded.


3. Route coverage — the check must fire regardless of how the app starts

a) langflow run (CLI path): covered by §1.

b) make backend / uvicorn --factory (bypasses the CLI): the lifespan enforces it and
hard-exits the worker.

LANGFLOW_DEPLOYMENT_PROFILE=prod LANGFLOW_STORAGE_TYPE=local make backend

Expect: preflight renders, then the worker exits (code 3) instead of serving. (Confirms the
check isn't CLI-only.)


4. Run-once verification (multi-worker shouldn't probe N times)

LANGFLOW_DEPLOYMENT_PROFILE=prod uv run langflow run --backend-only --workers 3

Expect: the "production preflight" section prints once, not once per worker. (The parent runs
it pre-fork and sets LANGFLOW_PREFLIGHT_COMPLETED=1, which workers inherit and skip.)

Note: real multi-worker forking only happens on Linux (Gunicorn). On macOS/Windows
langflow run uses single-process uvicorn and clamps --workers to 1, so this test is most
meaningful on Linux.


5. Full GREEN path (QA — needs infra)

Spin up Postgres+pgvector and Redis with Docker:

docker run -d --name lf-pg   -e POSTGRES_PASSWORD=pg -p 5432:5432 pgvector/pgvector:pg16
docker run -d --name lf-redis -p 6379:6379 redis:7
# enable the pgvector extension
docker exec lf-pg psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS vector;"

Then boot with everything wired (storage still needs real S3 — or MinIO, see note):

LANGFLOW_DEPLOYMENT_PROFILE=prod \
LANGFLOW_DATABASE_URL="postgresql://postgres:pg@localhost:5432/postgres" \
PGVECTOR_CONNECTION_STRING="postgresql://postgres:pg@localhost:5432/postgres" \
LANGFLOW_SECRET_KEY="$(python3 -c 'print("x"*40)')" \
LANGFLOW_CACHE_TYPE=redis LANGFLOW_JOB_QUEUE_TYPE=redis \
LANGFLOW_STORAGE_TYPE=s3 \
LANGFLOW_OBJECT_STORAGE_BUCKET_NAME=<your-bucket> \
AWS_ACCESS_KEY_ID=<...> AWS_SECRET_ACCESS_KEY=<...> AWS_DEFAULT_REGION=<...> \
uv run langflow run --backend-only

Expect: all ✓/⚠, "Preflight passed … Continuing boot", server starts.

Notes:

  • Storage green needs a reachable bucket. Real S3 with creds is simplest. MinIO works too but
    requires AWS_ENDPOINT_URL=http://localhost:9000 and a pre-created bucket.
  • To see pgVector's extension-missing failure specifically: point PGVECTOR_CONNECTION_STRING
    at a Postgres where you have not run CREATE EXTENSION vector; → red ✗ "reachable, but the
    'vector' extension is not installed".

Cleanup: docker rm -f lf-pg lf-redis

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 599b1422-6b28-49e9-94cd-f2a6a9025a5e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Changes

Production deployment preflight

Layer / File(s) Summary
Profile and readiness contracts
src/lfx/src/lfx/services/settings/groups/server.py, src/*/services/storage/service.py
Adds dev and prod deployment profiles and typed storage readiness checks for local backends.
Storage backend probes
src/backend/base/langflow/services/storage/s3.py, src/backend/base/langflow/cli/preflight.py
Validates S3 access and rejects local storage for production preflight.
Preflight checks and reporting
src/backend/base/langflow/cli/preflight.py, src/backend/base/langflow/cli/progress.py, src/backend/tests/unit/test_cli_preflight.py
Adds required and warning-only checks for database, storage, secrets, pgVector, telemetry, cache, and queues. Tests cover execution, reporting, gating, and sentinel behavior.
CLI and lifespan integration
src/backend/base/langflow/__main__.py, src/backend/base/langflow/main.py
Adds the CLI profile option, runs production preflight before service initialization, and handles failed preflight startup termination.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested labels: enhancement

Suggested reviewers: ogabrielluiz

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant SettingsService
  participant Preflight
  participant StorageService
  participant Database
  participant Lifespan
  CLI->>SettingsService: resolve deployment profile
  CLI->>Preflight: run production preflight
  Preflight->>StorageService: check storage readiness
  Preflight->>Database: test database and pgVector
  Preflight-->>Lifespan: allow startup or raise abort
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error The PR adds only one unit test file; no integration tests cover production CLI/FastAPI startup or storage readiness, and no tests cover the new ProgressIndicator behavior. Add backend integration tests for production startup and storage readiness, plus focused tests for ProgressIndicator and the CLI deployment-profile path.
Docstring Coverage ⚠️ Warning Docstring coverage is 12.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Quality And Coverage ⚠️ Warning The async preflight tests cover core probe outcomes and runner gating, but no tests cover ProgressIndicator, S3/local readiness, CLI/lifespan integration, rendering, or Redis branches. Add focused pytest tests for every new readiness branch, progress output/status behavior, CLI flag and environment propagation, lifespan abort handling, rendering, and Redis cache/queue probes.
✅ Passed checks (6 passed)
Check name Status Explanation
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.
Test File Naming And Structure ✅ Passed The PR adds discoverable backend unit tests in test_cli_preflight.py with descriptive names, isolated pytest fixtures, and positive/negative coverage for profile, probes, warnings, and aborts.
Excessive Mock Usage Warning ✅ Passed The changed tests use no Mock/AsyncMock/MagicMock/patch; small stubs and monkeypatch isolate environment, external storage, and registries while real probe and runner logic remains exercised.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the deployment-profile-based production pre-flight checks introduced by the pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch runtime-fail-loud-checks

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.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Aug 3, 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.

🧹 Nitpick comments (8)
src/backend/tests/unit/test_cli_preflight.py (2)

243-254: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import os at module level instead of __import__("os").

Line 273 already uses a local import os in another test. Use a single module-level import for both.

♻️ Proposed change
-    assert PREFLIGHT_COMPLETED_ENV not in __import__("os").environ
+    assert PREFLIGHT_COMPLETED_ENV not in os.environ
🤖 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/backend/tests/unit/test_cli_preflight.py` around lines 243 - 254, Update
the test module imports to add a module-level os import, then replace the
__import__("os").environ usage in test_ensure_is_noop_in_dev and the local
import in the other test with that shared os symbol.

127-156: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for the two check_readiness implementations.

These tests replace StorageServiceFactory.create with a fake, so they verify only the reason-to-remediation mapping in probe_storage. The new probe implementations stay untested: the default filesystem probe in StorageService.check_readiness (writable path and the OSError to unwritable path) and the S3 status-code classification in S3StorageService.check_readiness (bucket-missing, access-denied, unreachable). Cover the filesystem probe with a real tmp_path directory and a read-only directory, and cover the S3 branches with a stub client that raises errors carrying a response dict.

As per coding guidelines: "For new backend implementations or bug fixes, ensure corresponding pytest test files are included ... and verify the tests actually cover the new or changed behavior rather than acting as placeholders."

🤖 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/backend/tests/unit/test_cli_preflight.py` around lines 127 - 156, Add
direct pytest coverage for StorageService.check_readiness using a real tmp_path
directory, including writable success and an OSError case that maps to
unwritable. Add S3StorageService.check_readiness tests with a stub client whose
errors expose response dictionaries, covering bucket-missing, access-denied, and
unreachable classifications; keep these separate from probe_storage
factory-mocking tests so the implementations themselves are exercised.

Source: Coding guidelines

src/backend/base/langflow/services/storage/s3.py (1)

191-193: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider bounding the head_bucket probe with an explicit timeout.

_get_client() uses default botocore timeouts and retries. If the endpoint blackholes packets, this preflight probe can block boot for a long time before it reports unreachable. Pass a short botocore.config.Config (connect/read timeout plus a low retry count) for the readiness client, or wrap the call in asyncio.timeout.

♻️ Example: dedicated short-timeout client for the probe
+        from botocore.config import Config
+
         try:
-            async with self._get_client() as s3_client:
+            probe_config = Config(connect_timeout=5, read_timeout=5, retries={"max_attempts": 1})
+            async with self.session.create_client("s3", config=probe_config) as s3_client:
                 await s3_client.head_bucket(Bucket=self.bucket_name)
🤖 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/backend/base/langflow/services/storage/s3.py` around lines 191 - 193,
Update the readiness probe around S3Storage’s head_bucket call to enforce a
short explicit timeout, preferably by using a dedicated botocore Config with
bounded connect/read timeouts and low retries when creating the probe client, or
by wrapping the operation in asyncio.timeout. Keep the existing unreachable
handling while ensuring blackholed endpoints cannot block startup indefinitely.
src/backend/base/langflow/main.py (1)

620-647: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Merge the two identical abort handlers.

The UnsupportedPostgreSQLVersionError and PreflightAbortError handlers have the same body: flush both streams, signal the parent, then os._exit(3). Combine them into one except clause so the exit contract stays in one place.

♻️ Proposed change
-        except UnsupportedPostgreSQLVersionError:
-            # Normally caught by the pre-flight check in __main__.py
-            # before the server starts.  If we get here anyway (e.g.
-            # direct uvicorn invocation via ``make backend``), exit
-            # immediately and tell the parent (reloader) to stop.
-            import signal
-
-            sys.stdout.flush()
-            sys.stderr.flush()
-            with suppress(ProcessLookupError, PermissionError):
-                os.kill(os.getppid(), signal.SIGTERM)
-            os._exit(3)
-        except PreflightAbortError:
-            # Same rationale as UnsupportedPostgreSQLVersionError above: on the
-            # `langflow run` route this is caught before the server starts. If we
-            # reach here, boot came via a CLI-bypassing entrypoint (make backend,
-            # uvicorn --factory) with deployment_profile=prod and a required
-            # dependency missing. The summary has already been printed — exit
-            # immediately and tell the parent (reloader) to stop.
-            import signal
-
+        except (UnsupportedPostgreSQLVersionError, PreflightAbortError):
+            # Normally caught before the server starts: the Postgres version check
+            # and the production preflight both run in __main__.py. Reaching here
+            # means boot came via a CLI-bypassing entrypoint (make backend,
+            # uvicorn --factory). The diagnostic output is already printed, so exit
+            # immediately and tell the parent (reloader) to stop.
+            import signal
+
             sys.stdout.flush()
🤖 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/backend/base/langflow/main.py` around lines 620 - 647, Merge the
UnsupportedPostgreSQLVersionError and PreflightAbortError handlers in the
lifespan exception flow into a single multi-exception except clause. Preserve
the existing shared behavior—flush stdout and stderr, signal the parent process
while suppressing lookup/permission errors, then exit with status 3—and retain
any necessary explanatory comments.
src/backend/base/langflow/cli/preflight.py (2)

336-361: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Tear down the throwaway queue service like the cache probe does.

probe_cache and probe_storage both call teardown() in a finally block to keep no socket in the pre-fork parent. probe_shared_queue drops queue_service without teardown on every path, including the non-Redis branch and the failure branch of is_connected(). The inline comment states the guarantee for one method on one class only. Add the same symmetric cleanup so the invariant does not depend on the internals of is_connected().

♻️ Proposed change
     queue_service = JobQueueServiceFactory().create(settings_service)
-    if isinstance(queue_service, RedisJobQueueService):
-        if await queue_service.is_connected():
-            return CheckResult("ok", f"redis reachable ({queue_service.connection_target})")
-        return CheckResult(
-            "warn",
-            f"redis queue configured but unreachable at {queue_service.connection_target}",
-            "Start Redis or fix LANGFLOW_REDIS_QUEUE_* settings; jobs will not be distributed.",
-        )
-    return CheckResult("ok", f"configured ({queue_type})")
+    try:
+        if isinstance(queue_service, RedisJobQueueService):
+            if await queue_service.is_connected():
+                return CheckResult("ok", f"redis reachable ({queue_service.connection_target})")
+            return CheckResult(
+                "warn",
+                f"redis queue configured but unreachable at {queue_service.connection_target}",
+                "Start Redis or fix LANGFLOW_REDIS_QUEUE_* settings; jobs will not be distributed.",
+            )
+        return CheckResult("ok", f"configured ({queue_type})")
+    finally:
+        teardown = getattr(queue_service, "teardown", None)
+        if teardown is not None:
+            with contextlib.suppress(Exception):
+                await teardown()
🤖 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/backend/base/langflow/cli/preflight.py` around lines 336 - 361, Update
probe_shared_queue to always tear down the created queue_service in a finally
block, covering non-Redis, successful connectivity, and failed is_connected()
paths. Keep the existing CheckResult behavior unchanged while ensuring cleanup
is performed symmetrically with probe_cache and probe_storage.

368-408: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

ALL_CHECKS is a snapshot, so collect_outcomes can ignore later list changes.

ALL_CHECKS is built once at import time. _execute_and_render reads REQUIRED_CHECKS and DEGRADED_CHECKS at call time, so the two entry points can disagree after either list is reassigned (the tests reassign preflight.REQUIRED_CHECKS). Compute the default inside collect_outcomes to keep one behavior.

♻️ Proposed change
-    selected = checks if checks is not None else ALL_CHECKS
+    selected = checks if checks is not None else [*REQUIRED_CHECKS, *DEGRADED_CHECKS]
🤖 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/backend/base/langflow/cli/preflight.py` around lines 368 - 408, Update
collect_outcomes so its default selection is built at call time from the current
REQUIRED_CHECKS and DEGRADED_CHECKS lists instead of using the import-time
ALL_CHECKS snapshot. Preserve the explicit checks argument behavior, and align
the default path with _execute_and_render when either list is reassigned.
src/backend/base/langflow/__main__.py (1)

405-411: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider constraining the option values at the CLI layer.

The option accepts any string. An invalid value such as --deployment-profile staging reaches settings_service.set(...) in the CLI-arg loop and raises a Pydantic validation error inside progress.step(1), so the user sees a traceback instead of a usage error. Add click.Choice(["dev", "prod"]) so Typer rejects the value with a clean message.

♻️ Proposed change
     deployment_profile: str | None = typer.Option(  # noqa: ARG001 — applied to settings via the CLI-arg loop below
         None,
+        click_type=click.Choice(["dev", "prod"], case_sensitive=False),
         help="Deployment profile: 'dev' (default) or 'prod'. 'prod' runs fail-loud "
🤖 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/backend/base/langflow/__main__.py` around lines 405 - 411, Constrain the
deployment_profile Typer option to the supported values by using
click.Choice(["dev", "prod"]) instead of an unrestricted string type. Keep the
existing default, help text, and CLI-argument loop behavior unchanged so invalid
values are rejected as usage errors before reaching settings_service.set.
src/lfx/src/lfx/services/storage/service.py (1)

18-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated storage readiness contract in lfx and langflow-base. Both packages define the same StorageReadiness dataclass and the same default check_readiness body, including the same reason vocabulary. The two definitions can drift, and a value produced by one package fails an isinstance check against the other. langflow.cli.preflight already imports from lfx, so one source of truth is available.

  • src/lfx/src/lfx/services/storage/service.py#L18-L36: keep this definition as the single source of truth and keep the default probe at lines 212-239 here.
  • src/backend/base/langflow/services/storage/service.py#L21-L39: import and re-export StorageReadiness from lfx.services.storage.service instead of redefining it, and delegate the default check_readiness body at lines 51-74 to the lfx implementation.
🤖 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/lfx/src/lfx/services/storage/service.py` around lines 18 - 36, Use
src/lfx/src/lfx/services/storage/service.py lines 18-36 as the single
StorageReadiness definition and retain its default check_readiness
implementation at lines 212-239. In
src/backend/base/langflow/services/storage/service.py lines 21-39, remove the
duplicate dataclass and import/re-export StorageReadiness from
lfx.services.storage.service; update the default check_readiness at lines 51-74
to delegate to the lfx implementation while preserving the existing API.
🤖 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.

Nitpick comments:
In `@src/backend/base/langflow/__main__.py`:
- Around line 405-411: Constrain the deployment_profile Typer option to the
supported values by using click.Choice(["dev", "prod"]) instead of an
unrestricted string type. Keep the existing default, help text, and CLI-argument
loop behavior unchanged so invalid values are rejected as usage errors before
reaching settings_service.set.

In `@src/backend/base/langflow/cli/preflight.py`:
- Around line 336-361: Update probe_shared_queue to always tear down the created
queue_service in a finally block, covering non-Redis, successful connectivity,
and failed is_connected() paths. Keep the existing CheckResult behavior
unchanged while ensuring cleanup is performed symmetrically with probe_cache and
probe_storage.
- Around line 368-408: Update collect_outcomes so its default selection is built
at call time from the current REQUIRED_CHECKS and DEGRADED_CHECKS lists instead
of using the import-time ALL_CHECKS snapshot. Preserve the explicit checks
argument behavior, and align the default path with _execute_and_render when
either list is reassigned.

In `@src/backend/base/langflow/main.py`:
- Around line 620-647: Merge the UnsupportedPostgreSQLVersionError and
PreflightAbortError handlers in the lifespan exception flow into a single
multi-exception except clause. Preserve the existing shared behavior—flush
stdout and stderr, signal the parent process while suppressing lookup/permission
errors, then exit with status 3—and retain any necessary explanatory comments.

In `@src/backend/base/langflow/services/storage/s3.py`:
- Around line 191-193: Update the readiness probe around S3Storage’s head_bucket
call to enforce a short explicit timeout, preferably by using a dedicated
botocore Config with bounded connect/read timeouts and low retries when creating
the probe client, or by wrapping the operation in asyncio.timeout. Keep the
existing unreachable handling while ensuring blackholed endpoints cannot block
startup indefinitely.

In `@src/backend/tests/unit/test_cli_preflight.py`:
- Around line 243-254: Update the test module imports to add a module-level os
import, then replace the __import__("os").environ usage in
test_ensure_is_noop_in_dev and the local import in the other test with that
shared os symbol.
- Around line 127-156: Add direct pytest coverage for
StorageService.check_readiness using a real tmp_path directory, including
writable success and an OSError case that maps to unwritable. Add
S3StorageService.check_readiness tests with a stub client whose errors expose
response dictionaries, covering bucket-missing, access-denied, and unreachable
classifications; keep these separate from probe_storage factory-mocking tests so
the implementations themselves are exercised.

In `@src/lfx/src/lfx/services/storage/service.py`:
- Around line 18-36: Use src/lfx/src/lfx/services/storage/service.py lines 18-36
as the single StorageReadiness definition and retain its default check_readiness
implementation at lines 212-239. In
src/backend/base/langflow/services/storage/service.py lines 21-39, remove the
duplicate dataclass and import/re-export StorageReadiness from
lfx.services.storage.service; update the default check_readiness at lines 51-74
to delegate to the lfx implementation while preserving the existing API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc268c1c-5a23-479d-964d-ed4cf05d8198

📥 Commits

Reviewing files that changed from the base of the PR and between e73fd4b and 55dc77d.

📒 Files selected for processing (9)
  • src/backend/base/langflow/__main__.py
  • src/backend/base/langflow/cli/preflight.py
  • src/backend/base/langflow/cli/progress.py
  • src/backend/base/langflow/main.py
  • src/backend/base/langflow/services/storage/s3.py
  • src/backend/base/langflow/services/storage/service.py
  • src/backend/tests/unit/test_cli_preflight.py
  • src/lfx/src/lfx/services/settings/groups/server.py
  • src/lfx/src/lfx/services/storage/service.py

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 49%
49.57% (71705/144626) 70.35% (10042/14274) 46.77% (1644/3515)

Unit Test Results

Tests Skipped Failures Errors Time
5529 0 💤 0 ❌ 0 🔥 19m 34s ⏱️

@dkaushik94
dkaushik94 force-pushed the runtime-fail-loud-checks branch from 55dc77d to f6ec8d0 Compare August 3, 2026 23:02
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Aug 3, 2026
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.38554% with 95 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.93%. Comparing base (281392d) to head (12abc7e).
⚠️ Report is 3 commits behind head on release-1.12.0.

Files with missing lines Patch % Lines
src/backend/base/langflow/cli/preflight.py 85.44% 31 Missing ⚠️
src/backend/base/langflow/services/storage/s3.py 12.50% 21 Missing ⚠️
.../backend/base/langflow/services/storage/service.py 47.36% 10 Missing ⚠️
src/lfx/src/lfx/services/storage/service.py 47.36% 10 Missing ⚠️
src/lfx/src/lfx/services/settings/groups/server.py 52.94% 5 Missing and 3 partials ⚠️
src/backend/base/langflow/cli/progress.py 76.00% 6 Missing ⚠️
src/backend/base/langflow/main.py 33.33% 6 Missing ⚠️
src/backend/base/langflow/__main__.py 50.00% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.12.0   #14393      +/-   ##
==================================================
+ Coverage           61.63%   62.93%   +1.30%     
==================================================
  Files                2416     2379      -37     
  Lines              242162   242289     +127     
  Branches            36186    33939    -2247     
==================================================
+ Hits               149249   152485    +3236     
+ Misses              90995    87883    -3112     
- Partials             1918     1921       +3     
Flag Coverage Δ
backend 70.32% <73.98%> (+1.93%) ⬆️
frontend 61.34% <ø> (+1.48%) ⬆️
lfx 61.07% <50.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/backend/base/langflow/__main__.py 56.85% <50.00%> (+1.77%) ⬆️
src/backend/base/langflow/cli/progress.py 66.91% <76.00%> (-7.89%) ⬇️
src/backend/base/langflow/main.py 64.29% <33.33%> (+0.20%) ⬆️
src/lfx/src/lfx/services/settings/groups/server.py 73.52% <52.94%> (-4.38%) ⬇️
.../backend/base/langflow/services/storage/service.py 68.33% <47.36%> (-9.72%) ⬇️
src/lfx/src/lfx/services/storage/service.py 65.78% <47.36%> (-18.43%) ⬇️
src/backend/base/langflow/services/storage/s3.py 31.89% <12.50%> (-2.68%) ⬇️
src/backend/base/langflow/cli/preflight.py 85.44% <85.44%> (ø)

... and 567 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Aug 4, 2026
@dkaushik94
dkaushik94 force-pushed the runtime-fail-loud-checks branch from f6ec8d0 to 413264f Compare August 4, 2026 18:18
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Aug 4, 2026
Jkavia pushed a commit that referenced this pull request Aug 4, 2026
erichare pushed a commit that referenced this pull request Aug 5, 2026
Added conditional check, if richer service is selected like redis but is not reachable, bootup fails.
@dkaushik94
dkaushik94 force-pushed the runtime-fail-loud-checks branch from bceb04c to 12abc7e Compare August 5, 2026 18:52
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request scaling-up

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant