local model

This commit is contained in:
cristhianzl
2026-05-06 16:13:44 -03:00
parent 1add5cc800
commit 6c175bef91
33 changed files with 139 additions and 58 deletions

View File

@ -4,6 +4,7 @@ import json
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, Query
from lfx.base.models.langflow_local_constants import LANGFLOW_LOCAL_PROVIDER_NAME
from lfx.base.models.model_utils import replace_with_live_models
from lfx.base.models.unified_models import (
get_model_provider_metadata,
@ -200,6 +201,13 @@ async def list_models(
prov_name = provider_dict.get("provider")
provider_dict["is_configured"] = provider_configured_status.get(prov_name, False)
# Langflow Model is the bundled local provider — always active, no
# user setup required. Force is_enabled regardless of per-user toggles.
if prov_name == LANGFLOW_LOCAL_PROVIDER_NAME:
provider_dict["is_configured"] = True
provider_dict["is_enabled"] = True
continue
# Provider is "enabled" (active) if it has at least one enabled model
prov_models_status = enabled_models_map.get(prov_name, {})
has_active_model = any(prov_models_status.values())
@ -567,6 +575,14 @@ async def get_enabled_models(
is_not_supported = metadata.get("not_supported", False)
is_default = metadata.get("default", False)
# Langflow Model is the bundled local provider — its curated
# models are always enabled and cannot be toggled off by the user.
if provider == LANGFLOW_LOCAL_PROVIDER_NAME:
enabled_models[provider][model_name] = (
not is_deprecated and not is_not_supported
)
continue
# Model is enabled if:
# 1. Provider is enabled
# 2. Model is not deprecated/unsupported

View File

@ -1320,15 +1320,15 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "googleapiclient",
"version": "2.194.0"
"version": "2.195.0"
}
],
"total_dependencies": 4

View File

@ -753,7 +753,7 @@
"dependencies": [
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "trustcall",

View File

@ -1173,7 +1173,7 @@
"dependencies": [
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "trustcall",
@ -1522,7 +1522,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "lfx",

View File

@ -1123,7 +1123,7 @@
"dependencies": [
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "trustcall",

View File

@ -2082,7 +2082,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -1192,7 +1192,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -558,7 +558,7 @@
},
{
"name": "langchain_openai",
"version": "1.2.0"
"version": "1.2.1"
},
{
"name": "langchain_huggingface",
@ -578,7 +578,7 @@
},
{
"name": "langchain_ibm",
"version": "1.0.6"
"version": "1.0.7"
}
],
"total_dependencies": 10

View File

@ -1202,7 +1202,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -1990,7 +1990,7 @@
"dependencies": [
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "trustcall",

View File

@ -1186,7 +1186,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -1816,7 +1816,7 @@
},
{
"name": "googleapiclient",
"version": "2.194.0"
"version": "2.195.0"
}
],
"total_dependencies": 7

View File

@ -810,7 +810,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -2138,11 +2138,11 @@
"dependencies": [
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "lfx",

View File

@ -1251,7 +1251,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -945,15 +945,15 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "googleapiclient",
"version": "2.194.0"
"version": "2.195.0"
}
],
"total_dependencies": 4
@ -2024,7 +2024,7 @@
"dependencies": [
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "trustcall",

View File

@ -1621,7 +1621,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -2821,7 +2821,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -903,7 +903,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -952,7 +952,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -367,7 +367,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -987,7 +987,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -2465,7 +2465,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -3073,11 +3073,11 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "lfx",

View File

@ -950,7 +950,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -160,11 +160,11 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "lfx",
@ -392,11 +392,11 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "lfx",
@ -1302,7 +1302,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -2637,15 +2637,15 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "googleapiclient",
"version": "2.194.0"
"version": "2.195.0"
}
],
"total_dependencies": 4

View File

@ -1715,7 +1715,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -2330,7 +2330,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2
@ -2945,7 +2945,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -1639,15 +1639,15 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
},
{
"name": "pydantic",
"version": "2.12.5"
"version": "2.13.3"
},
{
"name": "googleapiclient",
"version": "2.194.0"
"version": "2.195.0"
}
],
"total_dependencies": 4
@ -2691,7 +2691,7 @@
},
{
"name": "cryptography",
"version": "46.0.7"
"version": "47.0.0"
},
{
"name": "langchain_chroma",
@ -3073,7 +3073,7 @@
},
{
"name": "langchain_openai",
"version": "1.2.0"
"version": "1.2.1"
},
{
"name": "langchain_huggingface",
@ -3093,7 +3093,7 @@
},
{
"name": "langchain_ibm",
"version": "1.0.6"
"version": "1.0.7"
}
],
"total_dependencies": 10

View File

@ -274,7 +274,7 @@
},
{
"name": "googleapiclient",
"version": "2.194.0"
"version": "2.195.0"
},
{
"name": "lfx",
@ -510,7 +510,7 @@
},
{
"name": "langchain_core",
"version": "1.3.1"
"version": "1.3.2"
}
],
"total_dependencies": 2

View File

@ -39,7 +39,15 @@ const ModelList = ({
{Object.entries(groupedOptions).map(([provider, models]) => (
<CommandGroup className="p-0" key={provider}>
<div className="text-xs font-semibold my-2 ml-4 text-muted-foreground flex items-center justify-between pr-4">
<div className="flex items-center">{provider}</div>
<div className="flex items-center gap-2">
{models[0]?.icon && (
<ForwardedIconComponent
name={models[0].icon}
className="h-4 w-4 shrink-0"
/>
)}
{provider}
</div>
</div>
{models.map((data) => (
<CommandItem

View File

@ -48,6 +48,15 @@ export const PROVIDER_VARIABLE_MAPPING: Record<string, string> = {
*/
export const NO_API_KEY_PROVIDERS: string[] = [];
/**
* The bundled local LLM provider. Mirrors LANGFLOW_LOCAL_PROVIDER_NAME in
* lfx.base.models.langflow_local_constants — keep these in sync.
*
* This provider is always active and has no per-model toggle: the UI hides
* activate/deactivate controls and the per-model switch for it.
*/
export const LANGFLOW_LOCAL_PROVIDER_NAME = "Langflow Model";
export const VARIABLE_CATEGORY = {
GLOBAL: "Global",
CREDENTIAL: "Credential",

View File

@ -0,0 +1,17 @@
const SvgLangflowIcon = (props) => (
<svg
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M2.1665 11.3333H3.83317L7.1665 8H8.83317L12.1665 4.66667H13.8332M7.1665 13H8.83317L12.1665 9.66667H13.8332M2.1665 6.33333H3.83317L7.1665 3H8.83317"
stroke="#FF3276"
strokeWidth="1.11111"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export default SvgLangflowIcon;

View File

@ -0,0 +1,10 @@
import type React from "react";
import { forwardRef } from "react";
import SvgLangflowIcon from "./LangflowIcon";
export const LangflowIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
return <SvgLangflowIcon ref={ref} {...props} />;
});

View File

@ -57,6 +57,7 @@ import { IcosaIcon } from "@/icons/Icosa";
import { IFixIcon } from "@/icons/IFixIt";
import { JSIcon } from "@/icons/JSicon";
import { LangChainIcon } from "@/icons/LangChain";
import { LangflowIcon } from "@/icons/Langflow";
import { LangwatchIcon } from "@/icons/Langwatch";
import { LiteLLMIcon } from "@/icons/LiteLLM";
import { LMStudioIcon } from "@/icons/LMStudio";
@ -181,6 +182,7 @@ export const eagerIconsMapping = {
javascript: JSIcon,
JigsawStack: JigsawStackIcon,
LangChain: LangChainIcon,
Langflow: LangflowIcon,
Langwatch: LangwatchIcon,
LiteLLM: LiteLLMIcon,
LMStudio: LMStudioIcon,

View File

@ -315,6 +315,8 @@ export const lazyIconsMapping = {
import("@/icons/linear").then((mod) => ({ default: mod.LinearIcon })),
LangChain: () =>
import("@/icons/LangChain").then((mod) => ({ default: mod.LangChainIcon })),
Langflow: () =>
import("@/icons/Langflow").then((mod) => ({ default: mod.LangflowIcon })),
Langwatch: () =>
import("@/icons/Langwatch").then((mod) => ({ default: mod.LangwatchIcon })),
LiteLLM: () =>

View File

@ -784,6 +784,7 @@
"modelProviders.credentialsLink": "{{provider}} credentials",
"modelProviders.toEnableModels": "to enable these models",
"modelProviders.activateToEnable": "Activate {{provider}} to enable these models",
"modelProviders.langflowLocalAlwaysActive": "Always active and ready to use — runs locally with no API key required.",
"modelProviders.unknownProvider": "Unknown Provider",
"modelProviders.configuration": "Configuration",
"modelProviders.disconnectButton": "Disconnect",

View File

@ -1,6 +1,7 @@
import { useTranslation } from "react-i18next";
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import { Switch } from "@/components/ui/switch";
import { LANGFLOW_LOCAL_PROVIDER_NAME } from "@/constants/providerConstants";
import { useGetEnabledModels } from "@/controllers/API/queries/models/use-get-enabled-models";
import { Model } from "@/modals/modelProviderModal/components/types";
@ -20,6 +21,7 @@ interface ModelRowProps {
onToggle: (modelName: string, enabled: boolean) => void;
testIdPrefix: string;
isEnabledModel?: boolean;
hideToggle?: boolean;
}
/** Single row displaying a model with its toggle switch */
@ -29,6 +31,7 @@ const ModelRow = ({
enabled,
testIdPrefix,
isEnabledModel,
hideToggle,
}: ModelRowProps) => (
<div className="flex flex-row items-center justify-between h-[24px]">
<div className="flex flex-row items-center gap-2">
@ -42,7 +45,7 @@ const ModelRow = ({
{model.model_name}
</span>
</div>
{isEnabledModel && (
{!hideToggle && isEnabledModel && (
<Switch
checked={enabled}
onCheckedChange={(checked) => onToggle(model.model_name, checked)}
@ -80,6 +83,8 @@ const ModelSelection = ({
(model) => model.metadata?.model_type === "embeddings",
);
const isLangflowLocal = providerName === LANGFLOW_LOCAL_PROVIDER_NAME;
const renderModelSection = (
title: string,
models: Model[],
@ -100,6 +105,7 @@ const ModelSelection = ({
onToggle={onModelToggle}
testIdPrefix={testIdPrefix}
isEnabledModel={isEnabledModel}
hideToggle={isLangflowLocal}
/>
))}
</div>

View File

@ -5,7 +5,10 @@ import ShadTooltip from "@/components/common/shadTooltipComponent";
import MultiselectComponent from "@/components/core/parameterRenderComponent/components/multiselectComponent";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import type { ProviderVariable } from "@/constants/providerConstants";
import {
LANGFLOW_LOCAL_PROVIDER_NAME,
type ProviderVariable,
} from "@/constants/providerConstants";
import { customOpenNewTab } from "@/customization/utils/custom-open-new-tab";
import useAlertStore from "@/stores/alertStore";
import DisconnectWarning from "./DisconnectWarning";
@ -128,11 +131,16 @@ const ProviderConfigurationForm = ({
if (!selectedProvider) return null;
const isLangflowLocal =
selectedProvider.provider === LANGFLOW_LOCAL_PROVIDER_NAME;
return (
<div className="flex flex-col gap-1 px-4 pt-4">
<div className="flex flex-row gap-1 min-w-[300px]">
<span className="text-[13px] font-semibold mr-auto">
{isSingleVariableProvider ? (
{isLangflowLocal ? (
selectedProvider.provider
) : isSingleVariableProvider ? (
<>
{providerVariables[0].variable_name}
{providerVariables[0].required && (
@ -145,7 +153,9 @@ const ProviderConfigurationForm = ({
</span>
</div>
<span className="text-[13px] text-muted-foreground pt-1 pb-2">
{requiresConfiguration ? (
{isLangflowLocal ? (
<>{t("modelProviders.langflowLocalAlwaysActive")}</>
) : requiresConfiguration ? (
<>
{t("modelProviders.configurePrefix")}{" "}
<span
@ -170,7 +180,7 @@ const ProviderConfigurationForm = ({
</>
)}
</span>
{requiresConfiguration ? (
{isLangflowLocal ? null : requiresConfiguration ? (
<div className="flex flex-col gap-3">
{providerVariables.map((variable) => {
const isConfigured = isVariableConfigured(variable.variable_key);