mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 16:12:19 +08:00
* feat(bundles): add lfx-bundles metapackage skeleton
Creates the manifest-less lfx-bundles distribution (the langchain-community
model) that the long-tail providers will move into. Empty skeleton for now;
the bulk move (scripts/migrate/consolidate_bundles.py) populates the provider
folders + per-provider extras later.
- src/bundles/lfx-bundles: a single pyproject declaring the lfx.bundles
entry-point (lfx_bundles = "lfx_bundles"), an lfx>=1.10.0,<2.0.0 pin, and a
generated (currently empty) `all` extra; plus a bare lfx_bundles namespace
package and a README documenting the model + install stories
- wired into the root workspace via the existing bundle marker blocks:
dep lfx-bundles[all]>=1.0,<2.0, uv source, and member
- hyphen dir name so release.yml's src/bundles/*/pyproject.toml glob builds
it with zero workflow change
Verified: the wheel builds (entry_points.txt carries the lfx.bundles group);
load_lfx_bundles_extensions (PR-1) discovers the entry point and the empty
skeleton registers zero providers with no error.
* feat(bundles): consolidate first long-tail tranche into lfx-bundles
Adds scripts/migrate/consolidate_bundles.py (the inverse of port_bundle.py --
moves in-tree providers into the manifest-less lfx-bundles metapackage) and
runs it on a verified 5-provider tranche: tavily, exa, wikipedia, yahoosearch,
wolframalpha.
Per provider the script:
- moves src/lfx/src/lfx/components/<p>/ -> lfx_bundles/<p>/ (lowercase names);
- leaves a fail-soft import shim (first line `# lfx-bundles-shim`) so
`from lfx.components.<p> import X` keeps working when lfx-bundles is
installed, and raises an actionable ImportError otherwise;
- merges the provider's third-party deps into a PEP 685-normalized lfx-bundles
extra and regenerates the `all` aggregate. Dep parity holds: `uv sync` is a
no-op because those deps were already pulled via langflow-base[complete];
- appends the 4-entry migration block per Component class (28 entries) so saved
flows referencing lfx.components.<p>.<Class> migrate to
ext:<p>:<Class>@official.
To avoid double registration, the in-tree component walk
(_load_components_dynamically) now skips shimmed provider dirs, and
component_index.json is regenerated (355->348 components, 95->90 modules); the
moved providers load only at @official via lfx.bundles discovery.
Verified: discovery finds all 5 at @official with no errors; shims resolve;
`import lfx.components` still works; the index drops the 7 moved component
entries (residual `tools`-category name refs resolve via the shim); ruff clean.
First tranche proves the engine; the remaining long-tail scales by extending
PROVIDER_DEPS (each provider's deps verified individually -- the careful part).
* feat(bundles): consolidate 30-provider tranche 2 into lfx-bundles
Extends PROVIDER_DEPS with 30 individually-verified providers and runs the
consolidation: vector stores (chroma, clickhouse, couchbase, milvus, mongodb,
pgvector, pinecone, qdrant, supabase, upstash, weaviate), model providers
(groq, mistral, ollama, perplexity, sambanova), and tools/memory/data (apify,
assemblyai, confluence, firecrawl, git, glean, icosacomputing, mem0, needle,
scrapegraph, serpapi, unstructured, youtube, zep).
Dep verification (the careful part): every spec comes from langflow-base's
per-provider extras or its direct dependencies; langchain-community providers
carry the wrapper plus the SDK the wrapper lazy-imports (e.g. pgvector,
atlassian-python-api); requests is declared explicitly where imported (it is
only transitive in today's env); pinecone keeps its python_version<'3.14'
marker verbatim. Tranche excludes: providers with langflow imports (vlmrun),
provider-specific lfx.base dirs (composio/huggingface/langwatch), case-
sensitive names (FAISS/Notion), the openai-SDK family (azure/aiml/deepseek/
litellm/lmstudio/novita/openrouter/vllm/xai/cometapi -- cleaner after PR-8),
and the partner set.
Dep parity verified at the resolution level: the uv.lock diff is +220 lines of
lfx-bundles extras metadata with ZERO packages added or removed (`name =` diff
empty), so pip install langflow resolves the identical set.
Also: 192 append-only migration entries (48 classes x 4, zero bare-name
ambiguities); component_index.json regenerated 348->300 components / 90->60
modules (exactly the moved set, no stale standalone entries); mongodb_atlas.py
SLF001 per-file-ignore and the mem0/mongodb detect-secrets baseline entries
migrated to the new paths (lint/secrets exceptions travel with moved files);
35 bundles now discover at @official with 55 components; shims verified across
categories; 449 extension tests pass.
* feat(bundles): consolidate openai-SDK family tranche 3 into lfx-bundles
10 providers that ride the langchain-openai wrapper, deferred from tranche 2
until the partner graduation settled the openai-SDK dep story: aiml, azure,
cometapi, deepseek, litellm, lmstudio, novita, openrouter, vllm, xai.
Dep verification: every provider declares langchain-openai>=1.1.6; the openai
SDK is declared only where a component imports it directly (aiml, deepseek,
litellm, lmstudio, vllm, xai -- wrapper-transitive elsewhere); requests
declared where imported (cometapi, deepseek, novita, xai); lmstudio's lazy
NVIDIAEmbeddings path gets langchain-nvidia-ai-endpoints~=1.0.0. The litellm
component drives LiteLLM-served endpoints through the OpenAI client and does
NOT import the litellm package -- langflow-base's litellm extra stays put.
These providers use the lazy _dynamic_imports __init__ shape; it survives the
move unchanged (import_mod resolves via __spec__.parent and
lfx.components._importing remains a core helper).
Dep parity: uv.lock diff has zero package additions/removals (all specs
already resolved via langflow-base[complete]).
Also: 56 append-only migration entries (14 classes x 4, zero ambiguities);
component_index.json regenerated 300->286 components / 60->50 modules (exactly
the moved set); detect-secrets baseline re-keyed; 45 bundles now discover at
@official with 69 components; shims verified (azure/xai/litellm/deepseek);
ruff clean.
* fix(ci): nightly bundle rename follows [extras] refs and self-refs
Two gaps in update_bundle_versions.py around extras suffixes, both fatal
or silently wrong for the first nightly carrying the lfx-bundles
metapackage:
- update_root_pyproject_for_bundle's dep regex required the version
specifier immediately after the name, so "lfx-bundles[all]>=1.0,<2.0"
(a MAIN dep) was left unrewritten while the workspace member was
renamed to lfx-bundles-nightly; uv lock then tries to resolve stable
lfx-bundles from PyPI, where it does not exist. The same gap silently
left "lfx-docling[local]>=0.1.0" optional-dep refs pointing at the
stable distribution. The regex now tolerates an [extras] group and
carries it into the replacement.
- rename_bundle_pyproject skipped self-referencing extras, so the
metapackage's generated `all` extra kept 45 "lfx-bundles[<provider>]"
members after the rename, pulling the stable distribution (same
lfx_bundles import package, install collision) once published. Self
refs now follow the rename, idempotently.
Tests drive the real script module, mirroring test_bundle_lfx_pin.py.
This closes the PR-2 audit item flagged when the metapackage was
introduced. The canonical-pre-release cutover would retire the nightly
rename entirely; until it lands, the rename must be correct.
* feat(bundles): graduate partner set to standalone lfx-<provider> packages (#13573)
* feat(bundles): graduate partner set to standalone lfx-<provider> packages
Extracts the five partner/flagship providers into manifest-shipping
distributions: lfx-openai, lfx-anthropic, lfx-amazon, lfx-datastax,
lfx-cohere. Each ships extension.json (lfx.compat ["1"]), a
langflow.extensions entry-point, an lfx>=1.10.0,<2.0.0 pin, and is wired into
the root workspace marker blocks. Zero flow impact by the bundle-name
invariant: ext:<provider>:<Class>@official ids are unchanged.
Mechanics:
- adopts the five-phase scripts/migrate/port_bundle.py from
feat/bundle-mass-extraction (handles shared-base moves, consumer rewrites,
surgical index updates, migration-table appends) and runs it per partner
with --migration-release 1.11.0
- datastax's shared lfx.base.datastax moves into lfx_datastax.base; its
backend unit tests move to src/bundles/datastax/tests (74 pass); its ruff
per-file-ignore and its check_component_env_writes ALLOWLIST entry travel;
10 repo consumers rewritten (incl. the vector_store_rag starter project)
- amazon_bedrock_converse.py legacy langflow.* imports rewritten to the lfx.*
equivalents (thin re-export aliases; behavior-identical) pre-extraction
- runtime deps pinned from langflow-base's extras / direct deps (wrapper-
guaranteed SDKs stay transitive, parity-exact); --remove-base-extra dropped
the openai/anthropic/cohere/aws extras from langflow-base[complete] in favor
of the bundle pins; the astradb extra stays (also used outside datastax)
- in-tree dirs replaced with marker shims pointing at lfx_<p>.components.<p>
(skipped by the in-tree walk; legacy from lfx.components.<p> imports keep
working); lfx/components/__init__.py entries kept, consistent with the
consolidated providers
- validate.py: accept classes whose base is a *derived* Component base
(LCVectorStoreComponent / LCToolComponent / ...) -- they inherit the
class-level outputs declaration and only override the output method, which
the AST-only check could not see; bare Component subclasses keep the strict
build/outputs requirement (+ regression test)
- BUNDLE_API.md changelog entries added for the branch's surface changes: the
lfx.bundles manifest-less discovery group + precedence tier +
bundle-discovery-malformed code (from the foundation PR) and the validator
acceptance above
- 100 append-only migration entries (20 classes x 5 partners x 4 shapes);
component_index.json surgically updated 300->280 components / 60->55
modules, sha256 recomputed and verified
- detect-secrets baseline re-keyed for moved partner files
Dep parity: lock diff adds only the five lfx-* package names; no third-party
package added or removed. One benign transitive re-resolution: anthropic SDK
0.105.2 -> 0.109.0 (allowed by langchain-anthropic's range on both sides).
Verified: all five register at @official via the installed-manifest tier
(extension_id/distribution = lfx-<p>, 20 components); manifest-less
lfx_bundles unchanged (35 bundles, disjoint); all 5 shims import; engine-safe;
`lfx extension validate` passes for all five; 450 extension unit tests, 60
pilot-upgrade migration tests, 74 moved datastax tests pass; ruff clean.
* fix(bundles): shim lfx.base.datastax so stored-flow imports keep resolving
The datastax graduation moved lfx.base.datastax into lfx_datastax.base,
but saved flows and starter templates (Hybrid Search RAG,
TemplateAssistant) embed `from lfx.base.datastax.astradb_base import
AstraDBBaseComponent` inside their stored component code fields, which
is re-executed verbatim at flow build time. Without a shim that import
raises ModuleNotFoundError and the flows fail to build
(test-starter-projects red).
Mirror the lfx.components shim contract: module-aliasing to
lfx_datastax.base, narrow except that only translates a missing bundle
(transitive dep failures re-raise untouched), marker-tagged single-file
dir, removed at M4 together with the components shims.
* chore(bundles): bounded version ranges for curated lfx-* packages (#13576)
langflow's pyproject is the release-coordination point now that lfx and the
lfx-* bundles release independently. Every curated lfx-* dependency declares a
BOUNDED range (>=A,<B), never an exact pin -- exact pins in reusable library
metadata create resolver conflicts for downstreams that depend on a different
version. Exact pins for reproducibility live in uv.lock and the release-build
manifests.
- the 4 pilots + 5 graduated partners: >=0.1.0 -> >=0.1.0,<1.0.0 (bundles
follow their own 0.1.x cadence)
- lfx-bundles[all]>=1.0,<2.0 unchanged (versions with the metapackage
contract)
- the three lfx-docling[...] optional-dependency refs bounded the same way
- policy documented inline in the bundle-deps marker block; the cross-bundle
CI matrix verifies the ranges resolve together across the supported lfx axis
Verified: uv lock resolves with zero package/version changes (bounds are
metadata-only today); the nightly rename's dep regex already matches the
bounded form.
* test(bundles): lock the in-tree shim contract + breakage audit (#13577)
* chore(bundles): bounded version ranges for curated lfx-* packages
langflow's pyproject is the release-coordination point now that lfx and the
lfx-* bundles release independently. Every curated lfx-* dependency declares a
BOUNDED range (>=A,<B), never an exact pin -- exact pins in reusable library
metadata create resolver conflicts for downstreams that depend on a different
version. Exact pins for reproducibility live in uv.lock and the release-build
manifests.
- the 4 pilots + 5 graduated partners: >=0.1.0 -> >=0.1.0,<1.0.0 (bundles
follow their own 0.1.x cadence)
- lfx-bundles[all]>=1.0,<2.0 unchanged (versions with the metapackage
contract)
- the three lfx-docling[...] optional-dependency refs bounded the same way
- policy documented inline in the bundle-deps marker block; the cross-bundle
CI matrix verifies the ranges resolve together across the supported lfx axis
Verified: uv lock resolves with zero package/version changes (bounds are
metadata-only today); the nightly rename's dep regex already matches the
bounded form.
* test(bundles): lock the in-tree shim contract + breakage audit
Locks the five-point contract for the 50 lfx-bundles import shims under
lfx/components/ (45 metapackage + 5 graduated partners):
1. first line is the `# lfx-bundles-shim` marker (keys the in-tree walk's
double-registration skip);
2. a shim dir is exactly one __init__.py -- no impls, no deps;
3. sys.modules module-aliasing (submodule trees resolve);
4. bundle missing -> actionable ModuleNotFoundError whose wording is part
of the contract ("pip install lfx-bundles" / "pip install lfx-<p>");
5. a missing *transitive* dep re-raises untouched.
Test shape (env-adaptive by design -- the lfx test env prunes the venv, so
nothing here assumes bundles are installed):
- source-level sweep of every real shim, parameterized (no imports);
- hermetic mechanism tests against a synthetic shim + synthetic target
(alias-resolves / locked-message / transitive-reraise);
- the walk-skip detector and the marker sweep must agree exactly;
- one adaptive live test on lfx.components.tavily exercising whichever
branch the running env is in.
106 tests pass in the pruned lfx env (where the live test exercises the
bare-engine locked-message branch).
Breakage audit (gh code search, recorded in the PR): ~20-25 public repos
reference lfx.components.*; the majority are langflow forks (vendored tree,
unaffected by packaging). Genuine library consumers exist and the moved
providers have real surface (openai: 11 external repos, datastax: 17) --
covered by the shims wherever bundles are co-installed (every
`pip install langflow`). Decision: deprecation warnings NOT warranted now;
revisit at M4 (shim removal), where one loud minor release before removal
is the right shape.
* test(bundles): extend the shim contract sweep to lfx.base shims
The datastax graduation moved lfx.base.datastax into lfx_datastax.base
and left a module-aliasing shim behind (stored flow code fields embed
the legacy import and are re-executed verbatim at build time). Sweep
lfx/base for marker shims with the same source-level contract as the
components sweep: one-file stub, module-aliasing to the bundle's base
subpackage, narrow name-checked except, locked install message.
* docs(bundles): install shapes, override rule, bundle_api_version (#13578)
Documents the deliberately small user-facing rule surface of the metapackage
split (1.11):
- extensions-overview.mdx: the two install stories (pip install langflow =
everything, same as today; pip install lfx = engine only, bring your own
bundles); the bundle-name-is-identity invariant; the current package table
(lfx-bundles metapackage + the 5 partner packages + the 4 pilots); the
legacy-import shim behavior with the locked ModuleNotFoundError wording and
the migration recipe for direct lfx users (switch to lfx[bundles] or pin
lfx-<provider> packages); the override rule ("ship a manifest -- a manifest
always wins", with the bundle-shadowed warning); bundle_api_version as the
single compat number (lfx.compat ["1"]; manifest-less packages ride their
PEP 508 lfx pin) and the no-version-arithmetic rule.
- deployment-lfx-compatibility.mdx: lfx[bundles] as the headless/serverless
deployment footnote (engine + lfx-bundles[all]; slimmer per-provider
alternative; intentionally no lfx[all]).
Both files verified MDX-safe (no unbackticked angle brackets).
* fix(bundles): stack-review fixes — symlink containment, idempotency, docs accuracy (#13579)
* chore(bundles): bounded version ranges for curated lfx-* packages
langflow's pyproject is the release-coordination point now that lfx and the
lfx-* bundles release independently. Every curated lfx-* dependency declares a
BOUNDED range (>=A,<B), never an exact pin -- exact pins in reusable library
metadata create resolver conflicts for downstreams that depend on a different
version. Exact pins for reproducibility live in uv.lock and the release-build
manifests.
- the 4 pilots + 5 graduated partners: >=0.1.0 -> >=0.1.0,<1.0.0 (bundles
follow their own 0.1.x cadence)
- lfx-bundles[all]>=1.0,<2.0 unchanged (versions with the metapackage
contract)
- the three lfx-docling[...] optional-dependency refs bounded the same way
- policy documented inline in the bundle-deps marker block; the cross-bundle
CI matrix verifies the ranges resolve together across the supported lfx axis
Verified: uv lock resolves with zero package/version changes (bounds are
metadata-only today); the nightly rename's dep regex already matches the
bounded form.
* test(bundles): lock the in-tree shim contract + breakage audit
Locks the five-point contract for the 50 lfx-bundles import shims under
lfx/components/ (45 metapackage + 5 graduated partners):
1. first line is the `# lfx-bundles-shim` marker (keys the in-tree walk's
double-registration skip);
2. a shim dir is exactly one __init__.py -- no impls, no deps;
3. sys.modules module-aliasing (submodule trees resolve);
4. bundle missing -> actionable ModuleNotFoundError whose wording is part
of the contract ("pip install lfx-bundles" / "pip install lfx-<p>");
5. a missing *transitive* dep re-raises untouched.
Test shape (env-adaptive by design -- the lfx test env prunes the venv, so
nothing here assumes bundles are installed):
- source-level sweep of every real shim, parameterized (no imports);
- hermetic mechanism tests against a synthetic shim + synthetic target
(alias-resolves / locked-message / transitive-reraise);
- the walk-skip detector and the marker sweep must agree exactly;
- one adaptive live test on lfx.components.tavily exercising whichever
branch the running env is in.
106 tests pass in the pruned lfx env (where the live test exercises the
bare-engine locked-message branch).
Breakage audit (gh code search, recorded in the PR): ~20-25 public repos
reference lfx.components.*; the majority are langflow forks (vendored tree,
unaffected by packaging). Genuine library consumers exist and the moved
providers have real surface (openai: 11 external repos, datastax: 17) --
covered by the shims wherever bundles are co-installed (every
`pip install langflow`). Decision: deprecation warnings NOT warranted now;
revisit at M4 (shim removal), where one loud minor release before removal
is the right shape.
* docs(bundles): install shapes, override rule, bundle_api_version
Documents the deliberately small user-facing rule surface of the metapackage
split (1.11):
- extensions-overview.mdx: the two install stories (pip install langflow =
everything, same as today; pip install lfx = engine only, bring your own
bundles); the bundle-name-is-identity invariant; the current package table
(lfx-bundles metapackage + the 5 partner packages + the 4 pilots); the
legacy-import shim behavior with the locked ModuleNotFoundError wording and
the migration recipe for direct lfx users (switch to lfx[bundles] or pin
lfx-<provider> packages); the override rule ("ship a manifest -- a manifest
always wins", with the bundle-shadowed warning); bundle_api_version as the
single compat number (lfx.compat ["1"]; manifest-less packages ride their
PEP 508 lfx pin) and the no-version-arithmetic rule.
- deployment-lfx-compatibility.mdx: lfx[bundles] as the headless/serverless
deployment footnote (engine + lfx-bundles[all]; slimmer per-provider
alternative; intentionally no lfx[all]).
Both files verified MDX-safe (no unbackticked angle brackets).
* fix(bundles): review fixes — symlink containment, idempotency, docs accuracy
Fixes from the multi-agent stack review (kept as a separate PR so the
reviewed PRs' diffs stay frozen for QA's independent pass):
- _bundles_root.py: directory-level symlink containment — a provider dir
that resolves outside the bundle root is skipped with a typed
bundle-discovery-malformed warning, mirroring the seed-directory walk's
is_within rule (+ regression test). Anchors the trust boundary to the
installed package tree.
- consolidate_bundles.py: migration-append idempotency guard — entries are
deduped on full content so a partially-failed earlier run cannot duplicate
rows on re-run (table stays append-only).
- langflow-base pyproject: documented WHY the aws extra is deliberately
retained after the lfx-amazon graduation (boto3 also backs the server's
own S3 storage backend and lfx's S3 ingestion — review suggested removing
it; that would regress S3 storage support).
- extensions-overview.mdx: note that `lfx extension list` shows
manifest-shipping extensions only; manifest-less packages load at startup
but are not listed.
93 loader+migration tests pass (incl. the new symlink test); ruff clean.
* refactor(bundles): stabilize import_mod as a public BUNDLE_API utility
The lazy-import helper that bundle packages call from their
__getattr__-based __init__.py files lived at lfx.components._importing -- an
internal path with no stability contract, imported by 35 separately-installed
bundle __init__ files (30 lfx-bundles providers + the 5 graduated partners).
- canonical home is now lfx.utils.lazy_import.import_mod (code unchanged);
lfx.components._importing re-exports it so in-tree callers and any external
code on the old path keep working
- all 35 bundle-side imports rewritten to the stable path
- BUNDLE_API.md: surface table entry + changelog (additive)
- contract tests: re-export identity, both call forms, the AttributeError
conversion
Verified: identity holds across both paths; lazy __init__ loads work through
the new path for both bundle families; 110 tests pass; ruff clean.
* fix(bundles): tombstone the broken legacy ZepChatMemory build method (#13580)
build_message_history targeted the zep-python v1 SDK (ZepClient +
zep_python.langchain.ZepChatMessageHistory); both were removed in
zep-python 2.x and the zep extra pins 2.0.2, so the method has been
unable to run for as long as the pin has existed -- its ImportError
guard misleadingly told users to 'pip install zep-python' (already
installed). The component is legacy=True with helpers.Memory as its
designated replacement, so rather than hand-write a new integration
against the 2.x SDK, the method now raises a clear RuntimeError
pointing at the Message History component.
Flow identity is preserved: class/component name, display_name,
description, inputs and the memory output are byte-identical, so saved
flows keep loading, i18n locale keys are unchanged, and
migration_table.json needs no edits. New bundle tests pin the stub
contract (identity, actionable error, no zep_python import).
* test(bundles): extend the shim contract sweep to lfx.base shims
The datastax graduation moved lfx.base.datastax into lfx_datastax.base
and left a module-aliasing shim behind (stored flow code fields embed
the legacy import and are re-executed verbatim at build time). Sweep
lfx/base for marker shims with the same source-level contract as the
components sweep: one-file stub, module-aliasing to the bundle's base
subpackage, narrow name-checked except, locked install message.
* test(bundles): extras-drift guard for the lfx-bundles metapackage
Risk-2 of the metapackage split: the generated `all` extra and the
per-provider extras must never drift by hand-edit, or `pip install
langflow` silently loses a provider's deps. Guard the four invariants:
extras <-> provider dirs (PEP 685-normalized), `all` == the exact
self-ref set, normalized keys collision-free, and the metapackage
provider set disjoint from the graduated partner distributions.
* fix(extension): symlink-escaped providers reject with path-escape, matching the documented contract
The lfx.bundles provider containment check (stack-review symlink fix)
emitted bundle-discovery-malformed, whose template and hint describe a
broken entry-point declaration. The changelog's path-safety entry
already documents symlink escapes as path-escape on every other
discovery path; use the same code here. Also resolves the semantic
merge conflict with the per-mode code split (the removed
_malformed_error location kwarg).
* fix(tests): repoint lfx tests off moved providers; complete provider fallback map
CI fail-fast had been masking these: the LFX test job runs in an
engine-only env where openai/anthropic/chroma are now bundle-package
shims, so every test that used them as the example category failed on
all Python versions (3.14 just reported first), and the backend Group 2
leg failed collecting test_lfx_bundles_extras.py on Python 3.10.
- flow_requirements: complete _PROVIDER_PACKAGE_FALLBACKS for the nine
moved model providers (OpenAI, Anthropic, Amazon Bedrock, Groq,
Google Generative AI, SambaNova, IBM watsonx.ai, Ollama + existing
Azure OpenAI). In engine-only installs MODEL_PROVIDERS_DICT registers
only in-tree providers, so the dynamic source-inspection path cannot
resolve moved providers; the static fallbacks keep lfx run/serve
requirements inference working. A dict hit still wins.
- test_dynamic_imports / test_import_utils: use composio (still-in-tree
lazy category with its SDK absent in the bare env) as the example
category instead of openai/anthropic/chroma; patch import_module at
its canonical home lfx.utils.lazy_import.
- flow-builder tests (build_flow_from_spec, flow_builder_tools,
propose_field_edit): specs use LanguageModelComponent (in-tree)
instead of OpenAIModel.
- test_lfx_bundles_extras: tomli fallback for Python 3.10 (tomllib is
stdlib 3.11+; pytest guarantees tomli on <3.11).
Full lfx unit suite: 4550 passed. Backend extras+pin tests: 24 passed.
* fix(tests): repoint MCP client-server tests off graduated OpenAIModel key
Same fail-fast-masked class as 14dfa7f268, backend side: since the
partner graduation, extension components register in /api/v1/all under
their namespaced ids (ext:openai:OpenAIModelComponent@official), so the
bare 'OpenAIModel' key these tests hardcoded no longer exists on any
Python version (the 'py3.14-only' Group 2 failure was the only leg that
ran; 3.10 was cancelled by fail-fast). The MCP feature itself is fine —
search returns the namespaced ids and add_component accepts them.
- Use LanguageModelComponent (in-tree, stable bare key; SecretStr
api_key, real_time_refresh fields, advanced 'stream', LanguageModel
output) for the redaction/configure/search/describe/spec tests.
- configure_dynamic_field: model_name -> api_key (the refresh field on
LanguageModelComponent).
- prompt-template-variables spec: drop the model node — server-side
validation builds the graph and a model without an API key fails its
build; the test's subject is the dynamic {var} fields.
Full file: 69 passed.
* Update test_mongodb_atlas.py
* Frontend tests