From e7f78d99cb237f53d29d88149dda0683abf21662 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Fri, 13 Jun 2025 18:02:48 -0500 Subject: [PATCH] refactor: update build_structured_output to return only last output element (#8467) * Update structured_output.py * template updates * Update Financial Report Parser.json * Update structured_output.py * Update structured_output.py * update strcutred output with optimisations * Update test_structured_output_component.py * fix lint * update templates * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Update structured_output.py * [autofix.ci] apply automated fixes * Update test_structured_output_component.py * [autofix.ci] apply automated fixes * fix image sentiment analysis * [autofix.ci] apply automated fixes * Update Image Sentiment Analysis.json * [autofix.ci] apply automated fixes * update template with new language model component --------- Co-authored-by: Cristhian Zanforlin Lousa Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../processing/structured_output.py | 36 +- .../Financial Report Parser.json | 108 +- .../starter_projects/Hybrid Search RAG.json | 2 +- .../Image Sentiment Analysis.json | 1336 ++++++++++------- .../starter_projects/Market Research.json | 300 ++-- .../Portfolio Website Code Generator.json | 259 ++-- .../test_structured_output_component.py | 287 +++- 7 files changed, 1366 insertions(+), 962 deletions(-) diff --git a/src/backend/base/langflow/components/processing/structured_output.py b/src/backend/base/langflow/components/processing/structured_output.py index b50646f3c..f898c6854 100644 --- a/src/backend/base/langflow/components/processing/structured_output.py +++ b/src/backend/base/langflow/components/processing/structured_output.py @@ -119,7 +119,7 @@ class StructuredOutputComponent(Component): ), ] - def build_structured_output_base(self) -> Data: + def build_structured_output_base(self): schema_name = self.schema_name or "OutputModel" if not hasattr(self.llm, "with_structured_output"): @@ -142,6 +142,7 @@ class StructuredOutputComponent(Component): except NotImplementedError as exc: msg = f"{self.llm.__class__.__name__} does not support structured output." raise TypeError(msg) from exc + config_dict = { "run_name": self.display_name, "project_name": self.get_project_name(), @@ -153,16 +154,31 @@ class StructuredOutputComponent(Component): input_value=self.input_value, config=config_dict, ) - if isinstance(result, BaseModel): - result = result.model_dump() - if responses := result.get("responses"): - result = responses[0].model_dump() - if result and "objects" in result: - return result["objects"] - return result + # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure + # Handle non-dict responses (shouldn't happen with trustcall, but defensive) + if not isinstance(result, dict): + return result + + # Extract first response and convert BaseModel to dict + responses = result.get("responses", []) + if not responses: + return result + + # Convert BaseModel to dict (creates the "objects" key) + first_response = responses[0] + structured_data = first_response.model_dump() if isinstance(first_response, BaseModel) else first_response + + # Extract the objects array (guaranteed to exist due to our Pydantic model structure) + return structured_data.get("objects", structured_data) def build_structured_output(self) -> Data: output = self.build_structured_output_base() - - return Data(text_key="results", data={"results": output}) + if not isinstance(output, list) or not output: + # handle empty or unexpected type case + msg = "No structured output returned" + raise ValueError(msg) + if len(output) != 1: + msg = "Multiple structured outputs returned" + raise ValueError(msg) + return Data(data=output[0]) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json b/src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json index 316bf6479..d6cef59f0 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-XaYqD", + "id": "ChatInput-fK2C2", "name": "message", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "StructuredOutput-OUIin", + "id": "StructuredOutput-uvBkm", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-XaYqD{œdataTypeœ:œChatInputœ,œidœ:œChatInput-XaYqDœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-OUIin{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-OUIinœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-fK2C2{œdataTypeœ:œChatInputœ,œidœ:œChatInput-fK2C2œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-uvBkm{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-uvBkmœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-XaYqD", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-XaYqDœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "StructuredOutput-OUIin", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-OUIinœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-fK2C2", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-fK2C2œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "StructuredOutput-uvBkm", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-uvBkmœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-YY3Qp", + "id": "parser-YBMuU", "name": "parsed_text", "output_types": [ "Message" @@ -43,7 +43,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-xBwkk", + "id": "ChatOutput-19sYN", "inputTypes": [ "Data", "DataFrame", @@ -52,12 +52,12 @@ "type": "str" } }, - "id": "reactflow__edge-parser-YY3Qp{œdataTypeœ:œparserœ,œidœ:œparser-YY3Qpœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-xBwkk{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-xBwkkœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-parser-YBMuU{œdataTypeœ:œparserœ,œidœ:œparser-YBMuUœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-19sYN{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-19sYNœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "parser-YY3Qp", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-YY3Qpœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-xBwkk", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-xBwkkœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "parser-YBMuU", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-YBMuUœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-19sYN", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-19sYNœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -65,7 +65,7 @@ "data": { "sourceHandle": { "dataType": "StructuredOutput", - "id": "StructuredOutput-OUIin", + "id": "StructuredOutput-uvBkm", "name": "structured_output", "output_types": [ "Data" @@ -73,7 +73,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "parser-YY3Qp", + "id": "parser-YBMuU", "inputTypes": [ "DataFrame", "Data" @@ -81,12 +81,12 @@ "type": "other" } }, - "id": "reactflow__edge-StructuredOutput-OUIin{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-OUIinœ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-YY3Qp{œfieldNameœ:œinput_dataœ,œidœ:œparser-YY3Qpœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-StructuredOutput-uvBkm{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-uvBkmœ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-YBMuU{œfieldNameœ:œinput_dataœ,œidœ:œparser-YBMuUœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "StructuredOutput-OUIin", - "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-OUIinœ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", - "target": "parser-YY3Qp", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-YY3Qpœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "StructuredOutput-uvBkm", + "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-uvBkmœ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", + "target": "parser-YBMuU", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-YBMuUœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -94,7 +94,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-KS2eL", + "id": "OpenAIModel-rgotA", "name": "model_output", "output_types": [ "LanguageModel" @@ -102,25 +102,25 @@ }, "targetHandle": { "fieldName": "llm", - "id": "StructuredOutput-OUIin", + "id": "StructuredOutput-uvBkm", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIModel-KS2eL{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-KS2eLœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-OUIin{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-OUIinœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-OpenAIModel-rgotA{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-rgotAœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-uvBkm{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-uvBkmœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", "selected": false, - "source": "OpenAIModel-KS2eL", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-KS2eLœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "StructuredOutput-OUIin", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-OUIinœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + "source": "OpenAIModel-rgotA", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-rgotAœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "StructuredOutput-uvBkm", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-uvBkmœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" } ], "nodes": [ { "data": { - "id": "OpenAIModel-KS2eL", + "id": "OpenAIModel-rgotA", "node": { "base_classes": [ "LanguageModel", @@ -172,7 +172,7 @@ "group_outputs": false, "method": "text_response", "name": "text_output", - "selected": "Message", + "selected": null, "tool_mode": true, "types": [ "Message" @@ -213,7 +213,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -499,9 +499,9 @@ "type": "OpenAIModel" }, "dragging": false, - "id": "OpenAIModel-KS2eL", + "id": "OpenAIModel-rgotA", "measured": { - "height": 539, + "height": 540, "width": 320 }, "position": { @@ -513,7 +513,7 @@ }, { "data": { - "id": "ChatOutput-xBwkk", + "id": "ChatOutput-19sYN", "node": { "base_classes": [ "Message" @@ -811,7 +811,7 @@ "showNode": false, "type": "ChatOutput" }, - "id": "ChatOutput-xBwkk", + "id": "ChatOutput-19sYN", "measured": { "height": 48, "width": 192 @@ -825,7 +825,7 @@ }, { "data": { - "id": "ChatInput-XaYqD", + "id": "ChatInput-fK2C2", "node": { "base_classes": [ "Message" @@ -1125,7 +1125,7 @@ "type": "ChatInput" }, "dragging": false, - "id": "ChatInput-XaYqD", + "id": "ChatInput-fK2C2", "measured": { "height": 48, "width": 192 @@ -1139,7 +1139,7 @@ }, { "data": { - "id": "note-LKJJL", + "id": "note-FQnS7", "node": { "description": "### 💡 Add your OpenAI API key here", "display_name": "", @@ -1151,7 +1151,7 @@ "type": "note" }, "dragging": false, - "id": "note-LKJJL", + "id": "note-FQnS7", "measured": { "height": 324, "width": 324 @@ -1165,7 +1165,7 @@ }, { "data": { - "id": "note-XHS7X", + "id": "note-WcFYD", "node": { "description": "\n# Financial Report Parser\n\nThis template extracts key financial metrics from a given financial report text using OpenAI's GPT-4o-mini model. The extracted data is structured and formatted for chat consumption.\n\n## Prerequisites\n\n- **[OpenAI API Key](https://platform.openai.com/)**\n\n## Quickstart\n\n1. Add your OpenAI API key to the OpenAI model.\n2. To run the flow, click **Playground**.\nThe **Chat Input** component in this template is pre-loaded with a sample financial report for demonstrating how structured data is extracted.\n\n* The **OpenAI** model component identifies and retrieves Gross Profit, EBITDA, Net Income, and Operating Expenses from the financial report.\n* The **Structured Output** component formats extracted data into a structured format for better readability and further processing.\n* The **Parser** component converts extracted data into formatted messages for chat consumption.\n\n\n\n\n\n", "display_name": "", @@ -1176,7 +1176,7 @@ }, "dragging": false, "height": 688, - "id": "note-XHS7X", + "id": "note-WcFYD", "measured": { "height": 688, "width": 620 @@ -1192,7 +1192,7 @@ }, { "data": { - "id": "StructuredOutput-OUIin", + "id": "StructuredOutput-uvBkm", "node": { "base_classes": [ "Data", @@ -1254,7 +1254,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self) -> Data:\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n if isinstance(result, BaseModel):\n result = result.model_dump()\n if responses := result.get(\"responses\"):\n result = responses[0].model_dump()\n if result and \"objects\" in result:\n return result[\"objects\"]\n\n return result\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n\n return Data(text_key=\"results\", data={\"results\": output})\n" + "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self):\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n\n # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure\n # Handle non-dict responses (shouldn't happen with trustcall, but defensive)\n if not isinstance(result, dict):\n return result\n\n # Extract first response and convert BaseModel to dict\n responses = result.get(\"responses\", [])\n if not responses:\n return result\n\n # Convert BaseModel to dict (creates the \"objects\" key)\n first_response = responses[0]\n structured_data = first_response.model_dump() if isinstance(first_response, BaseModel) else first_response\n\n # Extract the objects array (guaranteed to exist due to our Pydantic model structure)\n return structured_data.get(\"objects\", structured_data)\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n if not isinstance(output, list) or not output:\n # handle empty or unexpected type case\n msg = \"No structured output returned\"\n raise ValueError(msg)\n if len(output) != 1:\n msg = \"Multiple structured outputs returned\"\n raise ValueError(msg)\n return Data(data=output[0])\n" }, "input_value": { "_input_type": "MessageTextInput", @@ -1451,9 +1451,9 @@ "type": "StructuredOutput" }, "dragging": false, - "id": "StructuredOutput-OUIin", + "id": "StructuredOutput-uvBkm", "measured": { - "height": 348, + "height": 349, "width": 320 }, "position": { @@ -1465,7 +1465,7 @@ }, { "data": { - "id": "parser-YY3Qp", + "id": "parser-YBMuU", "node": { "base_classes": [ "Message" @@ -1627,9 +1627,9 @@ "type": "parser" }, "dragging": false, - "id": "parser-YY3Qp", + "id": "parser-YBMuU", "measured": { - "height": 360, + "height": 361, "width": 320 }, "position": { @@ -1641,17 +1641,17 @@ } ], "viewport": { - "x": -234.08229698659375, - "y": 285.65346951591044, - "zoom": 0.6423907280538087 + "x": -275.9882952608343, + "y": 250.87377864647806, + "zoom": 0.5793112052990664 } }, "description": "Extracts key financial metrics like Gross Profit, EBITDA, and Net Income from financial reports and structures them for easy analysis, using Structured Output Component", "endpoint_name": null, - "id": "5e867fb1-2ce2-481c-9114-aa381cdf1a79", + "id": "8e7bfce3-68ab-4bb9-ab2a-590c71e4456a", "is_component": false, - "last_tested_version": "1.4.2", - "name": "Financial Report Parser (1)", + "last_tested_version": "1.4.3", + "name": "Financial Report Parser", "tags": [ "chatbots", "content-generation" diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Hybrid Search RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Hybrid Search RAG.json index 30a79acee..16404de3d 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Hybrid Search RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Hybrid Search RAG.json @@ -609,7 +609,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self) -> Data:\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n if isinstance(result, BaseModel):\n result = result.model_dump()\n if responses := result.get(\"responses\"):\n result = responses[0].model_dump()\n if result and \"objects\" in result:\n return result[\"objects\"]\n\n return result\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n\n return Data(text_key=\"results\", data={\"results\": output})\n" + "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self):\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n\n # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure\n # Handle non-dict responses (shouldn't happen with trustcall, but defensive)\n if not isinstance(result, dict):\n return result\n\n # Extract first response and convert BaseModel to dict\n responses = result.get(\"responses\", [])\n if not responses:\n return result\n\n # Convert BaseModel to dict (creates the \"objects\" key)\n first_response = responses[0]\n structured_data = first_response.model_dump() if isinstance(first_response, BaseModel) else first_response\n\n # Extract the objects array (guaranteed to exist due to our Pydantic model structure)\n return structured_data.get(\"objects\", structured_data)\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n if not isinstance(output, list) or not output:\n # handle empty or unexpected type case\n msg = \"No structured output returned\"\n raise ValueError(msg)\n if len(output) != 1:\n msg = \"Multiple structured outputs returned\"\n raise ValueError(msg)\n return Data(data=output[0])\n" }, "input_value": { "_input_type": "MessageTextInput", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Image Sentiment Analysis.json b/src/backend/base/langflow/initial_setup/starter_projects/Image Sentiment Analysis.json index b27552bd1..30a76e6fb 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Image Sentiment Analysis.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Image Sentiment Analysis.json @@ -1,152 +1,13 @@ { "data": { "edges": [ - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-nHfqr", - "name": "message", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "OpenAIModel-S4vrc", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-ChatInput-nHfqr{œdataTypeœ:œChatInputœ,œidœ:œChatInput-nHfqrœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-S4vrc{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-S4vrcœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "ChatInput-nHfqr", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-nHfqrœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-S4vrc", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-S4vrcœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "Prompt", - "id": "Prompt-j1IvC", - "name": "prompt", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "system_message", - "id": "OpenAIModel-S4vrc", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Prompt-j1IvC{œdataTypeœ:œPromptœ,œidœ:œPrompt-j1IvCœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-S4vrc{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-S4vrcœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "Prompt-j1IvC", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-j1IvCœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-S4vrc", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-S4vrcœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "OpenAIModel", - "id": "OpenAIModel-S4vrc", - "name": "model_output", - "output_types": [ - "LanguageModel" - ] - }, - "targetHandle": { - "fieldName": "llm", - "id": "StructuredOutput-jrD6H", - "inputTypes": [ - "LanguageModel" - ], - "type": "other" - } - }, - "id": "reactflow__edge-OpenAIModel-S4vrc{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-S4vrcœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-jrD6H{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-jrD6Hœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", - "selected": false, - "source": "OpenAIModel-S4vrc", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-S4vrcœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "StructuredOutput-jrD6H", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-jrD6Hœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "OpenAIModel", - "id": "OpenAIModel-S4vrc", - "name": "text_output", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "StructuredOutput-jrD6H", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-OpenAIModel-S4vrc{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-S4vrcœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-jrD6H{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-jrD6Hœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "OpenAIModel-S4vrc", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-S4vrcœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "StructuredOutput-jrD6H", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-jrD6Hœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "StructuredOutput", - "id": "StructuredOutput-jrD6H", - "name": "structured_output_dataframe", - "output_types": [] - }, - "targetHandle": { - "fieldName": "input_data", - "id": "parser-IVklX", - "inputTypes": [ - "DataFrame", - "Data" - ], - "type": "other" - } - }, - "id": "xy-edge__StructuredOutput-jrD6H{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-jrD6Hœ,œnameœ:œstructured_output_dataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-IVklX{œfieldNameœ:œinput_dataœ,œidœ:œparser-IVklXœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", - "selected": false, - "source": "StructuredOutput-jrD6H", - "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-jrD6Hœ, œnameœ: œstructured_output_dataframeœ, œoutput_typesœ: []}", - "target": "parser-IVklX", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-IVklXœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" - }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-IVklX", + "id": "parser-sIcOW", "name": "parsed_text", "output_types": [ "Message" @@ -154,7 +15,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-gjqnD", + "id": "ChatOutput-1VmAz", "inputTypes": [ "Data", "DataFrame", @@ -163,12 +24,153 @@ "type": "str" } }, - "id": "xy-edge__parser-IVklX{œdataTypeœ:œparserœ,œidœ:œparser-IVklXœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-gjqnD{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-gjqnDœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-parser-sIcOW{œdataTypeœ:œparserœ,œidœ:œparser-sIcOWœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-1VmAz{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-1VmAzœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "parser-IVklX", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-IVklXœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-gjqnD", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-gjqnDœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "parser-sIcOW", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-sIcOWœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-1VmAz", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-1VmAzœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "StructuredOutput", + "id": "StructuredOutput-J6Rvk", + "name": "structured_output", + "output_types": [ + "Data" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "parser-sIcOW", + "inputTypes": [ + "DataFrame", + "Data" + ], + "type": "other" + } + }, + "id": "reactflow__edge-StructuredOutput-J6Rvk{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-J6Rvkœ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-sIcOW{œfieldNameœ:œinput_dataœ,œidœ:œparser-sIcOWœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "selected": false, + "source": "StructuredOutput-J6Rvk", + "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-J6Rvkœ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", + "target": "parser-sIcOW", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-sIcOWœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "LanguageModelComponent", + "id": "LanguageModelComponent-x2hKm", + "name": "model_output", + "output_types": [ + "LanguageModel" + ] + }, + "targetHandle": { + "fieldName": "llm", + "id": "StructuredOutput-J6Rvk", + "inputTypes": [ + "LanguageModel" + ], + "type": "other" + } + }, + "id": "reactflow__edge-LanguageModelComponent-x2hKm{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-x2hKmœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-J6Rvk{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-J6Rvkœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "selected": false, + "source": "LanguageModelComponent-x2hKm", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-x2hKmœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "StructuredOutput-J6Rvk", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-J6Rvkœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "Prompt", + "id": "Prompt-AGdss", + "name": "prompt", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "system_message", + "id": "LanguageModelComponent-tAUaX", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-Prompt-AGdss{œdataTypeœ:œPromptœ,œidœ:œPrompt-AGdssœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-tAUaX{œfieldNameœ:œsystem_messageœ,œidœ:œLanguageModelComponent-tAUaXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "Prompt-AGdss", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-AGdssœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-tAUaX", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œLanguageModelComponent-tAUaXœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-osYcH", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "LanguageModelComponent-tAUaX", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-osYcH{œdataTypeœ:œChatInputœ,œidœ:œChatInput-osYcHœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-tAUaX{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-tAUaXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-osYcH", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-osYcHœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-tAUaX", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-tAUaXœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "LanguageModelComponent", + "id": "LanguageModelComponent-tAUaX", + "name": "text_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "StructuredOutput-J6Rvk", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-LanguageModelComponent-tAUaX{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-tAUaXœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-J6Rvk{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-J6Rvkœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "LanguageModelComponent-tAUaX", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-tAUaXœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "StructuredOutput-J6Rvk", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-J6Rvkœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -176,7 +178,7 @@ "data": { "description": "Get chat inputs from the Playground.", "display_name": "Chat Input", - "id": "ChatInput-nHfqr", + "id": "ChatInput-osYcH", "node": { "base_classes": [ "Message" @@ -202,7 +204,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -456,12 +458,11 @@ }, "tool_mode": false }, - "selected_output": "message", "type": "ChatInput" }, "dragging": false, "height": 234, - "id": "ChatInput-nHfqr", + "id": "ChatInput-osYcH", "measured": { "height": 234, "width": 320 @@ -482,7 +483,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-gjqnD", + "id": "ChatOutput-1VmAz", "node": { "base_classes": [ "Message" @@ -508,7 +509,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -765,7 +766,7 @@ }, "dragging": false, "height": 234, - "id": "ChatOutput-gjqnD", + "id": "ChatOutput-1VmAz", "measured": { "height": 234, "width": 320 @@ -778,13 +779,13 @@ "x": 2742.72534045604, "y": 681.9098282545469 }, - "selected": false, + "selected": true, "type": "genericNode", "width": 320 }, { "data": { - "id": "note-jDost", + "id": "note-uscMT", "node": { "description": "# Image Sentiment Analysis\nWelcome to the Image Sentiment Classifier - an AI tool for quick image sentiment analysis!\n\n## Instructions\n\n1. **Prepare Your Image**\n - Image should be clear and visible\n\n2. **Upload Options**\n - Open the Playground\n - Click file attachment icon\n - Or drag and drop into playground\n\n3. **Wait for Analysis**\n - System will process the image\n - Uses zero-shot learning\n - Classification happens automatically\n\n4. **Review Results**\n - Get classification: Positive/Negative/Neutral\n - Review confidence level\n - Check reasoning if provided\n\n5. **Expected Classifications**\n - Positive: Happy scenes, smiles, celebrations\n - Negative: Sad scenes, problems, conflicts\n - Neutral: Objects, landscapes, neutral scenes\n\nRemember: The clearer the image, the more accurate the classification! 📸✨", "display_name": "", @@ -795,7 +796,7 @@ }, "dragging": false, "height": 583, - "id": "note-jDost", + "id": "note-uscMT", "measured": { "height": 583, "width": 325 @@ -821,7 +822,7 @@ "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-j1IvC", + "id": "Prompt-AGdss", "node": { "base_classes": [ "Message" @@ -841,7 +842,7 @@ "frozen": false, "icon": "braces", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -924,12 +925,11 @@ }, "tool_mode": false }, - "selected_output": "prompt", "type": "Prompt" }, "dragging": false, "height": 260, - "id": "Prompt-j1IvC", + "id": "Prompt-AGdss", "measured": { "height": 260, "width": 320 @@ -948,405 +948,12 @@ }, { "data": { - "id": "OpenAIModel-S4vrc", + "id": "StructuredOutput-J6Rvk", "node": { "base_classes": [ - "LanguageModel", - "Message" + "Data" ], "beta": false, - "category": "models", - "conditional_paths": [], - "custom_fields": {}, - "description": "Generates text using OpenAI LLMs.", - "display_name": "OpenAI", - "documentation": "", - "edited": false, - "field_order": [ - "input_value", - "system_message", - "stream", - "max_tokens", - "model_kwargs", - "json_mode", - "model_name", - "openai_api_base", - "api_key", - "temperature", - "seed" - ], - "frozen": false, - "icon": "OpenAI", - "key": "OpenAIModel", - "legacy": false, - "lf_version": "1.2.0", - "metadata": { - "keywords": [ - "model", - "llm", - "language model", - "large language model" - ] - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Model Response", - "group_outputs": false, - "method": "text_response", - "name": "text_output", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Language Model", - "group_outputs": false, - "method": "build_model", - "name": "model_output", - "selected": "LanguageModel", - "tool_mode": true, - "types": [ - "LanguageModel" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 0.14285714285714285, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [], - "load_from_db": true, - "name": "api_key", - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str", - "value": "OPENAI_API_KEY" - }, - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from typing import Any\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import (\n OPENAI_MODEL_NAMES,\n OPENAI_REASONING_MODEL_NAMES,\n)\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput, DictInput, DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\nfrom langflow.logging import logger\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = [\n *LCModelComponent._base_inputs,\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n range_spec=RangeSpec(min=0, max=128000),\n ),\n DictInput(\n name=\"model_kwargs\",\n display_name=\"Model Kwargs\",\n advanced=True,\n info=\"Additional keyword arguments to pass to the model.\",\n ),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=OPENAI_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[1],\n combobox=True,\n real_time_refresh=True,\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. \"\n \"Defaults to https://api.openai.com/v1. \"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n required=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n show=True,\n ),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n IntInput(\n name=\"max_retries\",\n display_name=\"Max Retries\",\n info=\"The maximum number of retries to make when generating.\",\n advanced=True,\n value=5,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"The timeout for requests to OpenAI completion API.\",\n advanced=True,\n value=700,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n parameters = {\n \"api_key\": SecretStr(self.api_key).get_secret_value() if self.api_key else None,\n \"model_name\": self.model_name,\n \"max_tokens\": self.max_tokens or None,\n \"model_kwargs\": self.model_kwargs or {},\n \"base_url\": self.openai_api_base or \"https://api.openai.com/v1\",\n \"seed\": self.seed,\n \"max_retries\": self.max_retries,\n \"timeout\": self.timeout,\n \"temperature\": self.temperature if self.temperature is not None else 0.1,\n }\n\n logger.info(f\"Model name: {self.model_name}\")\n if self.model_name in OPENAI_REASONING_MODEL_NAMES:\n logger.info(\"Getting reasoning model parameters\")\n parameters.pop(\"temperature\")\n parameters.pop(\"seed\")\n output = ChatOpenAI(**parameters)\n if self.json_mode:\n output = output.bind(response_format={\"type\": \"json_object\"})\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"Get a message from an OpenAI exception.\n\n Args:\n e (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n try:\n from openai import BadRequestError\n except ImportError:\n return None\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\")\n if message:\n return message\n return None\n\n def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:\n if field_name in {\"base_url\", \"model_name\", \"api_key\"} and field_value in OPENAI_REASONING_MODEL_NAMES:\n build_config[\"temperature\"][\"show\"] = False\n build_config[\"seed\"][\"show\"] = False\n if field_name in {\"base_url\", \"model_name\", \"api_key\"} and field_value in OPENAI_MODEL_NAMES:\n build_config[\"temperature\"][\"show\"] = True\n build_config[\"seed\"][\"show\"] = True\n return build_config\n" - }, - "input_value": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Input", - "dynamic": false, - "info": "", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "json_mode": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "JSON Mode", - "dynamic": false, - "info": "If True, it will output JSON regardless of passing a schema.", - "list": false, - "list_add_label": "Add More", - "name": "json_mode", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "max_retries": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Max Retries", - "dynamic": false, - "info": "The maximum number of retries to make when generating.", - "list": false, - "list_add_label": "Add More", - "name": "max_retries", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 5 - }, - "max_tokens": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Max Tokens", - "dynamic": false, - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", - "list": false, - "list_add_label": "Add More", - "name": "max_tokens", - "placeholder": "", - "range_spec": { - "max": 128000, - "min": 0, - "step": 0.1, - "step_type": "float" - }, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": "" - }, - "model_kwargs": { - "_input_type": "DictInput", - "advanced": true, - "display_name": "Model Kwargs", - "dynamic": false, - "info": "Additional keyword arguments to pass to the model.", - "list": false, - "list_add_label": "Add More", - "name": "model_kwargs", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "type": "dict", - "value": {} - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": true, - "dialog_inputs": {}, - "display_name": "Model Name", - "dynamic": false, - "info": "", - "name": "model_name", - "options": [ - "gpt-4o-mini", - "gpt-4o", - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "gpt-4.5-preview", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo", - "o1" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "gpt-4.1-mini" - }, - "openai_api_base": { - "_input_type": "StrInput", - "advanced": true, - "display_name": "OpenAI API Base", - "dynamic": false, - "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "openai_api_base", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "seed": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Seed", - "dynamic": false, - "info": "The seed controls the reproducibility of the job.", - "list": false, - "list_add_label": "Add More", - "name": "seed", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 1 - }, - "stream": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "info": "Stream the response from the model. Streaming works only in Chat.", - "list": false, - "list_add_label": "Add More", - "name": "stream", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "system_message": { - "_input_type": "MultilineInput", - "advanced": false, - "display_name": "System Message", - "dynamic": false, - "info": "System message to pass to the model.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "temperature": { - "_input_type": "SliderInput", - "advanced": false, - "display_name": "Temperature", - "dynamic": false, - "info": "", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", - "placeholder": "", - "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, - "step_type": "float" - }, - "required": false, - "show": true, - "slider_buttons": false, - "slider_buttons_options": [], - "slider_input": false, - "title_case": false, - "tool_mode": false, - "type": "slider", - "value": 0.1 - }, - "timeout": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Timeout", - "dynamic": false, - "info": "The timeout for requests to OpenAI completion API.", - "list": false, - "list_add_label": "Add More", - "name": "timeout", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 700 - } - }, - "tool_mode": false - }, - "selected_output": "text_output", - "showNode": true, - "type": "OpenAIModel" - }, - "dragging": false, - "id": "OpenAIModel-S4vrc", - "measured": { - "height": 525, - "width": 320 - }, - "position": { - "x": 1638.1662423437713, - "y": 374.7199736704084 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "StructuredOutput-jrD6H", - "node": { - "base_classes": [ - "Data", - "DataFrame" - ], - "beta": false, - "category": "helpers", "conditional_paths": [], "custom_fields": {}, "description": "Uses an LLM to generate structured data. Ideal for extraction and consistency.", @@ -1358,14 +965,12 @@ "input_value", "system_prompt", "schema_name", - "output_schema", - "multiple" + "output_schema" ], "frozen": false, "icon": "braces", - "key": "StructuredOutput", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, "minimized": false, "output_types": [], @@ -1386,7 +991,6 @@ } ], "pinned": false, - "score": 0.007568328950209746, "template": { "_type": "Component", "code": { @@ -1405,7 +1009,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self) -> Data:\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n if isinstance(result, BaseModel):\n result = result.model_dump()\n if responses := result.get(\"responses\"):\n result = responses[0].model_dump()\n if result and \"objects\" in result:\n return result[\"objects\"]\n\n return result\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n\n return Data(text_key=\"results\", data={\"results\": output})\n" + "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self):\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n\n # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure\n # Handle non-dict responses (shouldn't happen with trustcall, but defensive)\n if not isinstance(result, dict):\n return result\n\n # Extract first response and convert BaseModel to dict\n responses = result.get(\"responses\", [])\n if not responses:\n return result\n\n # Convert BaseModel to dict (creates the \"objects\" key)\n first_response = responses[0]\n structured_data = first_response.model_dump() if isinstance(first_response, BaseModel) else first_response\n\n # Extract the objects array (guaranteed to exist due to our Pydantic model structure)\n return structured_data.get(\"objects\", structured_data)\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n if not isinstance(output, list) or not output:\n # handle empty or unexpected type case\n msg = \"No structured output returned\"\n raise ValueError(msg)\n if len(output) != 1:\n msg = \"Multiple structured outputs returned\"\n raise ValueError(msg)\n return Data(data=output[0])\n" }, "input_value": { "_input_type": "MessageTextInput", @@ -1458,6 +1062,7 @@ "info": "Define the structure and data types for the model's output.", "is_list": true, "list_add_label": "Add More", + "load_from_db": false, "name": "output_schema", "placeholder": "", "required": true, @@ -1493,7 +1098,7 @@ }, { "default": "str", - "description": "Indicate the data type of the output field (e.g., str, int, float, bool, list, dict).", + "description": "Indicate the data type of the output field (e.g., str, int, float, bool, dict).", "disable_edit": false, "display_name": "Type", "edit_mode": "inline", @@ -1506,7 +1111,6 @@ "int", "float", "bool", - "list", "dict" ], "sortable": true, @@ -1516,7 +1120,7 @@ "default": false, "description": "Set to True if this output field should be a list of the specified type.", "disable_edit": false, - "display_name": "Multiple", + "display_name": "As List", "edit_mode": "inline", "filterable": true, "formatter": "boolean", @@ -1599,14 +1203,13 @@ }, "tool_mode": false }, - "selected_output": "structured_output_dataframe", "showNode": true, "type": "StructuredOutput" }, "dragging": false, - "id": "StructuredOutput-jrD6H", + "id": "StructuredOutput-J6Rvk", "measured": { - "height": 447, + "height": 349, "width": 320 }, "position": { @@ -1618,7 +1221,7 @@ }, { "data": { - "id": "parser-IVklX", + "id": "parser-sIcOW", "node": { "base_classes": [ "Message" @@ -1641,7 +1244,7 @@ "icon": "braces", "key": "parser", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, "minimized": false, "output_types": [], @@ -1775,35 +1378,612 @@ }, "tool_mode": false }, - "selected_output": "parsed_text", "showNode": true, "type": "parser" }, "dragging": false, - "id": "parser-IVklX", + "id": "parser-sIcOW", "measured": { - "height": 395, + "height": 361, "width": 320 }, "position": { "x": 2381.0436064002697, "y": 353.98212620918343 }, - "selected": true, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "LanguageModelComponent-x2hKm", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", + "documentation": "", + "edited": false, + "field_order": [ + "provider", + "model_name", + "api_key", + "input_value", + "system_message", + "stream", + "temperature" + ], + "frozen": false, + "icon": "brain-circuit", + "legacy": false, + "lf_version": "1.4.3", + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": 0, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "Model Provider API key", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"Google\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input text to send to the model", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "Select the model to use", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o-mini" + }, + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Google" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Whether to stream the response", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "A system message that helps set the behavior of the assistant", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "Controls randomness in responses", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "LanguageModelComponent" + }, + "dragging": false, + "id": "LanguageModelComponent-x2hKm", + "measured": { + "height": 451, + "width": 320 + }, + "position": { + "x": 1639.476019848849, + "y": -40.182392195326116 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "LanguageModelComponent-tAUaX", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", + "documentation": "", + "edited": false, + "field_order": [ + "provider", + "model_name", + "api_key", + "input_value", + "system_message", + "stream", + "temperature" + ], + "frozen": false, + "icon": "brain-circuit", + "legacy": false, + "lf_version": "1.4.3", + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": 0, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "Model Provider API key", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"Google\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input text to send to the model", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "Select the model to use", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o-mini" + }, + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Google" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Whether to stream the response", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "A system message that helps set the behavior of the assistant", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "Controls randomness in responses", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "LanguageModelComponent" + }, + "dragging": false, + "id": "LanguageModelComponent-tAUaX", + "measured": { + "height": 534, + "width": 320 + }, + "position": { + "x": 1659.6268676037548, + "y": 577.7964730516118 + }, + "selected": false, "type": "genericNode" } ], "viewport": { - "x": -282.47108266384464, - "y": 179.13713712012208, - "zoom": 0.5222378453386497 + "x": -394.48978663654907, + "y": 83.15122658160072, + "zoom": 0.5803621249210289 } }, "description": "Analyzes images and categorizes them as positive, negative, or neutral using zero-shot learning.", "endpoint_name": null, - "id": "210ef2ad-2a88-4fe2-be1f-6ac73b226b42", + "id": "1a6e7961-5c68-450d-a5b3-db6d0bd90bfe", "is_component": false, - "last_tested_version": "1.2.0", + "last_tested_version": "1.4.3", "name": "Image Sentiment Analysis", "tags": [ "classification" diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json b/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json index c1a9bedd5..3f5256f36 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-b4sJc", + "id": "ChatInput-xGZs1", "name": "message", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "Agent-bjWdX", + "id": "Agent-GuZvV", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-b4sJc{œdataTypeœ:œChatInputœ,œidœ:œChatInput-b4sJcœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-bjWdX{œfieldNameœ:œinput_valueœ,œidœ:œAgent-bjWdXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-xGZs1{œdataTypeœ:œChatInputœ,œidœ:œChatInput-xGZs1œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-GuZvV{œfieldNameœ:œinput_valueœ,œidœ:œAgent-GuZvVœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-b4sJc", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-b4sJcœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Agent-bjWdX", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-bjWdXœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-xGZs1", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-xGZs1œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-GuZvV", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-GuZvVœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "Agent", - "id": "Agent-bjWdX", + "id": "Agent-GuZvV", "name": "response", "output_types": [ "Message" @@ -43,19 +43,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "StructuredOutput-5OFKk", + "id": "StructuredOutput-TgwNF", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Agent-bjWdX{œdataTypeœ:œAgentœ,œidœ:œAgent-bjWdXœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-5OFKk{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-5OFKkœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Agent-GuZvV{œdataTypeœ:œAgentœ,œidœ:œAgent-GuZvVœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-TgwNF{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-TgwNFœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Agent-bjWdX", - "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-bjWdXœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", - "target": "StructuredOutput-5OFKk", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-5OFKkœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Agent-GuZvV", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-GuZvVœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "StructuredOutput-TgwNF", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-TgwNFœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -63,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "TavilySearchComponent", - "id": "TavilySearchComponent-vpPJd", + "id": "TavilySearchComponent-oQBdN", "name": "component_as_tool", "output_types": [ "Tool" @@ -71,19 +71,19 @@ }, "targetHandle": { "fieldName": "tools", - "id": "Agent-bjWdX", + "id": "Agent-GuZvV", "inputTypes": [ "Tool" ], "type": "other" } }, - "id": "reactflow__edge-TavilySearchComponent-vpPJd{œdataTypeœ:œTavilySearchComponentœ,œidœ:œTavilySearchComponent-vpPJdœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-bjWdX{œfieldNameœ:œtoolsœ,œidœ:œAgent-bjWdXœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-TavilySearchComponent-oQBdN{œdataTypeœ:œTavilySearchComponentœ,œidœ:œTavilySearchComponent-oQBdNœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-GuZvV{œfieldNameœ:œtoolsœ,œidœ:œAgent-GuZvVœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, - "source": "TavilySearchComponent-vpPJd", - "sourceHandle": "{œdataTypeœ: œTavilySearchComponentœ, œidœ: œTavilySearchComponent-vpPJdœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-bjWdX", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-bjWdXœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "TavilySearchComponent-oQBdN", + "sourceHandle": "{œdataTypeœ: œTavilySearchComponentœ, œidœ: œTavilySearchComponent-oQBdNœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-GuZvV", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-GuZvVœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -91,7 +91,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-2q7JZ", + "id": "OpenAIModel-kaJVR", "name": "model_output", "output_types": [ "LanguageModel" @@ -99,51 +99,27 @@ }, "targetHandle": { "fieldName": "llm", - "id": "StructuredOutput-5OFKk", + "id": "StructuredOutput-TgwNF", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIModel-2q7JZ{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-2q7JZœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-5OFKk{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-5OFKkœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-OpenAIModel-kaJVR{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-kaJVRœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-TgwNF{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-TgwNFœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", "selected": false, - "source": "OpenAIModel-2q7JZ", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-2q7JZœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "StructuredOutput-5OFKk", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-5OFKkœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + "source": "OpenAIModel-kaJVR", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-kaJVRœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "StructuredOutput-TgwNF", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-TgwNFœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" }, { "animated": false, - "data": { - "sourceHandle": { - "dataType": "StructuredOutput", - "id": "StructuredOutput-5OFKk", - "name": "structured_output_dataframe", - "output_types": [] - }, - "targetHandle": { - "fieldName": "input_data", - "id": "parser-7JQpB", - "inputTypes": [ - "DataFrame", - "Data" - ], - "type": "other" - } - }, - "id": "xy-edge__StructuredOutput-5OFKk{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-5OFKkœ,œnameœ:œstructured_output_dataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-7JQpB{œfieldNameœ:œinput_dataœ,œidœ:œparser-7JQpBœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", - "selected": false, - "source": "StructuredOutput-5OFKk", - "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-5OFKkœ, œnameœ: œstructured_output_dataframeœ, œoutput_typesœ: []}", - "target": "parser-7JQpB", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-7JQpBœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" - }, - { + "className": "", "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-7JQpB", + "id": "parser-4iVNG", "name": "parsed_text", "output_types": [ "Message" @@ -151,7 +127,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-CFSA4", + "id": "ChatOutput-eTO66", "inputTypes": [ "Data", "DataFrame", @@ -160,11 +136,40 @@ "type": "str" } }, - "id": "xy-edge__parser-7JQpB{œdataTypeœ:œparserœ,œidœ:œparser-7JQpBœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-CFSA4{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-CFSA4œ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", - "source": "parser-7JQpB", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-7JQpBœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-CFSA4", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-CFSA4œ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-parser-4iVNG{œdataTypeœ:œparserœ,œidœ:œparser-4iVNGœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-eTO66{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-eTO66œ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "parser-4iVNG", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-4iVNGœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-eTO66", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-eTO66œ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "data": { + "sourceHandle": { + "dataType": "StructuredOutput", + "id": "StructuredOutput-TgwNF", + "name": "structured_output", + "output_types": [ + "Data" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "parser-4iVNG", + "inputTypes": [ + "DataFrame", + "Data" + ], + "type": "other" + } + }, + "id": "xy-edge__StructuredOutput-TgwNF{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-TgwNFœ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-4iVNG{œfieldNameœ:œinput_dataœ,œidœ:œparser-4iVNGœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "selected": false, + "source": "StructuredOutput-TgwNF", + "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-TgwNFœ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", + "target": "parser-4iVNG", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-4iVNGœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" } ], "nodes": [ @@ -172,7 +177,7 @@ "data": { "description": "Get chat inputs from the Playground.", "display_name": "Chat Input", - "id": "ChatInput-b4sJc", + "id": "ChatInput-xGZs1", "node": { "base_classes": [ "Message" @@ -449,7 +454,7 @@ }, "dragging": false, "height": 234, - "id": "ChatInput-b4sJc", + "id": "ChatInput-xGZs1", "measured": { "height": 234, "width": 320 @@ -470,7 +475,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-CFSA4", + "id": "ChatOutput-eTO66", "node": { "base_classes": [ "Message" @@ -753,7 +758,7 @@ }, "dragging": false, "height": 234, - "id": "ChatOutput-CFSA4", + "id": "ChatOutput-eTO66", "measured": { "height": 234, "width": 320 @@ -772,7 +777,7 @@ }, { "data": { - "id": "note-UqfqM", + "id": "note-tcZdh", "node": { "description": "# Market Research\nThis flow helps you gather comprehensive information about companies for sales and business intelligence purposes.\n\n## Prerequisites\n\n- **[Tavily API Key](https://docs.tavily.com/welcome)**\n- **[OpenAI API Key](https://platform.openai.com/)**\n\n## Quickstart\n\n1. Add your **OpenAI API key** to the **OpenAI** model and **Agent** components.\n2. Add your **Tavily API key** to the **Tavily Search** component.\n3. In the **Chat Input**, enter a company name you want to research.\n4. Open the **Playground** and research the company. The **Structured Output** component transforms the raw LLM response into structured data, and the **Parser** component presents the data as text for the **Chat output** component to present.", "display_name": "", @@ -785,7 +790,7 @@ }, "dragging": false, "height": 671, - "id": "note-UqfqM", + "id": "note-tcZdh", "measured": { "height": 671, "width": 660 @@ -811,7 +816,7 @@ "data": { "description": "Transforms LLM responses into **structured data formats**. Ideal for extracting specific information or creating consistent outputs.", "display_name": "Structured Output", - "id": "StructuredOutput-5OFKk", + "id": "StructuredOutput-TgwNF", "node": { "base_classes": [ "Data", @@ -874,7 +879,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self) -> Data:\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n if isinstance(result, BaseModel):\n result = result.model_dump()\n if responses := result.get(\"responses\"):\n result = responses[0].model_dump()\n if result and \"objects\" in result:\n return result[\"objects\"]\n\n return result\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n\n return Data(text_key=\"results\", data={\"results\": output})\n" + "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self):\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n\n # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure\n # Handle non-dict responses (shouldn't happen with trustcall, but defensive)\n if not isinstance(result, dict):\n return result\n\n # Extract first response and convert BaseModel to dict\n responses = result.get(\"responses\", [])\n if not responses:\n return result\n\n # Convert BaseModel to dict (creates the \"objects\" key)\n first_response = responses[0]\n structured_data = first_response.model_dump() if isinstance(first_response, BaseModel) else first_response\n\n # Extract the objects array (guaranteed to exist due to our Pydantic model structure)\n return structured_data.get(\"objects\", structured_data)\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n if not isinstance(output, list) or not output:\n # handle empty or unexpected type case\n msg = \"No structured output returned\"\n raise ValueError(msg)\n if len(output) != 1:\n msg = \"Multiple structured outputs returned\"\n raise ValueError(msg)\n return Data(data=output[0])\n" }, "input_value": { "_input_type": "MessageTextInput", @@ -1122,7 +1127,7 @@ }, "dragging": false, "height": 541, - "id": "StructuredOutput-5OFKk", + "id": "StructuredOutput-TgwNF", "measured": { "height": 541, "width": 320 @@ -1143,7 +1148,7 @@ "data": { "description": "Define the agent's instructions, then enter a task to complete using tools.", "display_name": "Agent", - "id": "Agent-bjWdX", + "id": "Agent-GuZvV", "node": { "base_classes": [ "Message" @@ -1292,7 +1297,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -1815,7 +1820,7 @@ }, "dragging": false, "height": 650, - "id": "Agent-bjWdX", + "id": "Agent-GuZvV", "measured": { "height": 650, "width": 320 @@ -1832,7 +1837,7 @@ "data": { "description": "**Tavily AI** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", "display_name": "Tavily AI Search", - "id": "TavilySearchComponent-vpPJd", + "id": "TavilySearchComponent-oQBdN", "node": { "base_classes": [ "Data", @@ -1867,6 +1872,7 @@ "allows_loop": false, "cache": true, "display_name": "Toolset", + "group_outputs": false, "hidden": null, "method": "to_toolkit", "name": "component_as_tool", @@ -1898,7 +1904,7 @@ "show": true, "title_case": false, "type": "str", - "value": "TAVILY_API_KEY" + "value": "" }, "chunks_per_source": { "_input_type": "IntInput", @@ -2143,11 +2149,11 @@ "type": "str" }, "tools_metadata": { - "_input_type": "TableInput", + "_input_type": "ToolsInput", "advanced": false, - "display_name": "Edit tools", + "display_name": "Actions", "dynamic": false, - "info": "", + "info": "Modify tool names and descriptions to help agents understand when to use each tool.", "is_list": true, "list_add_label": "Add More", "name": "tools_metadata", @@ -2155,100 +2161,28 @@ "real_time_refresh": true, "required": false, "show": true, - "table_icon": "Hammer", - "table_options": { - "block_add": true, - "block_delete": true, - "block_edit": true, - "block_filter": true, - "block_hide": true, - "block_select": true, - "block_sort": true, - "description": "Modify tool names and descriptions to help agents understand when to use each tool.", - "field_parsers": { - "commands": "commands", - "name": [ - "snake_case", - "no_blank" - ] - }, - "hide_options": true - }, - "table_schema": { - "columns": [ - { - "default": "None", - "description": "Specify the name of the tool.", - "disable_edit": false, - "display_name": "Tool Name", - "edit_mode": "inline", - "filterable": false, - "formatter": "text", - "hidden": false, - "name": "name", - "sortable": false, - "type": "str" - }, - { - "default": "None", - "description": "Describe the purpose of the tool.", - "disable_edit": false, - "display_name": "Tool Description", - "edit_mode": "popover", - "filterable": false, - "formatter": "text", - "hidden": false, - "name": "description", - "sortable": false, - "type": "str" - }, - { - "default": "None", - "description": "The default identifiers for the tools and cannot be changed.", - "disable_edit": true, - "display_name": "Tool Identifiers", - "edit_mode": "inline", - "filterable": false, - "formatter": "text", - "hidden": true, - "name": "tags", - "sortable": false, - "type": "str" - }, - { - "default": true, - "description": "Indicates whether the tool is currently active. Set to True to activate this tool.", - "disable_edit": false, - "display_name": "Enable", - "edit_mode": "popover", - "filterable": true, - "formatter": "boolean", - "hidden": false, - "name": "status", - "sortable": true, - "type": "boolean" - } - ] - }, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "trigger_icon": "Hammer", - "trigger_text": "", - "type": "table", + "type": "tools", "value": [ { - "description": "fetch_content(api_key: Message) - **Tavily AI** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", - "name": "TavilySearchComponent-fetch_content", + "args": { + "query": { + "default": "", + "description": "The search query you want to execute with Tavily.", + "title": "Query", + "type": "string" + } + }, + "description": "TavilySearchComponent. fetch_content_dataframe - **Tavily Search** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", + "display_description": "TavilySearchComponent. fetch_content_dataframe - **Tavily Search** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", + "display_name": "fetch_content_dataframe", + "name": "fetch_content_dataframe", + "readonly": false, + "status": true, "tags": [ - "TavilySearchComponent-fetch_content" - ] - }, - { - "description": "fetch_content_text(api_key: Message) - **Tavily AI** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", - "name": "TavilySearchComponent-fetch_content_text", - "tags": [ - "TavilySearchComponent-fetch_content_text" + "fetch_content_dataframe" ] } ] @@ -2284,21 +2218,21 @@ "type": "TavilySearchComponent" }, "dragging": false, - "id": "TavilySearchComponent-vpPJd", + "id": "TavilySearchComponent-oQBdN", "measured": { - "height": 437, + "height": 316, "width": 320 }, "position": { - "x": 875.7686789989679, - "y": 798.478848045035 + "x": 868.5433981701304, + "y": 653.9732314682851 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "OpenAIModel-2q7JZ", + "id": "OpenAIModel-kaJVR", "node": { "base_classes": [ "LanguageModel", @@ -2348,7 +2282,7 @@ "group_outputs": false, "method": "text_response", "name": "text_output", - "selected": "Message", + "selected": null, "tool_mode": true, "types": [ "Message" @@ -2389,7 +2323,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -2675,9 +2609,9 @@ "type": "OpenAIModel" }, "dragging": false, - "id": "OpenAIModel-2q7JZ", + "id": "OpenAIModel-kaJVR", "measured": { - "height": 525, + "height": 540, "width": 320 }, "position": { @@ -2689,7 +2623,7 @@ }, { "data": { - "id": "parser-7JQpB", + "id": "parser-4iVNG", "node": { "base_classes": [ "Message" @@ -2850,30 +2784,30 @@ "type": "parser" }, "dragging": false, - "id": "parser-7JQpB", + "id": "parser-4iVNG", "measured": { - "height": 395, + "height": 361, "width": 320 }, "position": { "x": 2132.7576351687417, "y": 744.0431534971781 }, - "selected": true, + "selected": false, "type": "genericNode" } ], "viewport": { - "x": 218.73749848572567, - "y": 82.09531548995307, - "zoom": 0.3869462183165413 + "x": -318.95425982795314, + "y": -363.533384278095, + "zoom": 0.6920146245449773 } }, "description": "Researches companies, extracts key business data, and presents structured information for efficient analysis. ", "endpoint_name": null, - "id": "312739e7-29bb-4a71-8faf-3bcf2d7c22bb", + "id": "8ce19804-52f5-4894-9354-4272aff238f7", "is_component": false, - "last_tested_version": "1.2.0", + "last_tested_version": "1.4.3", "name": "Market Research", "tags": [ "assistants", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json b/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json index 26b35d566..4895e94e4 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "TextInput", - "id": "TextInput-WR8cL", + "id": "TextInput-5rO9p", "name": "text", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "AnthropicModel-tZx4n", + "id": "AnthropicModel-Te07A", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-TextInput-WR8cL{œdataTypeœ:œTextInputœ,œidœ:œTextInput-WR8cLœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-tZx4n{œfieldNameœ:œsystem_messageœ,œidœ:œAnthropicModel-tZx4nœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-TextInput-5rO9p{œdataTypeœ:œTextInputœ,œidœ:œTextInput-5rO9pœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-Te07A{œfieldNameœ:œsystem_messageœ,œidœ:œAnthropicModel-Te07Aœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "TextInput-WR8cL", - "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-WR8cLœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", - "target": "AnthropicModel-tZx4n", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œAnthropicModel-tZx4nœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "TextInput-5rO9p", + "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-5rO9pœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", + "target": "AnthropicModel-Te07A", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œAnthropicModel-Te07Aœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "AnthropicModel", - "id": "AnthropicModel-fz6rd", + "id": "AnthropicModel-baay5", "name": "model_output", "output_types": [ "LanguageModel" @@ -43,19 +43,19 @@ }, "targetHandle": { "fieldName": "llm", - "id": "StructuredOutput-tWLRa", + "id": "StructuredOutput-GJfDv", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-AnthropicModel-fz6rd{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-fz6rdœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-tWLRa{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-tWLRaœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-AnthropicModel-baay5{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-baay5œ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-GJfDv{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-GJfDvœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", "selected": false, - "source": "AnthropicModel-fz6rd", - "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-fz6rdœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "StructuredOutput-tWLRa", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-tWLRaœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + "source": "AnthropicModel-baay5", + "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-baay5œ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "StructuredOutput-GJfDv", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-GJfDvœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -63,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "ParserComponent", - "id": "ParserComponent-wrPyP", + "id": "ParserComponent-FPw08", "name": "parsed_text", "output_types": [ "Message" @@ -71,19 +71,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "StructuredOutput-tWLRa", + "id": "StructuredOutput-GJfDv", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ParserComponent-wrPyP{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-wrPyPœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-tWLRa{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-tWLRaœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ParserComponent-FPw08{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-FPw08œ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-GJfDv{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-GJfDvœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ParserComponent-wrPyP", - "sourceHandle": "{œdataTypeœ: œParserComponentœ, œidœ: œParserComponent-wrPyPœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "StructuredOutput-tWLRa", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-tWLRaœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ParserComponent-FPw08", + "sourceHandle": "{œdataTypeœ: œParserComponentœ, œidœ: œParserComponent-FPw08œ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "StructuredOutput-GJfDv", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-GJfDvœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -91,7 +91,7 @@ "data": { "sourceHandle": { "dataType": "File", - "id": "File-v8ouW", + "id": "File-nhzgk", "name": "dataframe", "output_types": [ "DataFrame" @@ -99,7 +99,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "ParserComponent-wrPyP", + "id": "ParserComponent-FPw08", "inputTypes": [ "DataFrame", "Data" @@ -107,12 +107,12 @@ "type": "other" } }, - "id": "reactflow__edge-File-v8ouW{œdataTypeœ:œFileœ,œidœ:œFile-v8ouWœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-ParserComponent-wrPyP{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-wrPyPœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-File-nhzgk{œdataTypeœ:œFileœ,œidœ:œFile-nhzgkœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-ParserComponent-FPw08{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-FPw08œ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "File-v8ouW", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-v8ouWœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "ParserComponent-wrPyP", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œParserComponent-wrPyPœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "File-nhzgk", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-nhzgkœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "ParserComponent-FPw08", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œParserComponent-FPw08œ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -120,7 +120,7 @@ "data": { "sourceHandle": { "dataType": "AnthropicModel", - "id": "AnthropicModel-tZx4n", + "id": "AnthropicModel-Te07A", "name": "text_output", "output_types": [ "Message" @@ -128,7 +128,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-iLekZ", + "id": "ChatOutput-XN5RE", "inputTypes": [ "Data", "DataFrame", @@ -137,39 +137,12 @@ "type": "str" } }, - "id": "reactflow__edge-AnthropicModel-tZx4n{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-tZx4nœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-iLekZ{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-iLekZœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-AnthropicModel-Te07A{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-Te07Aœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-XN5RE{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-XN5REœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "AnthropicModel-tZx4n", - "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-tZx4nœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-iLekZ", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-iLekZœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "StructuredOutput", - "id": "StructuredOutput-tWLRa", - "name": "structured_output_dataframe", - "output_types": [] - }, - "targetHandle": { - "fieldName": "input_data", - "id": "parser-ifKFs", - "inputTypes": [ - "DataFrame", - "Data" - ], - "type": "other" - } - }, - "id": "xy-edge__StructuredOutput-tWLRa{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-tWLRaœ,œnameœ:œstructured_output_dataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-ifKFs{œfieldNameœ:œinput_dataœ,œidœ:œparser-ifKFsœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", - "selected": false, - "source": "StructuredOutput-tWLRa", - "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-tWLRaœ, œnameœ: œstructured_output_dataframeœ, œoutput_typesœ: []}", - "target": "parser-ifKFs", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-ifKFsœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "AnthropicModel-Te07A", + "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-Te07Aœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-XN5RE", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-XN5REœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -177,7 +150,7 @@ "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-ifKFs", + "id": "parser-8Jyry", "name": "parsed_text", "output_types": [ "Message" @@ -185,25 +158,53 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "AnthropicModel-tZx4n", + "id": "AnthropicModel-Te07A", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__parser-ifKFs{œdataTypeœ:œparserœ,œidœ:œparser-ifKFsœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-tZx4n{œfieldNameœ:œinput_valueœ,œidœ:œAnthropicModel-tZx4nœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-parser-8Jyry{œdataTypeœ:œparserœ,œidœ:œparser-8Jyryœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-Te07A{œfieldNameœ:œinput_valueœ,œidœ:œAnthropicModel-Te07Aœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "parser-ifKFs", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-ifKFsœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "AnthropicModel-tZx4n", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAnthropicModel-tZx4nœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "parser-8Jyry", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-8Jyryœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "AnthropicModel-Te07A", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAnthropicModel-Te07Aœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "data": { + "sourceHandle": { + "dataType": "StructuredOutput", + "id": "StructuredOutput-GJfDv", + "name": "structured_output", + "output_types": [ + "Data" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "parser-8Jyry", + "inputTypes": [ + "DataFrame", + "Data" + ], + "type": "other" + } + }, + "id": "xy-edge__StructuredOutput-GJfDv{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-GJfDvœ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-8Jyry{œfieldNameœ:œinput_dataœ,œidœ:œparser-8Jyryœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "selected": false, + "source": "StructuredOutput-GJfDv", + "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-GJfDvœ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", + "target": "parser-8Jyry", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-8Jyryœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" } ], "nodes": [ { "data": { - "id": "TextInput-WR8cL", + "id": "TextInput-5rO9p", "node": { "base_classes": [ "Message" @@ -297,9 +298,9 @@ "type": "TextInput" }, "dragging": false, - "id": "TextInput-WR8cL", + "id": "TextInput-5rO9p", "measured": { - "height": 230, + "height": 204, "width": 320 }, "position": { @@ -311,7 +312,7 @@ }, { "data": { - "id": "AnthropicModel-fz6rd", + "id": "AnthropicModel-baay5", "node": { "base_classes": [ "LanguageModel", @@ -402,7 +403,7 @@ "show": true, "title_case": false, "type": "str", - "value": "ANTHROPIC_API_KEY" + "value": "" }, "base_url": { "_input_type": "MessageTextInput", @@ -636,9 +637,9 @@ "type": "AnthropicModel" }, "dragging": false, - "id": "AnthropicModel-fz6rd", + "id": "AnthropicModel-baay5", "measured": { - "height": 670, + "height": 510, "width": 320 }, "position": { @@ -650,7 +651,7 @@ }, { "data": { - "id": "AnthropicModel-tZx4n", + "id": "AnthropicModel-Te07A", "node": { "base_classes": [ "LanguageModel", @@ -741,7 +742,7 @@ "show": true, "title_case": false, "type": "str", - "value": "ANTHROPIC_API_KEY" + "value": "" }, "base_url": { "_input_type": "MessageTextInput", @@ -975,21 +976,21 @@ "type": "AnthropicModel" }, "dragging": false, - "id": "AnthropicModel-tZx4n", + "id": "AnthropicModel-Te07A", "measured": { - "height": 670, + "height": 510, "width": 320 }, "position": { - "x": 2310.3492481396656, - "y": 257.7980880183202 + "x": 2197.188568196028, + "y": 492.960126026192 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "ChatOutput-iLekZ", + "id": "ChatOutput-XN5RE", "node": { "base_classes": [ "Message" @@ -1288,9 +1289,9 @@ "type": "ChatOutput" }, "dragging": false, - "id": "ChatOutput-iLekZ", + "id": "ChatOutput-XN5RE", "measured": { - "height": 66, + "height": 48, "width": 192 }, "position": { @@ -1302,7 +1303,7 @@ }, { "data": { - "id": "note-isxpR", + "id": "note-OWjt8", "node": { "description": "### 💡 Upload your resume here", "display_name": "", @@ -1315,14 +1316,14 @@ }, "dragging": false, "height": 358, - "id": "note-isxpR", + "id": "note-OWjt8", "measured": { "height": 358, "width": 345 }, "position": { - "x": -114.57765784557182, - "y": 483.643773301725 + "x": 309.867023997511, + "y": 504.0319457920041 }, "resizing": false, "selected": false, @@ -1331,7 +1332,7 @@ }, { "data": { - "id": "note-zvgQE", + "id": "note-jSID0", "node": { "description": "## 📝 Portfolio Website Code Generator\n\nYour uploaded resume is parsed into a structured format, and output as HTML/CSS code for your own resume website!\n\n✅ **Accepted Formats:** PDF or TXT \n✅ To ensure readability, provide clear headings, bullet points, and proper formatting. \n### 📌 Structured output fields:\n1. 🏷 **Full Name** - Candidate's full name \n2. 📧 **Email** - A valid email address \n3. 📞 **Phone Number** - Contact number \n4. 🔗 **LinkedIn** - LinkedIn profile URL \n5. 🖥 **GitHub** - GitHub profile URL (if applicable) \n6. 🌐 **Portfolio** - Personal website or portfolio URL \n7. 🛂 **Visa Status** - Work authorization details (if applicable) \n8. 📝 **Summary** - A brief professional summary or objective statement \n9. 💼 **Experience** - Work experience details (in dictionary format) \n10. 🎓 **Education** - Education details (in dictionary format) \n11. 🛠 **Skills** - Skills mentioned in the resume (comma-separated) \n12. 🚀 **Projects** - Titles, descriptions, and details of projects.", "display_name": "", @@ -1341,24 +1342,24 @@ "type": "note" }, "dragging": false, - "height": 324, - "id": "note-zvgQE", + "height": 621, + "id": "note-jSID0", "measured": { - "height": 324, - "width": 325 + "height": 621, + "width": 478 }, "position": { - "x": -510.6692280684694, - "y": 451.2228866325788 + "x": -195.6127187156656, + "y": 477.17146980202494 }, "resizing": false, "selected": false, "type": "noteNode", - "width": 324 + "width": 478 }, { "data": { - "id": "note-smoRV", + "id": "note-ufZW4", "node": { "description": "### 💡 Click **Open table** to view the schema", "display_name": "", @@ -1371,14 +1372,14 @@ }, "dragging": false, "height": 438, - "id": "note-smoRV", + "id": "note-ufZW4", "measured": { "height": 438, "width": 358 }, "position": { - "x": 1291.3796543651624, - "y": 549.6773427834883 + "x": 1293.169988591412, + "y": 592.6453642134813 }, "resizing": false, "selected": false, @@ -1387,7 +1388,7 @@ }, { "data": { - "id": "note-opd23", + "id": "note-trdVO", "node": { "description": "### 💡 Add your Anthropic API key here", "display_name": "", @@ -1400,7 +1401,7 @@ }, "dragging": false, "height": 324, - "id": "note-opd23", + "id": "note-trdVO", "measured": { "height": 324, "width": 361 @@ -1416,7 +1417,7 @@ }, { "data": { - "id": "note-vBwkt", + "id": "note-UU4gf", "node": { "description": "### 💡 Add your Anthropic API key here", "display_name": "", @@ -1429,14 +1430,14 @@ }, "dragging": false, "height": 324, - "id": "note-vBwkt", + "id": "note-UU4gf", "measured": { "height": 324, "width": 343 }, "position": { - "x": 2298.488837453492, - "y": 201.45217675238473 + "x": 2185.328157509855, + "y": 431.30980788789844 }, "resizing": false, "selected": false, @@ -1445,7 +1446,7 @@ }, { "data": { - "id": "StructuredOutput-tWLRa", + "id": "StructuredOutput-GJfDv", "node": { "base_classes": [ "Data", @@ -1508,7 +1509,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self) -> Data:\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n if isinstance(result, BaseModel):\n result = result.model_dump()\n if responses := result.get(\"responses\"):\n result = responses[0].model_dump()\n if result and \"objects\" in result:\n return result[\"objects\"]\n\n return result\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n\n return Data(text_key=\"results\", data={\"results\": output})\n" + "value": "from pydantic import BaseModel, Field, create_model\nfrom trustcall import create_extractor\n\nfrom langflow.base.models.chat_result import get_chat_result\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.base_model import build_model_from_schema\nfrom langflow.io import (\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TableInput,\n)\nfrom langflow.schema.data import Data\nfrom langflow.schema.table import EditMode\n\n\nclass StructuredOutputComponent(Component):\n display_name = \"Structured Output\"\n description = \"Uses an LLM to generate structured data. Ideal for extraction and consistency.\"\n name = \"StructuredOutput\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n info=\"The language model to use to generate the structured output.\",\n input_types=[\"LanguageModel\"],\n required=True,\n ),\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Input Message\",\n info=\"The input message to the language model.\",\n tool_mode=True,\n required=True,\n ),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Format Instructions\",\n info=\"The instructions to the language model for formatting the output.\",\n value=(\n \"You are an AI system designed to extract structured information from unstructured text.\"\n \"Given the input_text, return a JSON object with predefined keys based on the expected structure.\"\n \"Extract values accurately and format them according to the specified type \"\n \"(e.g., string, integer, float, date).\"\n \"If a value is missing or cannot be determined, return a default \"\n \"(e.g., null, 0, or 'N/A').\"\n \"If multiple instances of the expected structure exist within the input_text, \"\n \"stream each as a separate JSON object.\"\n ),\n required=True,\n advanced=True,\n ),\n MessageTextInput(\n name=\"schema_name\",\n display_name=\"Schema Name\",\n info=\"Provide a name for the output data schema.\",\n advanced=True,\n ),\n TableInput(\n name=\"output_schema\",\n display_name=\"Output Schema\",\n info=\"Define the structure and data types for the model's output.\",\n required=True,\n # TODO: remove deault value\n table_schema=[\n {\n \"name\": \"name\",\n \"display_name\": \"Name\",\n \"type\": \"str\",\n \"description\": \"Specify the name of the output field.\",\n \"default\": \"field\",\n \"edit_mode\": EditMode.INLINE,\n },\n {\n \"name\": \"description\",\n \"display_name\": \"Description\",\n \"type\": \"str\",\n \"description\": \"Describe the purpose of the output field.\",\n \"default\": \"description of field\",\n \"edit_mode\": EditMode.POPOVER,\n },\n {\n \"name\": \"type\",\n \"display_name\": \"Type\",\n \"type\": \"str\",\n \"edit_mode\": EditMode.INLINE,\n \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n \"default\": \"str\",\n },\n {\n \"name\": \"multiple\",\n \"display_name\": \"As List\",\n \"type\": \"boolean\",\n \"description\": \"Set to True if this output field should be a list of the specified type.\",\n \"default\": \"False\",\n \"edit_mode\": EditMode.INLINE,\n },\n ],\n value=[\n {\n \"name\": \"field\",\n \"description\": \"description of field\",\n \"type\": \"str\",\n \"multiple\": \"False\",\n }\n ],\n ),\n ]\n\n outputs = [\n Output(\n name=\"structured_output\",\n display_name=\"Structured Output\",\n method=\"build_structured_output\",\n ),\n ]\n\n def build_structured_output_base(self):\n schema_name = self.schema_name or \"OutputModel\"\n\n if not hasattr(self.llm, \"with_structured_output\"):\n msg = \"Language model does not support structured output.\"\n raise TypeError(msg)\n if not self.output_schema:\n msg = \"Output schema cannot be empty\"\n raise ValueError(msg)\n\n output_model_ = build_model_from_schema(self.output_schema)\n\n output_model = create_model(\n schema_name,\n __doc__=f\"A list of {schema_name}.\",\n objects=(list[output_model_], Field(description=f\"A list of {schema_name}.\")), # type: ignore[valid-type]\n )\n\n try:\n llm_with_structured_output = create_extractor(self.llm, tools=[output_model])\n except NotImplementedError as exc:\n msg = f\"{self.llm.__class__.__name__} does not support structured output.\"\n raise TypeError(msg) from exc\n\n config_dict = {\n \"run_name\": self.display_name,\n \"project_name\": self.get_project_name(),\n \"callbacks\": self.get_langchain_callbacks(),\n }\n result = get_chat_result(\n runnable=llm_with_structured_output,\n system_message=self.system_prompt,\n input_value=self.input_value,\n config=config_dict,\n )\n\n # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure\n # Handle non-dict responses (shouldn't happen with trustcall, but defensive)\n if not isinstance(result, dict):\n return result\n\n # Extract first response and convert BaseModel to dict\n responses = result.get(\"responses\", [])\n if not responses:\n return result\n\n # Convert BaseModel to dict (creates the \"objects\" key)\n first_response = responses[0]\n structured_data = first_response.model_dump() if isinstance(first_response, BaseModel) else first_response\n\n # Extract the objects array (guaranteed to exist due to our Pydantic model structure)\n return structured_data.get(\"objects\", structured_data)\n\n def build_structured_output(self) -> Data:\n output = self.build_structured_output_base()\n if not isinstance(output, list) or not output:\n # handle empty or unexpected type case\n msg = \"No structured output returned\"\n raise ValueError(msg)\n if len(output) != 1:\n msg = \"Multiple structured outputs returned\"\n raise ValueError(msg)\n return Data(data=output[0])\n" }, "input_value": { "_input_type": "MessageTextInput", @@ -1768,9 +1769,9 @@ "type": "StructuredOutput" }, "dragging": false, - "id": "StructuredOutput-tWLRa", + "id": "StructuredOutput-GJfDv", "measured": { - "height": 447, + "height": 349, "width": 320 }, "position": { @@ -1782,7 +1783,7 @@ }, { "data": { - "id": "File-v8ouW", + "id": "File-nhzgk", "node": { "base_classes": [ "Data" @@ -2065,21 +2066,21 @@ "type": "File" }, "dragging": false, - "id": "File-v8ouW", + "id": "File-nhzgk", "measured": { - "height": 336, + "height": 230, "width": 320 }, "position": { - "x": -103.41952640765028, - "y": 527.2136756214662 + "x": 313.61127452987654, + "y": 566.1365503756354 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "ParserComponent-wrPyP", + "id": "ParserComponent-FPw08", "node": { "base_classes": [ "Message" @@ -2242,21 +2243,21 @@ "type": "ParserComponent" }, "dragging": false, - "id": "ParserComponent-wrPyP", + "id": "ParserComponent-FPw08", "measured": { - "height": 357, + "height": 329, "width": 320 }, "position": { - "x": 299.19957920557835, - "y": 511.92640315784786 + "x": 705.1095587847711, + "y": 565.677039723129 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "parser-ifKFs", + "id": "parser-8Jyry", "node": { "base_classes": [ "Message" @@ -2418,9 +2419,9 @@ "type": "parser" }, "dragging": false, - "id": "parser-ifKFs", + "id": "parser-8Jyry", "measured": { - "height": 312, + "height": 278, "width": 320 }, "position": { @@ -2432,16 +2433,16 @@ } ], "viewport": { - "x": 54.17007235163055, - "y": 267.40142033867886, - "zoom": 0.4655801942877969 + "x": -459.3293356910542, + "y": 141.83609996213664, + "zoom": 0.5655674747790203 } }, "description": "This template transforms PDF or TXT resume documents into structured JSON, generating a portfolio website HTML code from the structured data.", "endpoint_name": null, - "id": "7e497e5b-3b0f-4b11-9cd5-8662efad990b", + "id": "0d96753b-833e-48ca-8548-ca82b8ab6823", "is_component": false, - "last_tested_version": "1.2.0", + "last_tested_version": "1.4.3", "name": "Portfolio Website Code Generator", "tags": [ "chatbots", diff --git a/src/backend/tests/unit/components/processing/test_structured_output_component.py b/src/backend/tests/unit/components/processing/test_structured_output_component.py index 207d1988f..a650174c7 100644 --- a/src/backend/tests/unit/components/processing/test_structured_output_component.py +++ b/src/backend/tests/unit/components/processing/test_structured_output_component.py @@ -42,7 +42,13 @@ class TestStructuredOutputComponent(ComponentTestBaseWithoutClient): def model_dump(self, **__): return {"objects": [{"field": "value"}]} - return MockBaseModel() + # Return trustcall-style response structure + return { + "messages": ["mock_message"], + "responses": [MockBaseModel()], + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } component = StructuredOutputComponent( llm=MockLanguageModel(), @@ -185,11 +191,16 @@ class TestStructuredOutputComponent(ComponentTestBaseWithoutClient): def model_dump(self, **__): return {"objects": self.objects} - mock_llm = MockLanguageModel() - mock_get_chat_result.return_value = ParentModel() + # Update to return trustcall-style response + mock_get_chat_result.return_value = { + "messages": ["mock_message"], + "responses": [ParentModel()], + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } component = StructuredOutputComponent( - llm=mock_llm, + llm=MockLanguageModel(), input_value="Test input", schema_name="NestedSchema", output_schema=[ @@ -218,7 +229,13 @@ class TestStructuredOutputComponent(ComponentTestBaseWithoutClient): def model_dump(self, **__): return {"objects": self.objects} - mock_get_chat_result.return_value = MockBaseModel() + # Update to return trustcall-style response + mock_get_chat_result.return_value = { + "messages": ["mock_message"], + "responses": [MockBaseModel()], + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } component = StructuredOutputComponent( llm=MockLanguageModel(), @@ -291,8 +308,16 @@ class TestStructuredOutputComponent(ComponentTestBaseWithoutClient): with pytest.raises(openai.BadRequestError) as exc_info: component.build_structured_output_base() - # Verify the error message contains expected content - assert "max_tokens was reached" in str(exc_info.value) + # Verify the error message contains expected content (updated to match actual OpenAI error format) + error_message = str(exc_info.value) + assert any( + phrase in error_message + for phrase in [ + "max_tokens was reached", + "max_tokens or model output limit was reached", + "Could not finish the message because max_tokens", + ] + ), f"Expected max_tokens error but got: {error_message}" @pytest.mark.skipif( "OPENAI_API_KEY" not in os.environ, @@ -438,3 +463,251 @@ class TestStructuredOutputComponent(ComponentTestBaseWithoutClient): # The test is expected to fail with a 400 Bad Request error with pytest.raises(Exception, match="400 Bad Request"): component.build_structured_output_base() + + def test_structured_output_returns_dict_when_no_objects_key(self): + """Test that when trustcall returns a dict without 'objects' key, we return the dict directly.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + # Return trustcall-style response but without BaseModel that creates "objects" key + return { + "messages": ["mock_message"], + "responses": [{"field": "value", "another_field": "another_value"}], # Direct dict, not BaseModel + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Test input", + schema_name="TestSchema", + output_schema=[{"name": "field", "type": "str", "description": "A test field"}], + multiple=False, + system_prompt="Test system prompt", + ) + + with patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result): + result = component.build_structured_output_base() + # Should return the dict directly since there's no "objects" key + assert isinstance(result, dict) + assert result == {"field": "value", "another_field": "another_value"} + + def test_structured_output_returns_direct_response_when_not_dict(self): + """Test that when trustcall returns a non-dict response, we return it directly.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + # Return a string response (edge case) + return "Simple string response" + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Test input", + schema_name="TestSchema", + output_schema=[{"name": "field", "type": "str", "description": "A test field"}], + multiple=False, + system_prompt="Test system prompt", + ) + + with patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result): + result = component.build_structured_output_base() + # Should return the string directly + assert isinstance(result, str) + assert result == "Simple string response" + + def test_structured_output_handles_empty_responses_array(self): + """Test that when trustcall returns empty responses array, we return the result dict.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + # Return trustcall-style response with empty responses + return { + "messages": ["mock_message"], + "responses": [], # Empty responses array + "response_metadata": [], + "attempts": 1, + "fallback_data": {"field": "fallback_value"}, # Some other data in the result + } + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Test input", + schema_name="TestSchema", + output_schema=[{"name": "field", "type": "str", "description": "A test field"}], + multiple=False, + system_prompt="Test system prompt", + ) + + with patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result): + result = component.build_structured_output_base() + # Should return the entire result dict when responses is empty + assert isinstance(result, dict) + assert "messages" in result + assert "responses" in result + assert "fallback_data" in result + + def test_build_structured_output_fails_when_base_returns_non_list(self): + """Test that build_structured_output() fails when base method returns non-list.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + # Return a dict instead of list with objects + return { + "messages": ["mock_message"], + "responses": [{"single_item": "value"}], # Dict without "objects" key + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Test input", + schema_name="TestSchema", + output_schema=[{"name": "field", "type": "str", "description": "A test field"}], + multiple=False, + system_prompt="Test system prompt", + ) + + with ( + patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result), + pytest.raises(ValueError, match="No structured output returned"), + ): + component.build_structured_output() + + def test_build_structured_output_returns_data_with_dict(self): + """Test that build_structured_output() returns Data object with dict data.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + class MockBaseModel(BaseModel): + def model_dump(self, **__): + return {"objects": [{"field": "value2", "number": 24}]} # Return only one object + + # Return trustcall-style response structure + return { + "messages": ["mock_message"], + "responses": [MockBaseModel()], + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Test input", + schema_name="TestSchema", + output_schema=[ + {"name": "field", "type": "str", "description": "A test field"}, + {"name": "number", "type": "int", "description": "A test number"}, + ], + multiple=False, + system_prompt="Test system prompt", + ) + + with patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result): + result = component.build_structured_output() + + # Check that result is a Data object + from langflow.schema.data import Data + + assert isinstance(result, Data) + + # Check that result.data is a dict + assert isinstance(result.data, dict) + + # Check the content of the dict + assert result.data == {"field": "value2", "number": 24} + + # Verify the data has the expected keys + assert "field" in result.data + assert "number" in result.data + assert result.data["field"] == "value2" + assert result.data["number"] == 24 + + def test_build_structured_output_returns_data_with_single_item(self): + """Test that build_structured_output() returns Data object when only one item in objects.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + class MockBaseModel(BaseModel): + def model_dump(self, **__): + return {"objects": [{"name": "John Doe", "age": 30}]} + + return { + "messages": ["mock_message"], + "responses": [MockBaseModel()], + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Extract name and age from: John Doe is 30 years old", + schema_name="PersonInfo", + output_schema=[ + {"name": "name", "type": "str", "description": "Person's name"}, + {"name": "age", "type": "int", "description": "Person's age"}, + ], + multiple=False, + system_prompt="Extract person info", + ) + + with patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result): + result = component.build_structured_output() + + # Check that result is a Data object + from langflow.schema.data import Data + + assert isinstance(result, Data) + + # Check that result.data is a dict + assert isinstance(result.data, dict) + + # Check the content matches exactly + assert result.data == {"name": "John Doe", "age": 30} + + def test_build_structured_output_data_object_properties(self): + """Test that the returned Data object has proper properties.""" + + def mock_get_chat_result(runnable, system_message, input_value, config): # noqa: ARG001 + class MockBaseModel(BaseModel): + def model_dump(self, **__): + return {"objects": [{"product": "iPhone", "price": 999.99, "available": True}]} + + return { + "messages": ["mock_message"], + "responses": [MockBaseModel()], + "response_metadata": [{"id": "mock_id"}], + "attempts": 1, + } + + component = StructuredOutputComponent( + llm=MockLanguageModel(), + input_value="Product info: iPhone costs $999.99 and is available", + schema_name="ProductInfo", + output_schema=[ + {"name": "product", "type": "str", "description": "Product name"}, + {"name": "price", "type": "float", "description": "Product price"}, + {"name": "available", "type": "bool", "description": "Product availability"}, + ], + multiple=False, + system_prompt="Extract product info", + ) + + with patch("langflow.components.processing.structured_output.get_chat_result", mock_get_chat_result): + result = component.build_structured_output() + + # Check that result is a Data object + from langflow.schema.data import Data + + assert isinstance(result, Data) + + # Check that result.data is a dict with correct types + assert isinstance(result.data, dict) + assert isinstance(result.data["product"], str) + assert isinstance(result.data["price"], float) + assert isinstance(result.data["available"], bool) + + # Check values + assert result.data["product"] == "iPhone" + assert result.data["price"] == 999.99 + assert result.data["available"] is True + + # Test Data object methods if they exist + if hasattr(result, "get_text"): + # Data object should be able to represent itself as text + text_repr = result.get_text() + assert isinstance(text_repr, str)