Skip to content

feat: Langflow SDK and Flow DevOps API Toolkit#12245

Merged
erichare merged 29 commits into
release-1.9.0from
feature/flow-devops-toolkit
Apr 2, 2026
Merged

feat: Langflow SDK and Flow DevOps API Toolkit#12245
erichare merged 29 commits into
release-1.9.0from
feature/flow-devops-toolkit

Conversation

@erichare
Copy link
Copy Markdown
Collaborator

@erichare erichare commented Mar 18, 2026

Flow DevOps Toolkit (lfx CLI)

This PR expands the capabilities of lfx, a command-line toolkit for managing Langflow flows as code — enabling a proper dev → version control → deploy lifecycle outside the UI.

What's included

New lfx CLI (src/lfx) with the following commands, grouped by lifecycle stage:

Group Commands
Setup init, login
Authoring create, requirements, validate
Running run, serve
Remote status, push, pull, export

Key capabilities:

  • lfx init — scaffolds a local project with a flows/ directory and config
  • lfx create — creates a new flow from a template (e.g. hello-world)
  • lfx validate — validates one or all flows in flows/ against the Langflow schema
  • lfx push / pull — syncs flows between local disk and a running Langflow server
  • lfx status — shows ahead/behind state per flow, using both hash and timestamp so local-vs-remote modifications are correctly distinguished (not just "changed")
  • lfx run / serve — runs a flow directly or starts a local dev server

Supporting infrastructure:

  • langflow-sdk (src/sdk) — a thin Python client wrapping the Langflow REST API, used by both lfx and backend components
  • Docker fixes — both build_and_push.Dockerfile and build_and_push_backend.Dockerfile now correctly include src/sdk so lfx's dependency on langflow-sdk resolves at build time
  • Unit test coverage for init, login, create, push, pull, status, and export commands

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 18, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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

Run ID: 29a2bd36-5c7c-4c1d-b93f-c70043332934

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/flow-devops-toolkit

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.

@erichare erichare changed the base branch from release-1.9.0 to feat-unify-models++ March 18, 2026 19:15
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 18, 2026

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 28%
28.01% (29184/104184) 64.76% (3717/5739) 30.03% (689/2294)

Unit Test Results

Tests Skipped Failures Errors Time
3014 0 💤 0 ❌ 0 🔥 4m 28s ⏱️

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 18, 2026

Codecov Report

❌ Patch coverage is 75.08319% with 599 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.33%. Comparing base (96c9035) to head (59ba983).
⚠️ Report is 5 commits behind head on release-1.9.0.

Files with missing lines Patch % Lines
src/lfx/src/lfx/testing/runners.py 32.25% 83 Missing and 1 partial ⚠️
src/backend/base/langflow/api/v1/flows_helpers.py 71.07% 70 Missing ⚠️
src/lfx/src/lfx/testing/plugin.py 29.34% 61 Missing and 4 partials ⚠️
src/backend/base/langflow/api/v1/projects_files.py 45.61% 62 Missing ⚠️
src/backend/base/langflow/api/v1/flows.py 50.00% 37 Missing ⚠️
src/lfx/src/lfx/cli/validation/semantic.py 71.30% 23 Missing and 10 partials ⚠️
src/lfx/src/lfx/testing/result.py 34.00% 32 Missing and 1 partial ⚠️
src/lfx/src/lfx/cli/push.py 86.97% 17 Missing and 8 partials ⚠️
src/backend/base/langflow/api/v1/projects.py 31.42% 24 Missing ⚠️
src/backend/base/langflow/api/utils/flow_utils.py 72.94% 23 Missing ⚠️
... and 16 more

❌ Your project status has failed because the head coverage (47.86%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                @@
##           release-1.9.0   #12245      +/-   ##
=================================================
+ Coverage          48.91%   49.33%   +0.42%     
=================================================
  Files               1896     1923      +27     
  Lines             167650   170364    +2714     
  Branches           23125    24829    +1704     
=================================================
+ Hits               81998    84046    +2048     
- Misses             84729    85313     +584     
- Partials             923     1005      +82     
Flag Coverage Δ
backend 55.50% <64.74%> (+0.61%) ⬆️
frontend 48.25% <ø> (+0.04%) ⬆️
lfx 47.86% <79.28%> (+1.99%) ⬆️

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

Files with missing lines Coverage Δ
src/lfx/src/lfx/cli/_running_commands.py 100.00% <100.00%> (ø)
src/lfx/src/lfx/cli/export.py 100.00% <100.00%> (ø)
src/lfx/src/lfx/cli/init.py 100.00% <100.00%> (ø)
src/lfx/src/lfx/cli/validate.py 100.00% <100.00%> (ø)
src/lfx/src/lfx/cli/validation/_env_validation.py 100.00% <ø> (ø)
src/backend/base/langflow/initial_setup/setup.py 53.25% <50.00%> (+0.55%) ⬆️
src/lfx/src/lfx/cli/_setup_commands.py 81.81% <81.81%> (ø)
src/lfx/src/lfx/__main__.py 71.42% <80.00%> (-13.94%) ⬇️
src/backend/base/langflow/api/utils/core.py 76.71% <90.74%> (+8.47%) ⬆️
...se/langflow/services/database/models/flow/model.py 77.77% <64.28%> (ø)
... and 21 more

... and 47 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 Mar 18, 2026
@Cristhianzl
Copy link
Copy Markdown
Member

Items Fixed (Confirmed) ✅

Click to expand — 20 items verified as resolved
Item What was fixed
D1 _load_sdk() extracted to lfx/cli/common.py — no longer duplicated across 4 CLI files
D2 _safe_filename() extracted to lfx/cli/common.py
F1–F7 All flagged files now under 500 lines (client.py 466, validate.py 60, flows.py 513, projects.py 398, __main__.py 29, core.py 435)
F2 lfx/testing.py split into testing/plugin.py, runners.py, result.py, __init__.py
E1 Silent except: pass in push.py → now has logger.debug() with exc_info=True
E2 contextlib.suppress(Exception) in testing → removed
E3 Silent stream parse in client.py → now has _logger.debug("Skipping malformed SSE chunk")
E4 Broad suppression in background_job.py → now only asyncio.CancelledError
E5 Generic except Exception in export.py → now except (json.JSONDecodeError, OSError, ValueError)
E6 Generic except Exception in init.py → now except (OSError, ValueError, TypeError, RuntimeError)
E8 Redundant PermissionError catch → resolved via validate.py refactor
E9 Fragile int(split) in flows → wrapped in try/except ValueError
E10 Fragile int(split) in projects → wrapped in try/except ValueError
S2 __repr__ API key exposure → reduced from 8 to 4 chars
SO2 Pydantic validators no longer raise HTTPException → now raise ValueError
CQ5 Import ordering bug in status.pyLangflowNotFoundError now imported inside _load_sdk()
Zip Slip projects_files.py now uses Path(flow["name"]).name for ZIP entry sanitization

Still Open — Must Fix

1. 🟠 S3: LIKE Wildcard Injection — projects.py:68

# Line 68 — MISSING escape parameter
Folder.name.like(f"{new_project.name}%")

# Line 199 — correctly escaped (same file!)
Flow.name.like(f"%{_search}%", escape="\\")

new_project.name is user-controlled. Without escaping % and _, a user can craft project names that match unintended folders. The fix is already demonstrated on line 199 of the same file — just add escape="\\".

Fix: One-line change — add escape="\\" to the .like() call on line 68.


2. 🟠 D3: Sync/Async Client Duplication — sdk/client.py + _async_client.py

The split from 883 → 466 + 482 lines reduced file sizes (great), but the underlying DRY issue remains: 24+ methods are near-identical copies between LangflowClient and AsyncLangflowClient, differing only by async/await.

Every CRUD method (list_flows, get_flow, create_flow, update_flow, delete_flow, list_projects, get_project, push, pull, etc.) exists in both files with the same logic.

Common approaches to solve this:

  • Base class/mixin with shared URL-building and response-parsing logic
  • Code generation (e.g., unasync)
  • Template-based approach

Note: I understand this is a common pattern in Python SDKs and can be pragmatically acceptable. If the team decides to defer this, it should be tracked as a follow-up item.


3. 🟠 D4-D5: Sync/Async Runner Duplication — lfx/testing/runners.py

Same pattern as D3. Four classes in 438 lines:

  • LocalFlowRunner / AsyncLocalFlowRunner
  • RemoteFlowRunner / AsyncRemoteFlowRunner

The sync and async variants share the same logic with async/await sprinkled in. Same fix approaches as D3.


4. 🟠 E7: Generic Exception Handling in pull.py

pull.py still has 5 except Exception blocks. The CLI-facing ones (lines 180, 196, 208, 229) that do raise typer.Exit(1) are acceptable as catch-all CLI handlers.

However, line 94 inside _write_flow silently converts errors into a PullResult(status="error") without any logging:

except Exception as exc:  # noqa: BLE001
    dummy_path = dest_dir / f"{flow_id}.json"
    return PullResult(flow_id=flow_id, flow_name=flow_name, path=dummy_path, status="error", error=str(exc))

Internal errors (permission denied, encoding issues, etc.) vanish silently here. Fix: Add logger.debug("Failed to write flow %s", flow_id, exc_info=True) before the return.


5. 🟠 CQ10: Global Mutable State — flows.py:442

_starter_flows_cache: Response | None = None

Module-level mutable cache with no thread-safety and no invalidation mechanism. In a multi-worker ASGI deployment, this is a race condition risk.

Fix: Use functools.lru_cache, an asyncio.Lock-guarded cache, or move to a proper caching layer.


Still Open — Recommended

6. 🟡 Zip Slip Partial Fix — projects_files.py:73

safe_name = Path(flow["name"]).name or str(flow.get("id", "flow"))

Path.name strips path separators for the current OS only. On POSIX, Path("..\evil").name returns "..\evil" unchanged — backslashes are not path separators on Linux. If the resulting ZIP is extracted on Windows, this becomes a path traversal.

Fix: Explicitly strip both / and \, and reject .. segments:

safe_name = flow["name"].replace("/", "_").replace("\\", "_").replace("..", "_")

7. 🟡 RC1: SDK Client Has Zero Logging

sdk/client.py and _async_client.py have no logging statements at all. DEBUG-level logs for connection errors, HTTP status codes, and retries would significantly improve integration debugging.

8. 🟡 TS6: Test Coverage Not Shown

Per review rules, coverage must be executed and shown with >= 75% minimum for all created tests. Codecov shows 74.7% patch coverage with 595 uncovered lines — close but below threshold. Please verify and share the per-module breakdown.


Summary

Category Before After Remaining
CRITICAL (file sizes, DRY extractions) 12 violations 4 remaining D3, D4-D5 (sync/async duplication)
IMPORTANT (error handling, security, SOLID) 18 violations 4 remaining S3, E7, CQ10, Zip Slip
RECOMMENDED 8 items 3 remaining RC1, TS6
TESTING Pass with notes Same TS6 pending

Priority order for remaining fixes:

  1. S3 — one-line fix, security issue
  2. E7 — one-line fix, add debug log in _write_flow
  3. Zip Slip — small fix, security hardening
  4. CQ10 — thread-safety concern
  5. D3/D4-D5 — discuss whether to fix now or track as follow-up
  6. RC1/TS6 — polish items

Copy link
Copy Markdown
Member

@Cristhianzl Cristhianzl left a comment

Choose a reason for hiding this comment

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

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants