feat: add new openai reasoning models (#8786)

* Add new openai reasoning models

* [autofix.ci] apply automated fixes

* Updates language model, but FE doesn't send a POST for updating template atm

* use chatopenai constants

* [autofix.ci] apply automated fixes

* Add reasoning to language model test

* Remove temp from all reasoning models

* t [autofix.ci] apply automated fixes

* refactor: Update template notes (#8816)

* update templates

* small-changes

* template cleanup

---------

Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com>

* ruff

* uv lock

* starter projects update

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Mike Fortman <michael.fortman@datastax.com>
Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com>
This commit is contained in:
Jordan Frazier 2025-07-07 07:12:54 -07:00 • committed by Gabriel Luiz Freitas Almeida
commit be18f6d03a
29 changed files with 3676 additions and 59 deletions

View file

@ -84,12 +84,9 @@ class LCModelComponent(Component):
raise ValueError(msg) raise ValueError(msg)
async def text_response(self) -> Message: async def text_response(self) -> Message:
input_value = self.input_value
stream = self.stream
system_message = self.system_message
output = self.build_model() output = self.build_model()
result = await self.get_chat_result( result = await self.get_chat_result(
runnable=output, stream=stream, input_value=input_value, system_message=system_message runnable=output, stream=self.stream, input_value=self.input_value, system_message=self.system_message
) )
self.status = result self.status = result
return result return result
@ -176,6 +173,7 @@ class LCModelComponent(Component):
input_value: str | Message, input_value: str | Message,
system_message: str | None = None, system_message: str | None = None,
) -> Message: ) -> Message:
# NVIDIA reasoning models use detailed thinking
if getattr(self, "detailed_thinking", False): if getattr(self, "detailed_thinking", False):
system_message = DETAILED_THINKING_PREFIX + (system_message or "") system_message = DETAILED_THINKING_PREFIX + (system_message or "")

View file

@ -17,6 +17,13 @@ OPENAI_MODELS_DETAILED = [
create_model_metadata(provider="OpenAI", name="gpt-3.5-turbo", icon="OpenAI", tool_calling=True), create_model_metadata(provider="OpenAI", name="gpt-3.5-turbo", icon="OpenAI", tool_calling=True),
# Reasoning Models # Reasoning Models
create_model_metadata(provider="OpenAI", name="o1", icon="OpenAI", reasoning=True), create_model_metadata(provider="OpenAI", name="o1", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o1-mini", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o1-pro", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o3-mini", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o3", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o3-pro", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o4-mini", icon="OpenAI", reasoning=True),
create_model_metadata(provider="OpenAI", name="o4-mini-high", icon="OpenAI", reasoning=True),
# Search Models # Search Models
create_model_metadata( create_model_metadata(
provider="OpenAI", provider="OpenAI",
@ -27,7 +34,12 @@ OPENAI_MODELS_DETAILED = [
preview=True, preview=True,
), ),
create_model_metadata( create_model_metadata(
provider="OpenAI", name="gpt-4o-search-preview", icon="OpenAI", tool_calling=True, search=True, preview=True provider="OpenAI",
name="gpt-4o-search-preview",
icon="OpenAI",
tool_calling=True,
search=True,
preview=True,
), ),
# Not Supported Models # Not Supported Models
create_model_metadata( create_model_metadata(
@ -45,16 +57,13 @@ OPENAI_MODELS_DETAILED = [
create_model_metadata( create_model_metadata(
provider="OpenAI", name="gpt-4o-mini-realtime-preview", icon="OpenAI", not_supported=True, preview=True provider="OpenAI", name="gpt-4o-mini-realtime-preview", icon="OpenAI", not_supported=True, preview=True
), ),
create_model_metadata(provider="OpenAI", name="o3-mini", icon="OpenAI", reasoning=True, not_supported=True),
create_model_metadata(provider="OpenAI", name="o1-mini", icon="OpenAI", reasoning=True, not_supported=True),
] ]
OPENAI_CHAT_MODEL_NAMES = [
OPENAI_MODEL_NAMES = [
metadata["name"] metadata["name"]
for metadata in OPENAI_MODELS_DETAILED for metadata in OPENAI_MODELS_DETAILED
if not metadata.get("reasoning", False) if not metadata.get("not_supported", False)
and not metadata.get("reasoning", False)
and not metadata.get("search", False) and not metadata.get("search", False)
and not metadata.get("not_supported", False)
] ]
OPENAI_REASONING_MODEL_NAMES = [ OPENAI_REASONING_MODEL_NAMES = [
@ -78,4 +87,4 @@ OPENAI_EMBEDDING_MODEL_NAMES = [
] ]
# Backwards compatibility # Backwards compatibility
MODEL_NAMES = OPENAI_MODEL_NAMES MODEL_NAMES = OPENAI_CHAT_MODEL_NAMES

View file

@ -1,7 +1,7 @@
import requests import requests
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
from langflow.base.models.openai_constants import OPENAI_MODEL_NAMES from langflow.base.models.openai_constants import OPENAI_CHAT_MODEL_NAMES
from langflow.custom.custom_component.component import Component from langflow.custom.custom_component.component import Component
from langflow.inputs.inputs import DropdownInput, SecretStrInput, StrInput from langflow.inputs.inputs import DropdownInput, SecretStrInput, StrInput
from langflow.io import MessageTextInput, Output from langflow.io import MessageTextInput, Output
@ -43,8 +43,8 @@ class CombinatorialReasonerComponent(Component):
name="model_name", name="model_name",
display_name="Model Name", display_name="Model Name",
advanced=False, advanced=False,
options=OPENAI_MODEL_NAMES, options=OPENAI_CHAT_MODEL_NAMES,
value=OPENAI_MODEL_NAMES[0], value=OPENAI_CHAT_MODEL_NAMES[0],
), ),
] ]

View file

@ -7,7 +7,7 @@ from langchain_openai import ChatOpenAI
from langflow.base.models.anthropic_constants import ANTHROPIC_MODELS from langflow.base.models.anthropic_constants import ANTHROPIC_MODELS
from langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS from langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS
from langflow.base.models.model import LCModelComponent from langflow.base.models.model import LCModelComponent
from langflow.base.models.openai_constants import OPENAI_MODEL_NAMES from langflow.base.models.openai_constants import OPENAI_CHAT_MODEL_NAMES, OPENAI_REASONING_MODEL_NAMES
from langflow.field_typing import LanguageModel from langflow.field_typing import LanguageModel
from langflow.field_typing.range_spec import RangeSpec from langflow.field_typing.range_spec import RangeSpec
from langflow.inputs.inputs import BoolInput from langflow.inputs.inputs import BoolInput
@ -36,9 +36,10 @@ class LanguageModelComponent(LCModelComponent):
DropdownInput( DropdownInput(
name="model_name", name="model_name",
display_name="Model Name", display_name="Model Name",
options=OPENAI_MODEL_NAMES, options=OPENAI_CHAT_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES,
value=OPENAI_MODEL_NAMES[0], value=OPENAI_CHAT_MODEL_NAMES[0],
info="Select the model to use", info="Select the model to use",
real_time_refresh=True,
), ),
SecretStrInput( SecretStrInput(
name="api_key", name="api_key",
@ -57,7 +58,7 @@ class LanguageModelComponent(LCModelComponent):
name="system_message", name="system_message",
display_name="System Message", display_name="System Message",
info="A system message that helps set the behavior of the assistant", info="A system message that helps set the behavior of the assistant",
advanced=True, advanced=False,
), ),
BoolInput( BoolInput(
name="stream", name="stream",
@ -86,6 +87,11 @@ class LanguageModelComponent(LCModelComponent):
if not self.api_key: if not self.api_key:
msg = "OpenAI API key is required when using OpenAI provider" msg = "OpenAI API key is required when using OpenAI provider"
raise ValueError(msg) raise ValueError(msg)
if model_name in OPENAI_REASONING_MODEL_NAMES:
# reasoning models do not support temperature (yet)
temperature = None
return ChatOpenAI( return ChatOpenAI(
model_name=model_name, model_name=model_name,
temperature=temperature, temperature=temperature,
@ -118,8 +124,8 @@ class LanguageModelComponent(LCModelComponent):
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict: def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:
if field_name == "provider": if field_name == "provider":
if field_value == "OpenAI": if field_value == "OpenAI":
build_config["model_name"]["options"] = OPENAI_MODEL_NAMES build_config["model_name"]["options"] = OPENAI_CHAT_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES
build_config["model_name"]["value"] = OPENAI_MODEL_NAMES[0] build_config["model_name"]["value"] = OPENAI_CHAT_MODEL_NAMES[0]
build_config["api_key"]["display_name"] = "OpenAI API Key" build_config["api_key"]["display_name"] = "OpenAI API Key"
elif field_value == "Anthropic": elif field_value == "Anthropic":
build_config["model_name"]["options"] = ANTHROPIC_MODELS build_config["model_name"]["options"] = ANTHROPIC_MODELS
@ -129,4 +135,10 @@ class LanguageModelComponent(LCModelComponent):
build_config["model_name"]["options"] = GOOGLE_GENERATIVE_AI_MODELS build_config["model_name"]["options"] = GOOGLE_GENERATIVE_AI_MODELS
build_config["model_name"]["value"] = GOOGLE_GENERATIVE_AI_MODELS[0] build_config["model_name"]["value"] = GOOGLE_GENERATIVE_AI_MODELS[0]
build_config["api_key"]["display_name"] = "Google API Key" build_config["api_key"]["display_name"] = "Google API Key"
elif field_name == "model_name" and field_value.startswith("o1") and self.provider == "OpenAI":
# Hide system_message for o1 models - currently unsupported
if "system_message" in build_config:
build_config["system_message"]["show"] = False
elif field_name == "model_name" and not field_value.startswith("o1") and "system_message" in build_config:
build_config["system_message"]["show"] = True
return build_config return build_config

View file

@ -5,7 +5,7 @@ from pydantic.v1 import SecretStr
from langflow.base.models.model import LCModelComponent from langflow.base.models.model import LCModelComponent
from langflow.base.models.openai_constants import ( from langflow.base.models.openai_constants import (
OPENAI_MODEL_NAMES, OPENAI_CHAT_MODEL_NAMES,
OPENAI_REASONING_MODEL_NAMES, OPENAI_REASONING_MODEL_NAMES,
) )
from langflow.field_typing import LanguageModel from langflow.field_typing import LanguageModel
@ -45,8 +45,8 @@ class OpenAIModelComponent(LCModelComponent):
name="model_name", name="model_name",
display_name="Model Name", display_name="Model Name",
advanced=False, advanced=False,
options=OPENAI_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES, options=OPENAI_CHAT_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES,
value=OPENAI_MODEL_NAMES[1], value=OPENAI_CHAT_MODEL_NAMES[0],
combobox=True, combobox=True,
real_time_refresh=True, real_time_refresh=True,
), ),
@ -97,6 +97,7 @@ class OpenAIModelComponent(LCModelComponent):
] ]
def build_model(self) -> LanguageModel: # type: ignore[type-var] def build_model(self) -> LanguageModel: # type: ignore[type-var]
logger.debug(f"Executing request with model: {self.model_name}")
parameters = { parameters = {
"api_key": SecretStr(self.api_key).get_secret_value() if self.api_key else None, "api_key": SecretStr(self.api_key).get_secret_value() if self.api_key else None,
"model_name": self.model_name, "model_name": self.model_name,
@ -107,10 +108,15 @@ class OpenAIModelComponent(LCModelComponent):
"timeout": self.timeout, "timeout": self.timeout,
} }
logger.info(f"Model name: {self.model_name}") # TODO: Revisit if/once parameters are supported for reasoning models
unsupported_params_for_reasoning_models = ["temperature", "seed"]
if self.model_name not in OPENAI_REASONING_MODEL_NAMES: if self.model_name not in OPENAI_REASONING_MODEL_NAMES:
parameters["temperature"] = self.temperature if self.temperature is not None else 0.1 parameters["temperature"] = self.temperature if self.temperature is not None else 0.1
parameters["seed"] = self.seed parameters["seed"] = self.seed
else:
params_str = ", ".join(unsupported_params_for_reasoning_models)
logger.debug(f"{self.model_name} is a reasoning model, {params_str} are not configurable. Ignoring.")
output = ChatOpenAI(**parameters) output = ChatOpenAI(**parameters)
if self.json_mode: if self.json_mode:
@ -141,7 +147,12 @@ class OpenAIModelComponent(LCModelComponent):
if field_name in {"base_url", "model_name", "api_key"} and field_value in OPENAI_REASONING_MODEL_NAMES: if field_name in {"base_url", "model_name", "api_key"} and field_value in OPENAI_REASONING_MODEL_NAMES:
build_config["temperature"]["show"] = False build_config["temperature"]["show"] = False
build_config["seed"]["show"] = False build_config["seed"]["show"] = False
if field_name in {"base_url", "model_name", "api_key"} and field_value in OPENAI_MODEL_NAMES: # Hide system_message for o1 models - currently unsupported
if field_value.startswith("o1") and "system_message" in build_config:
build_config["system_message"]["show"] = False
if field_name in {"base_url", "model_name", "api_key"} and field_value in OPENAI_CHAT_MODEL_NAMES:
build_config["temperature"]["show"] = True build_config["temperature"]["show"] = True
build_config["seed"]["show"] = True build_config["seed"]["show"] = True
if "system_message" in build_config:
build_config["system_message"]["show"] = True
return build_config return build_config

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,6 +9,7 @@ OPENAI_MODELS = [
] ]
CHAT_OPENAI_MODELS = [ CHAT_OPENAI_MODELS = [
"gpt-4o", "gpt-4o",
"gpt-4o-mini",
"gpt-4-turbo-preview", "gpt-4-turbo-preview",
"gpt-4-0125-preview", "gpt-4-0125-preview",
"gpt-4-1106-preview", "gpt-4-1106-preview",
@ -17,6 +18,16 @@ CHAT_OPENAI_MODELS = [
"gpt-3.5-turbo-1106", "gpt-3.5-turbo-1106",
] ]
REASONING_OPENAI_MODELS = [
"o1",
"o1-mini",
"o1-pro",
"o3-mini",
"o3",
"o3-pro",
"o4-mini",
"o4-mini-high",
]
ANTHROPIC_MODELS = [ ANTHROPIC_MODELS = [
# largest model, ideal for a wide range of more complex tasks. # largest model, ideal for a wide range of more complex tasks.

View file

@ -382,6 +382,7 @@ def add_options_to_field(value: dict[str, Any], class_name: str | None, key: str
options_map = { options_map = {
"OpenAI": constants.OPENAI_MODELS, "OpenAI": constants.OPENAI_MODELS,
"ChatOpenAI": constants.CHAT_OPENAI_MODELS, "ChatOpenAI": constants.CHAT_OPENAI_MODELS,
"ReasoningOpenAI": constants.REASONING_OPENAI_MODELS,
"Anthropic": constants.ANTHROPIC_MODELS, "Anthropic": constants.ANTHROPIC_MODELS,
"ChatAnthropic": constants.ANTHROPIC_MODELS, "ChatAnthropic": constants.ANTHROPIC_MODELS,
} }

View file

@ -8,7 +8,7 @@ from langflow.base.models.model_input_constants import (
MODEL_PROVIDERS, MODEL_PROVIDERS,
) )
from langflow.base.models.openai_constants import ( from langflow.base.models.openai_constants import (
OPENAI_MODEL_NAMES, OPENAI_CHAT_MODEL_NAMES,
OPENAI_REASONING_MODEL_NAMES, OPENAI_REASONING_MODEL_NAMES,
) )
from langflow.components.agents.agent import AgentComponent from langflow.components.agents.agent import AgentComponent
@ -142,7 +142,7 @@ class TestAgentComponentWithClient(ComponentTestBaseWithClient):
# Iterate over all OpenAI models # Iterate over all OpenAI models
failed_models = [] failed_models = []
for model_name in OPENAI_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES: for model_name in OPENAI_CHAT_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES:
# Initialize the AgentComponent with mocked inputs # Initialize the AgentComponent with mocked inputs
tools = [CalculatorToolComponent().build_tool()] # Use the Calculator component as a tool tools = [CalculatorToolComponent().build_tool()] # Use the Calculator component as a tool
agent = AgentComponent( agent = AgentComponent(

View file

@ -6,7 +6,7 @@ from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langflow.base.models.anthropic_constants import ANTHROPIC_MODELS from langflow.base.models.anthropic_constants import ANTHROPIC_MODELS
from langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS from langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS
from langflow.base.models.openai_constants import OPENAI_MODEL_NAMES from langflow.base.models.openai_constants import OPENAI_CHAT_MODEL_NAMES, OPENAI_REASONING_MODEL_NAMES
from langflow.components.models.language_model import LanguageModelComponent from langflow.components.models.language_model import LanguageModelComponent
from tests.base import ComponentTestBaseWithoutClient from tests.base import ComponentTestBaseWithoutClient
@ -66,8 +66,8 @@ class TestLanguageModelComponent(ComponentTestBaseWithoutClient):
"api_key": {"display_name": "API Key"}, "api_key": {"display_name": "API Key"},
} }
updated_config = component.update_build_config(build_config, "OpenAI", "provider") updated_config = component.update_build_config(build_config, "OpenAI", "provider")
assert updated_config["model_name"]["options"] == OPENAI_MODEL_NAMES assert updated_config["model_name"]["options"] == OPENAI_CHAT_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES
assert updated_config["model_name"]["value"] == OPENAI_MODEL_NAMES[0] assert updated_config["model_name"]["value"] == OPENAI_CHAT_MODEL_NAMES[0]
assert updated_config["api_key"]["display_name"] == "OpenAI API Key" assert updated_config["api_key"]["display_name"] == "OpenAI API Key"
async def test_update_build_config_anthropic(self, component_class, default_kwargs): async def test_update_build_config_anthropic(self, component_class, default_kwargs):