Skip to content

fix(model-provider): guard the Ollama node's config before the run (#1302) - #1347

Merged
rafaelgiln merged 1 commit into
mainfrom
fix/issue-1302-ollama-node-config-revert
Aug 6, 2026
Merged

fix(model-provider): guard the Ollama node's config before the run (#1302)#1347
rafaelgiln merged 1 commit into
mainfrom
fix/issue-1302-ollama-node-config-revert

Conversation

@rafaelgiln

Copy link
Copy Markdown
Collaborator

Refs #1302. Deliberately does not close it — see What stays open.

Verdict: the node loses its configuration before the run. It is not the budget.

#1302 reads the failure as a 180 s budget too short for CPU inference, and its directive asks for a measured replacement. The artifacts refute that three independent ways, so step 2 of the directive should not be actioned:

Evidence Measurement
The retry — same run, same runner 180 445 ms failed → 5 644 ms passed (07-30) · 180 482 ms failed → 5 559 ms passed (08-05)
Green dailies, attempt 0 on a freshly created (therefore cold) Ollama container 5 408 ms (08-04) · 6 503 ms (08-03) — there is no cold-start penalty
div-chat-message in the dev18 bundle wraps chat-message-${sender_name}-${index} — it counts the user's bubble too

So the cost is 5–6.5 s cold or warm, and the failure is binary: either ~5 s, or nothing at all for 180 s. That is not a distribution with a long tail. And "0 elements 183 times" does not mean the model was slow — it means the message the test typed never rendered.

The failing attempt's error-context.md says why. The Ollama node on the canvas, behind the open Playground:

Model Name*
  combobox: "Select an option"        # empty
Ollama API URL
  textbox: "http://localhost:11434"   # the component default

The daily injects OLLAMA_BASE_URL_FROM_LANGFLOW: http://ollama:11434, and the step that selects the model passed in 1 059 ms, asserting value-dropdown-dropdown_str_model_name. So the model was selected, verified, and then lost. Model Name is required ⇒ the run cannot start. Everything agrees: the token artifact holds one flow trace for two attempts, and the failing attempt logged zero backend errors in 191 s.

This also confirms the fork the spec doc named after #931"the root cause to chase is whether the run starts at all versus the Ollama node failing to build". It is the first.

The fix

The mechanism is already documented in this repo, in helpers/flows/wait-for-flow-save-settled.ts: PATCH /api/v1/flows/{id} has no version check and the frontend applies whichever response lands last, so a stale autosave overwrites the selection in both the store and the database (the root of #358, #357, #995). The spec already called that barrier — it proves PATCH quiescence and says nothing about what persisted, and the Playground opens after it.

tests/helpers/flows/node-config-guard.ts adds the two halves the barrier cannot give:

  • waitForNodeConfigSettled — after selecting, re-reads the widget past the quiet window and re-applies the selection at most once. Bounded and deliberate: this recovers a write the product lost after it was applied and asserted; it is not a blind retry of a failed interaction.
  • assertNodeConfigHeld — runs immediately before button-send, never earlier (the revert was observed with the Playground already open), and fails naming both fields it read. A persistent revert still fails the test — in ~1 s with the cause named, instead of as a toHaveCount timeout three layers downstream, which is precisely how this got filed as a budget problem.

It reads the WIDGET, not the API, and that is measured rather than conventional: the run is dispatched as POST /api/v2/workflows with a 66 801-byte body — the frontend's in-memory graph, not a reference to the persisted flow. A guard querying GET /api/v1/flows/{id} could pass while the run executes the reverted state.

The 180 s budget is unchanged on purpose. The directive asks for a measured budget if the budget is the cause; it is not. The numbers are recorded in the spec doc so the question is not reopened: 5 408 / 5 559 / 5 644 / 6 503 ms across four dailies, and the run request itself took 4 063 ms.

The four earlier occurrences (a deliverable of the issue)

They are two causes, not one unsettled month — the line number separates them, because #931 rebuilt the file:

Date Line Signature Cause
07-15, 07-22 172 expect(locator).toBeVisible() failed same mechanism as now, pre-#931 assertion shape
07-23, 07-24 172 page.waitForSelector: Timeout 30000ms differentlfx-ollama absent from the image; already covered by the isProviderComponentAvailable pre-flight
07-30, 08-05 224 expect(locator).toHaveCount(expected) failed same mechanism, post-#931

Validation

npm run test:units          502 passed  (+9 for the new guard)
npm run typecheck           clean
eslint (touched files)      0 errors
orphans after the runs      0 test_* MCP servers, 0 "New Flow*" flows

Force-fails — executed, both directions:

  • FF-1classifyConfigOutcome mutated to always return "held"2 of the 9 unit tests fail (the reverted states…, a DIFFERENT model…). Reverted, 0 markers left.
  • FF-2a (browser, real DOM) — the guard pointed at a value not present ⇒ throws the attributed message: "…was set to "VALOR-QUE-NAO-EXISTE-NO-DOM" … but it now reads "MCP Server" … Do NOT read this as a slow model or a short timeout."
  • FF-2b (the other direction, which matters as much) — the same guard pointed at the real value ⇒ 1 passed (36.1 s), no false fire.

FF-2 was run against mcp-server-tab.spec.ts rather than this spec, because this spec cannot run on an arm64 Mac at all — see below. The probe was reverted and the tree verified clean.

This spec is CI-only on arm64 (measured on 1.12.0.dev18)

Recorded in the spec doc's Preconditions so the next person does not spend the cycle. A dockerized Langflow could not reach any Ollama in either topology (host instance via host.docker.internal; a sibling ollama/ollama container on a shared network with the CI's exact allowlist). Setting LANGFLOW_SSRF_ALLOWED_HOSTS makes it worse: without it the layer answers resolves to blocked IP address(es) (so the name resolved), with it the same name answers DNS resolution failed — for a name getent and socket.getaddrinfo resolve inside that same container. The amd64 image cannot run here (Fatal glibc error: CPU does not support x86-64-v3), and start-langflow-pip.sh installs the stable release rather than the nightly line.

