docs(integrations): headless connection resolver and deployment reference (INT-13) - #14954
docs(integrations): headless connection resolver and deployment reference (INT-13)#14954erichare wants to merge 7 commits into
Conversation
INT-13 documents the headless half of the connection contract, which had no page at all: how a handle becomes an LF_CONNECTION__* key, what the bare-token and JSON wire formats permit and refuse, how lfx run pre-flights a missing connection, how lfx serve injects one per request, how a host registers its own BaseConnectionResolverService without Langflow's database, and what a project deployment artifact declares in required_connections. The samples under docs/docs/Lfx/samples/connections are the page's source of truth: the page embeds them verbatim with raw-loader and the lfx tests execute the same files, so the documentation cannot describe code that does not run. They cover a connection-backed action component, environment-backed resolution through both injection channels, a secret-manager resolver (generic callable plus a dependency-free mounted-secrets implementation), the lfx.toml registration, and the serve request shape. connection-oauth.mdx was orphaned from the sidebar since it was added; it is registered here so the two connection pages form a navigable pair. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The reference samples are code operators copy into production, so CI executes them rather than only rendering them. The lfx suites load each sample by path and assert the invariants that make it safe as well as the behavior it demonstrates: the headless-operator deny floor, the refusal of refresh tokens and other long-lived secrets, typed auth-expired and scope-missing failures, the mounted-secrets path-containment check, and fail-closed lfx.toml registration. test_serve_app_connections.py drives the real serve app with the sample flow so the documented request shape is exercised end to end: a request-scoped credential resolves, an absent one surfaces as a 500 carrying the sanitized "could not be resolved" text (lfx serve has no pre-flight and no machine readable error code today), an ambient environment value loses under --no-env-fallback, and a request scope does not leak into the next request. Two drift tests pin the page to the code it describes: the error-code table against INTEGRATION_ERROR_CODES, and the artifact manifest example against ProjectArtifactRequiredConnection and the schema_version 4 rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…y paths The headless reference is only useful where a reader already is. lfx-run gains the connection pre-flight behavior and the exact ConnectionUnresolvedError text; lfx-serve documents global_vars, --no-env-fallback and --reset-environ, which had no documentation anywhere under docs/; deployment-wxo names the LF_CONNECTION__<PROVIDER>__<NAME> variable a watsonx Orchestrate connection must carry; configuration-global-variables introduces LANGFLOW_REQUEST_VARIABLES and the LF_CONNECTION__* family and separates LANGFLOW_FALLBACK_TO_ENV_VAR from --no-env-fallback, which are different switches. PLUGGABLE_SERVICES.md and lfx.toml.example gain the connection_resolver_service entry they were missing, including the fail-closed rules and the deny floor a host implementation must apply. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docusaurus.config.js sets onBrokenLinks to "throw", and the docs site builds the released version snapshots alongside `next`. An absolute slug link such as /lfx-connections resolves inside a released version too, where the page does not exist, so `npm run build` failed on five links. Relative .mdx links are resolved per version, which is the convention the rest of docs/docs already uses for cross-directory references. Verified with a full `cd docs && npm ci && npm run build`: SUCCESS, zero broken links (the remaining broken-anchor warnings are pre-existing, in the 1.9.0 and 1.10.0 snapshots). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… expiry Two pieces of shipped reference material were wrong, and neither test caught it. The project-artifact manifest example omitted the `name` key that `deployment_artifacts/builder.py` emits in every `flows[]` entry, so an operator who modelled a manifest reader on the page would reject every real .lfpkg. The drift test asserted the doc's key sets against hardcoded literals, so it agreed with the wrong shape; it now builds a real connection-referencing artifact with the builder and compares the documented key sets against the manifest that build produces. Removing the `name` line from the page fails the test. serve_request.sh hardcoded `expires_at: "2026-01-01T00:00:00+00:00"`, already in the past, so the copy-pasteable JSON-credential request failed with auth-expired on every attempt. The expiry is now computed at send time from an overridable TTL. It was the one sample no test executed, so two tests close that gap: one runs the script with a curl stub and resolves both request bodies through the env resolver, the other refuses any literal timestamp under the samples directory, since that is the class of defect that goes stale silently. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- sidebars.js keeps only this ticket's own entry. `Develop/connection-oauth` belongs to the PR that introduced the page (#14935); registering it here too would collide with that PR and list one doc twice in one sidebar, which Docusaurus rejects. The page stays reachable through the links this PR adds. - The section appended to connection-oauth.mdx now has a heading naming its subject instead of a generic "See also", so the later tickets that append to the same file do not all land on the same heading. - LANGFLOW_REQUEST_VARIABLES is a JSON-encoded string, not a nested object: runtime_variables.py json.loads() the value and logs-and-drops anything that is not a string containing an object. Both pages now say so. - The appended global-variables heading gets the blank line the rest of the file uses. - sample_loader fails loudly when docs/ is present but the samples are not. Skipping all seventeen sample tests on a moved directory is the opposite of the drift protection they exist for; an sdist-only tree without docs/ still skips. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
✅ Test Coverage AdvisorNo source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉
|
This comment has been minimized.
This comment has been minimized.
The lfx reference-sample test parsed the sample ``lfx.toml`` with a bare ``import tomllib``, which is stdlib only from 3.11, so the LFX Tests - Python 3.10 job failed with ModuleNotFoundError. Use the same guarded import the rest of lfx uses (manifest.py, config_discovery.py); lfx already depends on tomli unconditionally, so the fallback always resolves. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Build successful! ✅ |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## feat/int-5-oauth-broker #14954 +/- ##
===========================================================
+ Coverage 65.63% 66.71% +1.07%
===========================================================
Files 2519 2526 +7
Lines 263230 263493 +263
Branches 39299 36899 -2400
===========================================================
+ Hits 172771 175787 +3016
+ Misses 88239 85488 -2751
+ Partials 2220 2218 -2
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
docs(integrations): headless connection resolver and deployment reference (INT-13)
Base:
feat/int-5-oauth-broker(#14935) · Branch:feat/int-13-headless-reference· One PR.Summary
docs/docs/Lfx/lfx-connections.mdx— the headless half of the connection contract, which had no documentation anywhere: howprovider/namebecomesLF_CONNECTION__<PROVIDER>__<NAME>, the bare-token and JSON wire formats and the fields they refuse, thelfx runpre-flight and its exact error text, per-request injection underlfx serve, implementing aBaseConnectionResolverServicewithout Langflow's database, the sanitized error-code table, and therequired_connectionsa project deployment artifact declares. Registered in the LFX sidebar.docs/docs/Lfx/samples/connections/— the page embeds them verbatim withraw-loaderand the lfx tests execute the same files, so the page cannot document code that does not run:connection_action_component.py— aConnectionRefInputaction that resolves a lease and returns account and scopes, never a token, plus the flowlfx runandlfx serveload.env_resolver_host.py— environment-backed resolution through both injection channels, and the JSON credential builder.secret_manager_resolver.py— a generic callable-backedSecretManagerConnectionResolverplusMountedSecretsConnectionResolver, a dependency-free implementation over a mounted secrets directory (Kubernetes/Docker secrets), with AWS and Vault adapters shown in comments. No new dependencies.lfx.tomlandserve_request.sh— the registration and the request shape.test_reference_samples.py(19 tests),test_serve_app_connections.py(4 tests driving the real serve app), and two drift tests that pin the page to the code: the error-code table againstINTEGRATION_ERROR_CODES, and the manifest example against a manifestbuild_project_artifactactually produces (key sets and theschema_version4 rule are read off the builder's output, not restated).serve_request.shis executed too: a test runs it with acurlstub and resolves both request bodies through the env resolver, and a second test refuses any literal timestamp under the samples directory, so no sample can ship an expiry that goes stale.lfx-run.mdx(connection pre-flight and missing-connection text),lfx-serve.mdx(global_vars,--no-env-fallback,--reset-environ, none of which were documented underdocs/),deployment-wxo.mdx(the watsonx Orchestrate connection-variable naming rule),configuration-global-variables.mdx(LANGFLOW_REQUEST_VARIABLES, theLF_CONNECTION__*family, and the distinction betweenLANGFLOW_FALLBACK_TO_ENV_VARand--no-env-fallback),environment-variables.mdxpointer,lfx-devops-sdk.mdxsee-also, a new## Headless connection resolutionsection appended toconnection-oauth.mdx(a heading naming its subject, so the later INT/TRG tickets appending to that file do not all land on one generic## See also),src/lfx/PLUGGABLE_SERVICES.mdandsrc/lfx/lfx.toml.example(connection_resolver_service, which both omitted).[tool.ruff.lint.per-file-ignores]entry so the samples (loaded by path, not a package) do not tripINP001.docs/sidebars.jsgains exactly one entry, this page's.docs/docs/Develop/connection-oauth.mdxis still an orphan in the sidebar — it belongs to feat(integrations): broker delegated OAuth connections #14935, and registering it from here would collide with that PR (a doc listed twice in one sidebar is a Docusaurus error). It is reachable through the links this PR adds; feat(integrations): broker delegated OAuth connections #14935 should add its own sidebar line.What the documentation deliberately does not promise
Each of these is stated in the page as current behavior, and pinned by a test where testable:
lfx servehas no connection pre-flight and no machine-readable error code. A missing connection surfaces when the component awaits its lease, as HTTP 500 whoseresultisConnectionUnresolvedError's sanitized message. The serve test asserts on that text. A typed sanitized error event for serve is still owed by INT-2 (contract section 11); this PR documents what ships.--no-env-fallbackislfx serve-only. Forlfx runonly--check-variablesand the exact-key /x-langflow-global-var-*env lookups are documented.LANGFLOW_REQUEST_VARIABLESis a serve/TRM channel, not anlfx runone.VariableServicehonors the blob at resolution time, butvalidate_connection_refs_for_envreads onlygraph.context['request_variables']and env keys, so a credential carried only in that blob passes at runtime and fails the default pre-flight. Called out in a:::note; the fix belongs in feat(integrations): add lfx connection resolution contract #14919.required_connectionsis informational for deployment tooling today. Verified in the EE and CP trees:langflow_ctl/lfpkg.pySUPPORTED_SCHEMA_VERSIONS = frozenset({1, 2, 3})refuses a v4 artifact, and the control plane'scheckVariableAvailabilityhandles variable names only. Stated plainly rather than implied away.POST /flows/{id}/runwithglobal_vars(src/lfx/src/lfx/cli/runtime_variables.py,src/lfx/src/lfx/services/variable/request_scope.py). The page documents that a wxO connection variable must be named exactlyConnectionRef.env_key(), and that explicit keys beat theLANGFLOW_REQUEST_VARIABLESblob. No repository test exercises a live TRM; confirming it end to end is an INT-14 checklist item.DatabaseConnectionResolverService, notEnvConnectionResolver; the page says which host runs which resolver so the env-injection guidance is not read as applying to that plane.Verification
Run from
src/lfxin its isolated environment (uv sync --dev --extra otel), and from the repo root for the backend test.Not exercised: a live watsonx Orchestrate run, and any multi-worker demonstration of request-scope isolation (
test_serve_env_isolation_integration.pyis CI-skipped). Neither is reachable from this repository's test environment.Reviewers
Requesting the lfx owner and the serving-plane maintainers for the QA item "Serving owners review the documentation"; the host/resolver table and the EE/CP consumer caveat are the parts that need their sign-off.
Post-review changes
LFX Tests - Python 3.10CI failure:test_reference_samples.pyimportedtomllibunguarded (stdlib only from 3.11); it now falls back totomli, matchinglfx/extension/manifest.pyandlfx/services/config_discovery.py. No CodeRabbit review was posted (auto-review is disabled for non-release-*base branches).