Skip to content

Commit 1a1e16e

Browse files
committed
fix(hitl): rename inputs, advanced timeout (backend) + fix(frontend): HITL palette and control polish
1 parent 54c24c6 commit 1a1e16e

55 files changed

Lines changed: 669 additions & 321 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/features/durable-execution-hitl.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ The pause probe is a no-op unless a component requests it, so normal flows are b
127127
- **Then** the run finalizes once — no loop back to the first node
128128
- **And** only the chosen branch of each node runs (the first node's non-chosen branches stay dead across the second pause's resume, not just within the build that answered them)
129129

130+
### Scenario: Rerunning a paused flow supersedes the stale pause
131+
- **Given** a flow with a `SUSPENDED` run awaiting a decision
132+
- **When** the same user submits a new background run of that flow
133+
- **Then** the stale suspended run is cancelled (`CANCELLED`, pending request cleared) before the new run starts, so only the new pause is offered on every surface
134+
- **And** the scope is flow + user on the langflow backend (another user's pause on the same flow is untouched) and flow on `lfx serve` (single API-key identity); running jobs are never superseded, so parallel runs stay supported
135+
130136
### Scenario: Single-flight resume
131137
- **Given** two resume requests for the same `SUSPENDED` job
132138
- **When** both arrive
@@ -225,12 +231,17 @@ Drain the queue **inline** after cancelling the worker (`service.py::_stop`), an
225231

226232
## 6. Technical Specification
227233

228-
### 6.1 Pause → suspend (initial run)
234+
### 6.1 Submit supersedes stale pauses
235+
- `BackgroundExecutionService.submit` calls `supersede_suspended_runs(flow_id, user_id)` after `create_job` (so idempotent retries still return the existing job) and before enqueue: every `SUSPENDED` workflow job of the same flow + user is cancelled through the existing `_cancel_suspended` path (status `CANCELLED`, checkpoint deleted, pending cleared, `run_cancelled` event, bus closed).
236+
- `DurableServeWorkflowHost.submit_background` mirrors it flow-scoped via `SqliteDurableJobStore.suspended_job_ids_for_flow` (STOP signal, task cancel, `CANCELLED`, pending metadata cleared).
237+
- Frontend: the canvas badge auto-open is keyed by `request_id`, so the superseding run's new pause reopens a dismissed popover; the 5s pending poll converges every surface onto the single remaining pause.
238+
239+
### 6.2 Pause → suspend (initial run)
229240
- The graph consults a pause probe at each layer boundary; on a pending pause it snapshots itself, writes a `checkpoint` row (always-writable schema), and raises `GraphPausedException`.
230241
- The build seam (`api/build.py`) catches it, persists the human-input card to history, emits `human_input_required`, and ends the stream without `on_end`.
231242
- The runner marks the job `SUSPENDED` (`services/background_execution/runner.py`).
232243

233-
### 6.2 Resume (single-flight)
244+
### 6.3 Resume (single-flight)
234245
- `POST /api/v2/workflows/{job_id}/resume` with `{request_id, decision}`.
235246
- The route re-enforces `FlowAction.EXECUTE` (`ensure_resume_execute_permission`) before applying the decision — job ownership alone is not enough once shared access has been revoked.
236247
- `claim_suspended_for_resume` performs the atomic status claim; failure → `NOT_RESUMABLE`.
@@ -253,7 +264,7 @@ Drain the queue **inline** after cancelling the worker (`service.py::_stop`), an
253264
# restore built vertices; re-run only non-input predecessors of the gated vertex
254265
```
255266

256-
### 6.3 Trace continuity (initial run)
267+
### 6.4 Trace continuity (initial run)
257268
- `build_graph_from_data` (`api/utils/flow_utils.py`) pins the run id before tracing starts:
258269
```python
259270
if (caller_run_id := kwargs.get("run_id")) is not None:
@@ -262,15 +273,15 @@ Drain the queue **inline** after cancelling the worker (`service.py::_stop`), an
262273
```
263274
- `create_graph` forwards `run_id=str(job_id) if job_id is not None else run_id` to both `build_graph_from_data` and `build_graph_from_db`.
264275

265-
### 6.4 Gate span recording
276+
### 6.5 Gate span recording
266277
- `TracingService.record_event_span(span_id, name, outputs, span_type="chain")` writes a standalone start+end span into the active trace for every ready tracer; deactivated/contextless calls are no-ops.
267278

268-
### 6.5 Frontend (reads backend spans)
279+
### 6.6 Frontend (reads backend spans)
269280
- `useGetTraceQuery``GET /api/v1/monitor/traces/{id}` returns the span tree.
270281
- `TraceDetailView.tsx`: renders the backend gate span; `backendHasGate` dedup prevents a duplicate synthetic node; `executedOutputSpans` bridges Chat Output from live `flowPool` only during the resume window (deduped by name).
271282
- `hitlStore.ts`: in-memory only (`pending` slot); `localStorage`/`persist` removed.
272283

273-
### 6.6 lfx serve durable mode (LE-1695)
284+
### 6.7 lfx serve durable mode (LE-1695)
274285

275286
Bare `lfx serve` gains the same background + HITL contract without a database, backed by the single-node SQLite substrate:
276287

@@ -282,7 +293,7 @@ Bare `lfx serve` gains the same background + HITL contract without a database, b
282293
- **Opt-in**: `LFX_SERVE_DURABLE_DB=<path>`. Unset → serve stays stateless and `mode: background` keeps its 422; workers in multi-worker mode inherit the env var and share the DB file (a stop cancels the run only in the worker executing it).
283294
- **Proof**: `src/lfx/tests/unit/cli/test_serve_durable.py` — background echo completes with the sync-shaped response; a HITL flow suspends, exposes its pending request, resumes only the approved branch, and resumes to completion on a fresh app instance over the same DB file (restart equivalence). The `{job_id}/events` SSE stream replays a completed run, surfaces the `human_input_request` on a suspend, and — reconnected with `Last-Event-ID` after a resume — delivers only the post-resume `human_input_decision` + `end` frames.
284295

285-
### 6.7 Component map (quick index)
296+
### 6.8 Component map (quick index)
286297

287298
| Concern | Where | Key symbols |
288299
|---------|-------|-------------|

src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -897,19 +897,19 @@
897897
"dependencies": [
898898
{
899899
"name": "langchain",
900-
"version": "1.3.11"
900+
"version": "1.3.13"
901901
},
902902
{
903903
"name": "langgraph",
904-
"version": "1.2.8"
904+
"version": "1.2.9"
905905
},
906906
{
907907
"name": "lfx",
908908
"version": null
909909
},
910910
{
911911
"name": "langchain_core",
912-
"version": "1.4.8"
912+
"version": "1.4.9"
913913
}
914914
],
915915
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Content Aggregator.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,19 +1467,19 @@
14671467
"dependencies": [
14681468
{
14691469
"name": "langchain",
1470-
"version": "1.3.11"
1470+
"version": "1.3.13"
14711471
},
14721472
{
14731473
"name": "langgraph",
1474-
"version": "1.2.8"
1474+
"version": "1.2.9"
14751475
},
14761476
{
14771477
"name": "lfx",
14781478
"version": null
14791479
},
14801480
{
14811481
"name": "langchain_core",
1482-
"version": "1.4.8"
1482+
"version": "1.4.9"
14831483
}
14841484
],
14851485
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,19 +444,19 @@
444444
"dependencies": [
445445
{
446446
"name": "langchain",
447-
"version": "1.3.11"
447+
"version": "1.3.13"
448448
},
449449
{
450450
"name": "langgraph",
451-
"version": "1.2.8"
451+
"version": "1.2.9"
452452
},
453453
{
454454
"name": "lfx",
455455
"version": null
456456
},
457457
{
458458
"name": "langchain_core",
459-
"version": "1.4.8"
459+
"version": "1.4.9"
460460
}
461461
],
462462
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Deep Research Agent.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,19 +1224,19 @@
12241224
"dependencies": [
12251225
{
12261226
"name": "langchain",
1227-
"version": "1.3.11"
1227+
"version": "1.3.13"
12281228
},
12291229
{
12301230
"name": "langgraph",
1231-
"version": "1.2.8"
1231+
"version": "1.2.9"
12321232
},
12331233
{
12341234
"name": "lfx",
12351235
"version": null
12361236
},
12371237
{
12381238
"name": "langchain_core",
1239-
"version": "1.4.8"
1239+
"version": "1.4.9"
12401240
}
12411241
],
12421242
"total_dependencies": 4
@@ -1966,19 +1966,19 @@
19661966
"dependencies": [
19671967
{
19681968
"name": "langchain",
1969-
"version": "1.3.11"
1969+
"version": "1.3.13"
19701970
},
19711971
{
19721972
"name": "langgraph",
1973-
"version": "1.2.8"
1973+
"version": "1.2.9"
19741974
},
19751975
{
19761976
"name": "lfx",
19771977
"version": null
19781978
},
19791979
{
19801980
"name": "langchain_core",
1981-
"version": "1.4.8"
1981+
"version": "1.4.9"
19821982
}
19831983
],
19841984
"total_dependencies": 4
@@ -2720,19 +2720,19 @@
27202720
"dependencies": [
27212721
{
27222722
"name": "langchain",
2723-
"version": "1.3.11"
2723+
"version": "1.3.13"
27242724
},
27252725
{
27262726
"name": "langgraph",
2727-
"version": "1.2.8"
2727+
"version": "1.2.9"
27282728
},
27292729
{
27302730
"name": "lfx",
27312731
"version": null
27322732
},
27332733
{
27342734
"name": "langchain_core",
2735-
"version": "1.4.8"
2735+
"version": "1.4.9"
27362736
}
27372737
],
27382738
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@
700700
},
701701
{
702702
"name": "cryptography",
703-
"version": "48.0.1"
703+
"version": "49.0.0"
704704
},
705705
{
706706
"name": "langchain_chroma",
@@ -1358,19 +1358,19 @@
13581358
"dependencies": [
13591359
{
13601360
"name": "langchain",
1361-
"version": "1.3.11"
1361+
"version": "1.3.13"
13621362
},
13631363
{
13641364
"name": "langgraph",
1365-
"version": "1.2.8"
1365+
"version": "1.2.9"
13661366
},
13671367
{
13681368
"name": "lfx",
13691369
"version": null
13701370
},
13711371
{
13721372
"name": "langchain_core",
1373-
"version": "1.4.8"
1373+
"version": "1.4.9"
13741374
}
13751375
],
13761376
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@
626626
},
627627
{
628628
"name": "langchain_core",
629-
"version": "1.4.8"
629+
"version": "1.4.9"
630630
},
631631
{
632632
"name": "pydantic",
@@ -1310,19 +1310,19 @@
13101310
"dependencies": [
13111311
{
13121312
"name": "langchain",
1313-
"version": "1.3.11"
1313+
"version": "1.3.13"
13141314
},
13151315
{
13161316
"name": "langgraph",
1317-
"version": "1.2.8"
1317+
"version": "1.2.9"
13181318
},
13191319
{
13201320
"name": "lfx",
13211321
"version": null
13221322
},
13231323
{
13241324
"name": "langchain_core",
1325-
"version": "1.4.8"
1325+
"version": "1.4.9"
13261326
}
13271327
],
13281328
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Hybrid Search RAG.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@
269269
},
270270
{
271271
"name": "langchain_core",
272-
"version": "1.4.8"
272+
"version": "1.4.9"
273273
},
274274
{
275275
"name": "pydantic",
@@ -1230,11 +1230,11 @@
12301230
"dependencies": [
12311231
{
12321232
"name": "astrapy",
1233-
"version": "2.3.0"
1233+
"version": "2.3.1"
12341234
},
12351235
{
12361236
"name": "langchain_core",
1237-
"version": "1.4.8"
1237+
"version": "1.4.9"
12381238
},
12391239
{
12401240
"name": "lfx_datastax",
@@ -2486,19 +2486,19 @@
24862486
"dependencies": [
24872487
{
24882488
"name": "langchain",
2489-
"version": "1.3.11"
2489+
"version": "1.3.13"
24902490
},
24912491
{
24922492
"name": "langgraph",
2493-
"version": "1.2.8"
2493+
"version": "1.2.9"
24942494
},
24952495
{
24962496
"name": "lfx",
24972497
"version": null
24982498
},
24992499
{
25002500
"name": "langchain_core",
2501-
"version": "1.4.8"
2501+
"version": "1.4.9"
25022502
}
25032503
],
25042504
"total_dependencies": 4
@@ -3246,11 +3246,11 @@
32463246
"dependencies": [
32473247
{
32483248
"name": "astrapy",
3249-
"version": "2.3.0"
3249+
"version": "2.3.1"
32503250
},
32513251
{
32523252
"name": "langchain_core",
3253-
"version": "1.4.8"
3253+
"version": "1.4.9"
32543254
},
32553255
{
32563256
"name": "lfx_datastax",

src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -430,19 +430,19 @@
430430
"dependencies": [
431431
{
432432
"name": "langchain",
433-
"version": "1.3.11"
433+
"version": "1.3.13"
434434
},
435435
{
436436
"name": "langgraph",
437-
"version": "1.2.8"
437+
"version": "1.2.9"
438438
},
439439
{
440440
"name": "lfx",
441441
"version": null
442442
},
443443
{
444444
"name": "langchain_core",
445-
"version": "1.4.8"
445+
"version": "1.4.9"
446446
}
447447
],
448448
"total_dependencies": 4
@@ -1856,19 +1856,19 @@
18561856
"dependencies": [
18571857
{
18581858
"name": "langchain",
1859-
"version": "1.3.11"
1859+
"version": "1.3.13"
18601860
},
18611861
{
18621862
"name": "langgraph",
1863-
"version": "1.2.8"
1863+
"version": "1.2.9"
18641864
},
18651865
{
18661866
"name": "lfx",
18671867
"version": null
18681868
},
18691869
{
18701870
"name": "langchain_core",
1871-
"version": "1.4.8"
1871+
"version": "1.4.9"
18721872
}
18731873
],
18741874
"total_dependencies": 4

src/backend/base/langflow/initial_setup/starter_projects/Knowledge Retrieval.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@
706706
},
707707
{
708708
"name": "cryptography",
709-
"version": "48.0.1"
709+
"version": "49.0.0"
710710
},
711711
{
712712
"name": "langchain_chroma",

0 commit comments

Comments
 (0)