Worth flagging on its own: inside one container with the allowlist set, validate_model_provider_key("Ollama", …) called directly validates and connects, while POST /api/v1/models/validate-provider with the same argument returns DNS resolution failed. The allowlist is honoured by the library and not by the endpoint.

What stays open

  • @stable is NOT restored. test.fixme is lifted so the test runs again, but the mechanism fired on 2 of 26 dailies and cannot be reproduced locally, so neither a local green nor a single CI green is admissible evidence. The gate is written into the spec doc: several consecutive green manual.yml dispatches with -f retries=0. [Daily #1296] ollama-provider — the flow produces no chat message within 180 s (recurrent 2× same-signature, 6 hits in the 30-day window) #1302 stays open for that, which is why this PR says Refs and not Closes.

    gh workflow run manual.yml --repo oriontech-me/langflow-e2e \
      --ref fix/issue-1302-ollama-node-config-revert \
      -f langflow_target=latest -f langflow_image=nightly \
      -f test_grep="Ollama" -f retries=0
  • Which write reverts the node is not pinned. A stale autosave and the bulk DELETE /api/v1/flows/ that appears mid-test under actualWorkers: 2 are both candidates, and the artifacts do not separate them. The spec doc says so in those words rather than picking a culprit — if it turns out to be the delete, the fix belongs to the wiper, not here (triage-verdicts.md verdict 5).

🤖 Generated with Claude Code

…1302)

The quarantined test waited 180 s for `div-chat-message` and saw 0 elements 183
times. #1296 read that as a budget too short for CPU inference and #1302's
directive asks for a measured replacement. The artifacts refute the budget three
independent ways:

  the retry, same run, same runner   180 445 ms failed -> 5 644 ms passed (07-30)
                                     180 482 ms failed -> 5 559 ms passed (08-05)
  green dailies, attempt 0 on a      5 408 ms (08-04), 6 503 ms (08-03)
    FRESHLY created (cold) container   => there is no cold-start penalty
  `div-chat-message` in dev18        wraps chat-message-${sender_name}-${index},
                                     so it counts the USER's bubble too

So the cost is 5-6.5 s cold or warm and the failure is binary: either ~5 s, or
nothing at all for 180 s. And "0 elements" does not mean the model was slow — it
means the message the test typed never rendered.

The failing attempt's DOM says why: the Ollama node reverted to its DEFAULTS —
`Model Name` back to "Select an option", `Ollama API URL` back to
`http://localhost:11434` while the daily injects `http://ollama:11434`. The model
had been selected AND asserted one step earlier (that step passed in 1 059 ms).
`Model Name` is required, so the run could never start; consistently the token
artifact holds ONE flow trace for TWO attempts and the failing attempt logged
zero backend errors in 191 s.

The mechanism is the one `wait-for-flow-save-settled.ts` already documents:
`PATCH /api/v1/flows/{id}` has no version check and the frontend applies whichever
response lands LAST, so a stale autosave overwrites the store and the database
(#358/#357/#995). The spec called that barrier already — it proves PATCH
quiescence and nothing about what persisted, and the Playground opens after it.

`node-config-guard.ts` adds the two halves the barrier cannot give:

- `waitForNodeConfigSettled` re-reads the widget after the quiet window and
  re-applies the selection at most once. Bounded and deliberate: this recovers a
  write the product lost after it was applied and asserted, it is not a blind
  retry of a failed interaction.
- `assertNodeConfigHeld` runs immediately before `button-send` — never earlier,
  because the revert was observed with the Playground already open — and fails
  naming both fields it read. A persistent revert still fails the test; it fails
  in ~1 s with the cause named instead of as a `toHaveCount` timeout three layers
  downstream, which is exactly how this got filed as a budget problem.

It reads the WIDGET, not the API, and that is measured rather than conventional:
the run is dispatched as `POST /api/v2/workflows` with a 66 801-byte body — the
frontend's in-memory graph. A guard querying `GET /api/v1/flows/{id}` could pass
while the run executes the reverted state.

The 180 s budget is deliberately unchanged, and the measurements above are
recorded in the spec doc so the question is not reopened.

`test.fixme` is lifted so the test runs again. **`@stable` stays off**: the
mechanism fired on 2 of 26 dailies and this spec cannot be reproduced locally at
all on arm64 (the SSRF allowlist breaks the very hostname it should allow; the
amd64 image will not run; the pip script installs the stable release), so only a
`manual.yml` dispatch is admissible evidence and #1302 stays open for it.

Also recorded in the spec doc: the four earlier occurrences in the window are TWO
causes, not one unsettled month — 07-23/24 are the `lfx-ollama` packaging
regression already covered by the pre-flight, 07-15/22 are this same mechanism
under the pre-#931 assertion shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rafaelgiln
rafaelgiln merged commit 5f8b4d3 into main Aug 6, 2026
@Victor-w-Madeira
Victor-w-Madeira deleted the fix/issue-1302-ollama-node-config-revert branch August 8, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant