fix: disable upload message when ingesting on onboarding, wait for file to be ingested, added knowledge filters on nudges (#345)

* Removed upload start message

* Made onboarding upload refetch nudges and only finish when document is ingested

* Implemented query filters on nudges

* changed get to post

* Implemented filtering for documents that are not sample data on nudges

---------

Co-authored-by: Sebastián Estévez <estevezsebastian@gmail.com>
This commit is contained in:
Lucas Oliveira 2025-11-11 18:20:39 -03:00 committed by GitHub
parent d97c41bd7f
commit a5d25e0c0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 447 additions and 133 deletions

View file

@ -7,7 +7,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "EmbeddingModel", "dataType": "EmbeddingModel",
"id": "EmbeddingModel-eZ6bT", "id": "EmbeddingModel-rofSg",
"name": "embeddings", "name": "embeddings",
"output_types": [ "output_types": [
"Embeddings" "Embeddings"
@ -15,19 +15,19 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "embedding", "fieldName": "embedding",
"id": "OpenSearch-iYfjf", "id": "OpenSearchVectorStoreComponent-JA880",
"inputTypes": [ "inputTypes": [
"Embeddings" "Embeddings"
], ],
"type": "other" "type": "other"
} }
}, },
"id": "xy-edge__EmbeddingModel-eZ6bT{œdataTypeœ:œEmbeddingModelœ,œidœ:œEmbeddingModel-eZ6bTœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}-OpenSearch-iYfjf{œfieldNameœ:œembeddingœ,œidœ:œOpenSearch-iYfjfœ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}", "id": "reactflow__edge-EmbeddingModel-rofSg{œdataTypeœ:œEmbeddingModelœ,œidœ:œEmbeddingModel-rofSgœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}-OpenSearchVectorStoreComponent-JA880{œfieldNameœ:œembeddingœ,œidœ:œOpenSearchVectorStoreComponent-JA880œ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}",
"selected": false, "selected": false,
"source": "EmbeddingModel-eZ6bT", "source": "EmbeddingModel-rofSg",
"sourceHandle": "{œdataTypeœ:œEmbeddingModelœ,œidœ:œEmbeddingModel-eZ6bTœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}", "sourceHandle": "{œdataTypeœ:œEmbeddingModelœ,œidœ:œEmbeddingModel-rofSgœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}",
"target": "OpenSearch-iYfjf", "target": "OpenSearchVectorStoreComponent-JA880",
"targetHandle": "{œfieldNameœ:œembeddingœ,œidœ:œOpenSearch-iYfjfœ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}" "targetHandle": "{œfieldNameœ:œembeddingœ,œidœ:œOpenSearchVectorStoreComponent-JA880œ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}"
}, },
{ {
"animated": false, "animated": false,
@ -35,7 +35,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "ParserComponent", "dataType": "ParserComponent",
"id": "ParserComponent-tZs7s", "id": "ParserComponent-uMBcK",
"name": "parsed_text", "name": "parsed_text",
"output_types": [ "output_types": [
"Message" "Message"
@ -43,19 +43,19 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "docs", "fieldName": "docs",
"id": "Prompt Template-Wo6kR", "id": "Prompt Template-OoRfU",
"inputTypes": [ "inputTypes": [
"Message" "Message"
], ],
"type": "str" "type": "str"
} }
}, },
"id": "xy-edge__ParserComponent-tZs7s{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-tZs7sœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt Template-Wo6kR{œfieldNameœ:œdocsœ,œidœ:œPrompt Template-Wo6kRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "id": "reactflow__edge-ParserComponent-uMBcK{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-uMBcKœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt Template-OoRfU{œfieldNameœ:œdocsœ,œidœ:œPrompt Template-OoRfUœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"selected": false, "selected": false,
"source": "ParserComponent-tZs7s", "source": "ParserComponent-uMBcK",
"sourceHandle": "{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-tZs7sœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}", "sourceHandle": "{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-uMBcKœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}",
"target": "Prompt Template-Wo6kR", "target": "Prompt Template-OoRfU",
"targetHandle": "{œfieldNameœ:œdocsœ,œidœ:œPrompt Template-Wo6kRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" "targetHandle": "{œfieldNameœ:œdocsœ,œidœ:œPrompt Template-OoRfUœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}"
}, },
{ {
"animated": false, "animated": false,
@ -63,7 +63,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "ChatInput", "dataType": "ChatInput",
"id": "ChatInput-bqH7H", "id": "ChatInput-kYraY",
"name": "message", "name": "message",
"output_types": [ "output_types": [
"Message" "Message"
@ -71,19 +71,19 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "prompt", "fieldName": "prompt",
"id": "Prompt Template-Wo6kR", "id": "Prompt Template-OoRfU",
"inputTypes": [ "inputTypes": [
"Message" "Message"
], ],
"type": "str" "type": "str"
} }
}, },
"id": "xy-edge__ChatInput-bqH7H{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bqH7Hœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt Template-Wo6kR{œfieldNameœ:œpromptœ,œidœ:œPrompt Template-Wo6kRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "id": "reactflow__edge-ChatInput-kYraY{œdataTypeœ:œChatInputœ,œidœ:œChatInput-kYraYœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt Template-OoRfU{œfieldNameœ:œpromptœ,œidœ:œPrompt Template-OoRfUœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"selected": false, "selected": false,
"source": "ChatInput-bqH7H", "source": "ChatInput-kYraY",
"sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bqH7Hœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-kYraYœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}",
"target": "Prompt Template-Wo6kR", "target": "Prompt Template-OoRfU",
"targetHandle": "{œfieldNameœ:œpromptœ,œidœ:œPrompt Template-Wo6kRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" "targetHandle": "{œfieldNameœ:œpromptœ,œidœ:œPrompt Template-OoRfUœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}"
}, },
{ {
"animated": false, "animated": false,
@ -91,7 +91,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "LanguageModelComponent", "dataType": "LanguageModelComponent",
"id": "LanguageModelComponent-NSTA6", "id": "LanguageModelComponent-dxtYP",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": [
"Message" "Message"
@ -99,7 +99,7 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-BMVN5", "id": "ChatOutput-S4nKr",
"inputTypes": [ "inputTypes": [
"Data", "Data",
"DataFrame", "DataFrame",
@ -108,12 +108,12 @@
"type": "other" "type": "other"
} }
}, },
"id": "xy-edge__LanguageModelComponent-NSTA6{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-NSTA6œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-BMVN5{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-BMVN5œ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "id": "reactflow__edge-LanguageModelComponent-dxtYP{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-dxtYPœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-S4nKr{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-S4nKrœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}",
"selected": false, "selected": false,
"source": "LanguageModelComponent-NSTA6", "source": "LanguageModelComponent-dxtYP",
"sourceHandle": "{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-NSTA6œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}", "sourceHandle": "{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-dxtYPœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}",
"target": "ChatOutput-BMVN5", "target": "ChatOutput-S4nKr",
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-BMVN5œ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}" "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-S4nKrœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}"
}, },
{ {
"animated": false, "animated": false,
@ -121,7 +121,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "Prompt Template", "dataType": "Prompt Template",
"id": "Prompt Template-Wo6kR", "id": "Prompt Template-OoRfU",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": [
"Message" "Message"
@ -129,19 +129,19 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "LanguageModelComponent-NSTA6", "id": "LanguageModelComponent-dxtYP",
"inputTypes": [ "inputTypes": [
"Message" "Message"
], ],
"type": "str" "type": "str"
} }
}, },
"id": "xy-edge__Prompt Template-Wo6kR{œdataTypeœ:œPrompt Templateœ,œidœ:œPrompt Template-Wo6kRœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-NSTA6{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-NSTA6œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "id": "reactflow__edge-Prompt Template-OoRfU{œdataTypeœ:œPrompt Templateœ,œidœ:œPrompt Template-OoRfUœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-dxtYP{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-dxtYPœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"selected": false, "selected": false,
"source": "Prompt Template-Wo6kR", "source": "Prompt Template-OoRfU",
"sourceHandle": "{œdataTypeœ:œPrompt Templateœ,œidœ:œPrompt Template-Wo6kRœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", "sourceHandle": "{œdataTypeœ:œPrompt Templateœ,œidœ:œPrompt Template-OoRfUœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}",
"target": "LanguageModelComponent-NSTA6", "target": "LanguageModelComponent-dxtYP",
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-NSTA6œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-dxtYPœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}"
}, },
{ {
"animated": false, "animated": false,
@ -149,7 +149,7 @@
"data": { "data": {
"sourceHandle": { "sourceHandle": {
"dataType": "OpenSearchVectorStoreComponent", "dataType": "OpenSearchVectorStoreComponent",
"id": "OpenSearch-iYfjf", "id": "OpenSearchVectorStoreComponent-JA880",
"name": "dataframe", "name": "dataframe",
"output_types": [ "output_types": [
"DataFrame" "DataFrame"
@ -157,7 +157,7 @@
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_data", "fieldName": "input_data",
"id": "ParserComponent-tZs7s", "id": "ParserComponent-uMBcK",
"inputTypes": [ "inputTypes": [
"DataFrame", "DataFrame",
"Data" "Data"
@ -165,12 +165,39 @@
"type": "other" "type": "other"
} }
}, },
"id": "xy-edge__OpenSearch-iYfjf{œdataTypeœ:œOpenSearchVectorStoreComponentœ,œidœ:œOpenSearch-iYfjfœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-ParserComponent-tZs7s{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-tZs7sœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "id": "reactflow__edge-OpenSearchVectorStoreComponent-JA880{œdataTypeœ:œOpenSearchVectorStoreComponentœ,œidœ:œOpenSearchVectorStoreComponent-JA880œ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-ParserComponent-uMBcK{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-uMBcKœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}",
"selected": false, "selected": false,
"source": "OpenSearch-iYfjf", "source": "OpenSearchVectorStoreComponent-JA880",
"sourceHandle": "{œdataTypeœ:œOpenSearchVectorStoreComponentœ,œidœ:œOpenSearch-iYfjfœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}", "sourceHandle": "{œdataTypeœ:œOpenSearchVectorStoreComponentœ,œidœ:œOpenSearchVectorStoreComponent-JA880œ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}",
"target": "ParserComponent-tZs7s", "target": "ParserComponent-uMBcK",
"targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-tZs7sœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}" "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-uMBcKœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}"
},
{
"animated": false,
"data": {
"sourceHandle": {
"dataType": "TextInput",
"id": "TextInput-OcjB6",
"name": "text",
"output_types": [
"Message"
]
},
"targetHandle": {
"fieldName": "filter_expression",
"id": "OpenSearchVectorStoreComponent-JA880",
"inputTypes": [
"Message"
],
"type": "str"
}
},
"id": "xy-edge__TextInput-OcjB6{œdataTypeœ:œTextInputœ,œidœ:œTextInput-OcjB6œ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-OpenSearchVectorStoreComponent-JA880{œfieldNameœ:œfilter_expressionœ,œidœ:œOpenSearchVectorStoreComponent-JA880œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"selected": false,
"source": "TextInput-OcjB6",
"sourceHandle": "{œdataTypeœ:œTextInputœ,œidœ:œTextInput-OcjB6œ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}",
"target": "OpenSearchVectorStoreComponent-JA880",
"targetHandle": "{œfieldNameœ:œfilter_expressionœ,œidœ:œOpenSearchVectorStoreComponent-JA880œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}"
} }
], ],
"nodes": [ "nodes": [
@ -178,7 +205,7 @@
"data": { "data": {
"description": "Get chat inputs from the Playground.", "description": "Get chat inputs from the Playground.",
"display_name": "Chat Input", "display_name": "Chat Input",
"id": "ChatInput-bqH7H", "id": "ChatInput-kYraY",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -419,9 +446,9 @@
"type": "ChatInput" "type": "ChatInput"
}, },
"dragging": false, "dragging": false,
"id": "ChatInput-bqH7H", "id": "ChatInput-kYraY",
"measured": { "measured": {
"height": 48, "height": 57,
"width": 192 "width": 192
}, },
"position": { "position": {
@ -435,7 +462,7 @@
"data": { "data": {
"description": "Display a chat message in the Playground.", "description": "Display a chat message in the Playground.",
"display_name": "Chat Output", "display_name": "Chat Output",
"id": "ChatOutput-BMVN5", "id": "ChatOutput-S4nKr",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -680,9 +707,9 @@
"type": "ChatOutput" "type": "ChatOutput"
}, },
"dragging": false, "dragging": false,
"id": "ChatOutput-BMVN5", "id": "ChatOutput-S4nKr",
"measured": { "measured": {
"height": 48, "height": 57,
"width": 192 "width": 192
}, },
"position": { "position": {
@ -694,7 +721,7 @@
}, },
{ {
"data": { "data": {
"id": "OpenSearch-iYfjf", "id": "OpenSearchVectorStoreComponent-JA880",
"node": { "node": {
"base_classes": [ "base_classes": [
"Data", "Data",
@ -1303,7 +1330,7 @@
"type": "OpenSearchVectorStoreComponent" "type": "OpenSearchVectorStoreComponent"
}, },
"dragging": false, "dragging": false,
"id": "OpenSearch-iYfjf", "id": "OpenSearchVectorStoreComponent-JA880",
"measured": { "measured": {
"height": 822, "height": 822,
"width": 320 "width": 320
@ -1317,7 +1344,7 @@
}, },
{ {
"data": { "data": {
"id": "EmbeddingModel-eZ6bT", "id": "EmbeddingModel-rofSg",
"node": { "node": {
"base_classes": [ "base_classes": [
"Embeddings" "Embeddings"
@ -1343,7 +1370,7 @@
], ],
"frozen": false, "frozen": false,
"icon": "binary", "icon": "binary",
"last_updated": "2025-10-01T20:14:43.010Z", "last_updated": "2025-11-03T19:42:29.170Z",
"legacy": false, "legacy": false,
"metadata": { "metadata": {
"code_hash": "8607e963fdef", "code_hash": "8607e963fdef",
@ -1614,9 +1641,9 @@
"type": "EmbeddingModel" "type": "EmbeddingModel"
}, },
"dragging": false, "dragging": false,
"id": "EmbeddingModel-eZ6bT", "id": "EmbeddingModel-rofSg",
"measured": { "measured": {
"height": 369, "height": 378,
"width": 320 "width": 320
}, },
"position": { "position": {
@ -1628,7 +1655,7 @@
}, },
{ {
"data": { "data": {
"id": "Prompt Template-Wo6kR", "id": "Prompt Template-OoRfU",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -1801,7 +1828,7 @@
"type": "Prompt Template" "type": "Prompt Template"
}, },
"dragging": false, "dragging": false,
"id": "Prompt Template-Wo6kR", "id": "Prompt Template-OoRfU",
"measured": { "measured": {
"height": 449, "height": 449,
"width": 320 "width": 320
@ -1815,7 +1842,7 @@
}, },
{ {
"data": { "data": {
"id": "ParserComponent-tZs7s", "id": "ParserComponent-uMBcK",
"node": { "node": {
"base_classes": [ "base_classes": [
"Message" "Message"
@ -1987,7 +2014,7 @@
"type": "ParserComponent" "type": "ParserComponent"
}, },
"dragging": false, "dragging": false,
"id": "ParserComponent-tZs7s", "id": "ParserComponent-uMBcK",
"measured": { "measured": {
"height": 329, "height": 329,
"width": 320 "width": 320
@ -2001,7 +2028,7 @@
}, },
{ {
"data": { "data": {
"id": "LanguageModelComponent-NSTA6", "id": "LanguageModelComponent-dxtYP",
"node": { "node": {
"base_classes": [ "base_classes": [
"LanguageModel", "LanguageModel",
@ -2025,7 +2052,7 @@
], ],
"frozen": false, "frozen": false,
"icon": "brain-circuit", "icon": "brain-circuit",
"last_updated": "2025-10-01T20:14:43.010Z", "last_updated": "2025-11-03T19:42:41.996Z",
"legacy": false, "legacy": false,
"metadata": { "metadata": {
"code_hash": "bb5f8714781b", "code_hash": "bb5f8714781b",
@ -2200,7 +2227,7 @@
"tool_mode": false, "tool_mode": false,
"trace_as_metadata": true, "trace_as_metadata": true,
"type": "str", "type": "str",
"value": "gpt-4o-mini" "value": "gpt-4o"
}, },
"provider": { "provider": {
"_input_type": "DropdownInput", "_input_type": "DropdownInput",
@ -2318,7 +2345,7 @@
"type": "LanguageModelComponent" "type": "LanguageModelComponent"
}, },
"dragging": false, "dragging": false,
"id": "LanguageModelComponent-NSTA6", "id": "LanguageModelComponent-dxtYP",
"measured": { "measured": {
"height": 534, "height": 534,
"width": 320 "width": 320
@ -2329,19 +2356,119 @@
}, },
"selected": false, "selected": false,
"type": "genericNode" "type": "genericNode"
},
{
"data": {
"id": "TextInput-OcjB6",
"node": {
"base_classes": [
"Message"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {},
"description": "Get user text inputs.",
"display_name": "Text Input",
"documentation": "https://docs.langflow.org/components-io#text-input",
"edited": true,
"field_order": [
"input_value"
],
"frozen": false,
"icon": "type",
"legacy": false,
"lf_version": "1.6.0",
"metadata": {},
"minimized": false,
"output_types": [],
"outputs": [
{
"allows_loop": false,
"cache": true,
"display_name": "Output Text",
"group_outputs": false,
"hidden": null,
"method": "text_response",
"name": "text",
"options": null,
"required_inputs": null,
"selected": "Message",
"tool_mode": true,
"types": [
"Message"
],
"value": "__UNDEFINED__"
}
],
"pinned": false,
"template": {
"_type": "Component",
"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 langflow.base.io.text import TextComponent\nfrom langflow.io import SecretStrInput, Output\nfrom langflow.schema.message import Message\n\n\nclass TextInputComponent(TextComponent):\n display_name = \"Text Input\"\n description = \"Get user text inputs.\"\n documentation: str = \"https://docs.langflow.org/components-io#text-input\"\n icon = \"type\"\n name = \"TextInput\"\n\n inputs = [\n SecretStrInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Text to be passed as input.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Output Text\", name=\"text\", method=\"text_response\"),\n ]\n\n def text_response(self) -> Message:\n return Message(\n text=self.input_value,\n )\n"
},
"input_value": {
"_input_type": "SecretStrInput",
"advanced": false,
"display_name": "Text",
"dynamic": false,
"info": "Text to be passed as input.",
"input_types": [],
"load_from_db": true,
"name": "input_value",
"password": true,
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"type": "str",
"value": "OPENRAG-QUERY-FILTER"
}
},
"tool_mode": false
},
"showNode": true,
"type": "TextInput"
},
"dragging": false,
"id": "TextInput-OcjB6",
"measured": {
"height": 204,
"width": 320
},
"position": {
"x": 474.45113226548517,
"y": 697.8781609303677
},
"selected": false,
"type": "genericNode"
} }
], ],
"viewport": { "viewport": {
"x": -196.5855347896957, "x": -195.10601766576656,
"y": 78.13813811398654, "y": 70.4638443501205,
"zoom": 0.5380793988167256 "zoom": 0.5524404935324336
} }
}, },
"description": "OpenRAG OpenSearch Nudges generator, based on the OpenSearch documents and the chat history.", "description": "OpenRAG OpenSearch Nudges generator, based on the OpenSearch documents and the chat history.",
"endpoint_name": null, "endpoint_name": null,
"id": "ebc01d31-1976-46ce-a385-b0240327226c", "id": "ebc01d31-1976-46ce-a385-b0240327226c",
"is_component": false, "is_component": false,
"last_tested_version": "1.6.0", "last_tested_version": "1.6.5.dev9",
"name": "OpenRAG OpenSearch Nudges", "name": "OpenRAG OpenSearch Nudges",
"tags": [ "tags": [
"assistants", "assistants",

View file

@ -122,11 +122,6 @@ export async function uploadFile(
file: File, file: File,
replace = false replace = false
): Promise<UploadFileResult> { ): Promise<UploadFileResult> {
window.dispatchEvent(
new CustomEvent("fileUploadStart", {
detail: { filename: file.name },
})
);
try { try {
const formData = new FormData(); const formData = new FormData();

View file

@ -1,47 +1,83 @@
import { import {
useQuery, type UseQueryOptions,
useQueryClient, useQuery,
UseQueryOptions, useQueryClient,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
type Nudge = string; type Nudge = string;
const DEFAULT_NUDGES: Nudge[] = []; const DEFAULT_NUDGES: Nudge[] = [];
export interface NudgeFilters {
data_sources?: string[];
document_types?: string[];
owners?: string[];
}
export interface NudgeQueryParams {
chatId?: string | null;
filters?: NudgeFilters;
limit?: number;
scoreThreshold?: number;
}
export const useGetNudgesQuery = ( export const useGetNudgesQuery = (
chatId?: string | null, params: NudgeQueryParams | null = {},
options?: Omit<UseQueryOptions, "queryKey" | "queryFn"> options?: Omit<UseQueryOptions, "queryKey" | "queryFn">,
) => { ) => {
const queryClient = useQueryClient(); const { chatId, filters, limit, scoreThreshold } = params ?? {};
const queryClient = useQueryClient();
function cancel() { function cancel() {
queryClient.removeQueries({ queryKey: ["nudges", chatId] }); queryClient.removeQueries({ queryKey: ["nudges", chatId, filters, limit, scoreThreshold] });
} }
async function getNudges(): Promise<Nudge[]> { async function getNudges(): Promise<Nudge[]> {
try { try {
const response = await fetch(`/api/nudges${chatId ? `/${chatId}` : ""}`); const requestBody: {
const data = await response.json(); filters?: NudgeFilters;
limit?: number;
score_threshold?: number;
} = {};
if (data.response && typeof data.response === "string") { if (filters) {
return data.response.split("\n").filter(Boolean); requestBody.filters = filters;
} }
if (limit !== undefined) {
requestBody.limit = limit;
}
if (scoreThreshold !== undefined) {
requestBody.score_threshold = scoreThreshold;
}
return DEFAULT_NUDGES; const response = await fetch(`/api/nudges${chatId ? `/${chatId}` : ""}`, {
} catch (error) { method: "POST",
console.error("Error getting nudges", error); headers: {
return DEFAULT_NUDGES; "Content-Type": "application/json",
} },
} body: JSON.stringify(requestBody),
});
const data = await response.json();
const queryResult = useQuery( if (data.response && typeof data.response === "string") {
{ return data.response.split("\n").filter(Boolean);
queryKey: ["nudges", chatId], }
queryFn: getNudges,
...options,
},
queryClient
);
return { ...queryResult, cancel }; return DEFAULT_NUDGES;
} catch (error) {
console.error("Error getting nudges", error);
return DEFAULT_NUDGES;
}
}
const queryResult = useQuery(
{
queryKey: ["nudges", chatId, filters, limit, scoreThreshold],
queryFn: getNudges,
...options,
},
queryClient,
);
return { ...queryResult, cancel };
}; };

View file

@ -588,32 +588,12 @@ function ChatPage() {
setLoading(true); setLoading(true);
setIsUploading(true); setIsUploading(true);
setUploadedFile(null); // Clear previous file setUploadedFile(null); // Clear previous file
// Add initial upload message
const uploadStartMessage: Message = {
role: "assistant",
content: `🔄 Starting upload of **${filename}**...`,
timestamp: new Date(),
};
setMessages((prev) => [...prev, uploadStartMessage]);
}; };
const handleFileUploaded = (event: CustomEvent) => { const handleFileUploaded = (event: CustomEvent) => {
const { result } = event.detail; const { result } = event.detail;
console.log("Chat page received file upload event:", result); console.log("Chat page received file upload event:", result);
// Replace the last message with upload complete message
const uploadMessage: Message = {
role: "assistant",
content: `📄 Document uploaded: **${result.filename}** (${
result.pages
} pages, ${result.content_length.toLocaleString()} characters)\n\n${
result.confirmation
}`,
timestamp: new Date(),
};
setMessages((prev) => [...prev.slice(0, -1), uploadMessage]);
setUploadedFile(null); // Clear file after upload setUploadedFile(null); // Clear file after upload
// Update the response ID for this endpoint // Update the response ID for this endpoint
@ -708,8 +688,38 @@ function ChatPage() {
return () => clearInterval(interval); return () => clearInterval(interval);
}, []); }, []);
// Prepare filters for nudges (same as chat)
const processedFiltersForNudges = parsedFilterData?.filters
? (() => {
const filters = parsedFilterData.filters;
const processed: SelectedFilters = {
data_sources: [],
document_types: [],
owners: [],
};
processed.data_sources = filters.data_sources.includes("*")
? []
: filters.data_sources;
processed.document_types = filters.document_types.includes("*")
? []
: filters.document_types;
processed.owners = filters.owners.includes("*") ? [] : filters.owners;
const hasFilters =
processed.data_sources.length > 0 ||
processed.document_types.length > 0 ||
processed.owners.length > 0;
return hasFilters ? processed : undefined;
})()
: undefined;
const { data: nudges = [], cancel: cancelNudges } = useGetNudgesQuery( const { data: nudges = [], cancel: cancelNudges } = useGetNudgesQuery(
previousResponseIds[endpoint], {
chatId: previousResponseIds[endpoint],
filters: processedFiltersForNudges,
limit: parsedFilterData?.limit ?? 3,
scoreThreshold: parsedFilterData?.scoreThreshold ?? 0,
},
{ {
enabled: isOnboardingComplete, // Only fetch nudges after onboarding is complete enabled: isOnboardingComplete, // Only fetch nudges after onboarding is complete
}, },

View file

@ -1,5 +1,7 @@
import { AnimatePresence, motion } from "motion/react"; import { AnimatePresence, motion } from "motion/react";
import { type ChangeEvent, useRef, useState } from "react"; import { type ChangeEvent, useEffect, useRef, useState } from "react";
import { useGetNudgesQuery } from "@/app/api/queries/useGetNudgesQuery";
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
import { AnimatedProviderSteps } from "@/app/onboarding/components/animated-provider-steps"; import { AnimatedProviderSteps } from "@/app/onboarding/components/animated-provider-steps";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { uploadFile } from "@/lib/upload-utils"; import { uploadFile } from "@/lib/upload-utils";
@ -15,6 +17,46 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
const STEP_LIST = ["Uploading your document", "Processing your document"]; const STEP_LIST = ["Uploading your document", "Processing your document"];
// Query tasks to track completion
const { data: tasks } = useGetTasksQuery({
enabled: currentStep !== null, // Only poll when upload has started
refetchInterval: currentStep !== null ? 1000 : false, // Poll every 1 second during upload
});
const { refetch: refetchNudges } = useGetNudgesQuery(null);
// Monitor tasks and call onComplete when file processing is done
useEffect(() => {
if (currentStep === null || !tasks) {
return;
}
// Check if there are any active tasks (pending, running, or processing)
const activeTasks = tasks.find(
(task) =>
task.status === "pending" ||
task.status === "running" ||
task.status === "processing",
);
// If no active tasks and we have more than 1 task (initial + new upload), complete it
if (
(!activeTasks || (activeTasks.processed_files ?? 0) > 0) &&
tasks.length > 1
) {
// Set to final step to show "Done"
setCurrentStep(STEP_LIST.length);
// Refetch nudges to get new ones
refetchNudges();
// Wait a bit before completing
setTimeout(() => {
onComplete();
}, 1000);
}
}, [tasks, currentStep, onComplete, refetchNudges]);
const resetFileInput = () => { const resetFileInput = () => {
if (fileInputRef.current) { if (fileInputRef.current) {
fileInputRef.current.value = ""; fileInputRef.current.value = "";
@ -30,15 +72,17 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
try { try {
setCurrentStep(0); setCurrentStep(0);
await uploadFile(file, true); await uploadFile(file, true);
console.log("Document uploaded successfully"); console.log("Document upload task started successfully");
// Move to processing step - task monitoring will handle completion
setTimeout(() => {
setCurrentStep(1);
}, 1500);
} catch (error) { } catch (error) {
console.error("Upload failed", (error as Error).message); console.error("Upload failed", (error as Error).message);
// Reset on error
setCurrentStep(null);
} finally { } finally {
setIsUploading(false); setIsUploading(false);
await new Promise((resolve) => setTimeout(resolve, 1000));
setCurrentStep(STEP_LIST.length);
await new Promise((resolve) => setTimeout(resolve, 500));
onComplete();
} }
}; };

View file

@ -12,9 +12,23 @@ async def nudges_from_kb_endpoint(request: Request, chat_service, session_manage
jwt_token = session_manager.get_effective_jwt_token(user_id, request.state.jwt_token) jwt_token = session_manager.get_effective_jwt_token(user_id, request.state.jwt_token)
try: try:
# Parse request body for filters
body = {}
try:
body = await request.json()
except Exception:
body = {}
filters = body.get("filters")
limit = body.get("limit")
score_threshold = body.get("score_threshold")
result = await chat_service.langflow_nudges_chat( result = await chat_service.langflow_nudges_chat(
user_id, user_id,
jwt_token, jwt_token,
filters=filters,
limit=limit,
score_threshold=score_threshold,
) )
return JSONResponse(result) return JSONResponse(result)
except Exception as e: except Exception as e:
@ -32,10 +46,24 @@ async def nudges_from_chat_id_endpoint(request: Request, chat_service, session_m
jwt_token = session_manager.get_effective_jwt_token(user_id, request.state.jwt_token) jwt_token = session_manager.get_effective_jwt_token(user_id, request.state.jwt_token)
try: try:
# Parse request body for filters
body = {}
try:
body = await request.json()
except Exception:
body = {}
filters = body.get("filters")
limit = body.get("limit")
score_threshold = body.get("score_threshold")
result = await chat_service.langflow_nudges_chat( result = await chat_service.langflow_nudges_chat(
user_id, user_id,
jwt_token, jwt_token,
previous_response_id=chat_id, previous_response_id=chat_id,
filters=filters,
limit=limit,
score_threshold=score_threshold,
) )
return JSONResponse(result) return JSONResponse(result)
except Exception as e: except Exception as e:

View file

@ -370,6 +370,7 @@ async def _ingest_default_documents_langflow(services, file_paths):
{"key": "owner_name", "value": anonymous_user.name}, {"key": "owner_name", "value": anonymous_user.name},
{"key": "owner_email", "value": anonymous_user.email}, {"key": "owner_email", "value": anonymous_user.email},
{"key": "connector_type", "value": "system_default"}, {"key": "connector_type", "value": "system_default"},
{"key": "is_sample_data", "value": "true"},
] ]
} }
} }
@ -413,6 +414,7 @@ async def _ingest_default_documents_openrag(services, file_paths):
jwt_token=None, jwt_token=None,
owner_name=None, owner_name=None,
owner_email=None, owner_email=None,
is_sample_data=True, # Mark as sample data
) )
task_id = await services["task_service"].create_custom_task( task_id = await services["task_service"].create_custom_task(
@ -1057,7 +1059,7 @@ async def create_app():
session_manager=services["session_manager"], session_manager=services["session_manager"],
) )
), ),
methods=["GET"], methods=["POST"],
), ),
Route( Route(
"/nudges/{chat_id}", "/nudges/{chat_id}",
@ -1068,7 +1070,7 @@ async def create_app():
session_manager=services["session_manager"], session_manager=services["session_manager"],
) )
), ),
methods=["GET"], methods=["POST"],
), ),
Route( Route(
"/reset-flow/{flow_type}", "/reset-flow/{flow_type}",

View file

@ -157,6 +157,7 @@ class TaskProcessor:
file_size: int = None, file_size: int = None,
connector_type: str = "local", connector_type: str = "local",
embedding_model: str = None, embedding_model: str = None,
is_sample_data: bool = False,
): ):
""" """
Standard processing pipeline for non-Langflow processors: Standard processing pipeline for non-Langflow processors:
@ -240,6 +241,10 @@ class TaskProcessor:
chunk_doc["owner_name"] = owner_name chunk_doc["owner_name"] = owner_name
if owner_email is not None: if owner_email is not None:
chunk_doc["owner_email"] = owner_email chunk_doc["owner_email"] = owner_email
# Mark as sample data if specified
if is_sample_data:
chunk_doc["is_sample_data"] = "true"
chunk_id = f"{file_hash}_{i}" chunk_id = f"{file_hash}_{i}"
try: try:
await opensearch_client.index( await opensearch_client.index(
@ -286,12 +291,14 @@ class DocumentFileProcessor(TaskProcessor):
jwt_token: str = None, jwt_token: str = None,
owner_name: str = None, owner_name: str = None,
owner_email: str = None, owner_email: str = None,
is_sample_data: bool = False,
): ):
super().__init__(document_service) super().__init__(document_service)
self.owner_user_id = owner_user_id self.owner_user_id = owner_user_id
self.jwt_token = jwt_token self.jwt_token = jwt_token
self.owner_name = owner_name self.owner_name = owner_name
self.owner_email = owner_email self.owner_email = owner_email
self.is_sample_data = is_sample_data
async def process_item( async def process_item(
self, upload_task: UploadTask, item: str, file_task: FileTask self, upload_task: UploadTask, item: str, file_task: FileTask
@ -326,6 +333,7 @@ class DocumentFileProcessor(TaskProcessor):
owner_email=self.owner_email, owner_email=self.owner_email,
file_size=file_size, file_size=file_size,
connector_type="local", connector_type="local",
is_sample_data=self.is_sample_data,
) )
file_task.status = TaskStatus.COMPLETED file_task.status = TaskStatus.COMPLETED

View file

@ -158,8 +158,11 @@ class ChatService:
user_id: str = None, user_id: str = None,
jwt_token: str = None, jwt_token: str = None,
previous_response_id: str = None, previous_response_id: str = None,
filters: dict = None,
limit: int = None,
score_threshold: float = None,
): ):
"""Handle Langflow chat requests""" """Handle Langflow nudges chat requests with knowledge filters"""
if not LANGFLOW_URL or not NUDGES_FLOW_ID: if not LANGFLOW_URL or not NUDGES_FLOW_ID:
raise ValueError( raise ValueError(
@ -171,6 +174,67 @@ class ChatService:
if jwt_token: if jwt_token:
extra_headers["X-LANGFLOW-GLOBAL-VAR-JWT"] = jwt_token extra_headers["X-LANGFLOW-GLOBAL-VAR-JWT"] = jwt_token
# Build the complete filter expression like the chat service does
filter_expression = {}
has_user_filters = False
filter_clauses = []
if filters:
# Map frontend filter names to backend field names
field_mapping = {
"data_sources": "filename",
"document_types": "mimetype",
"owners": "owner",
}
for filter_key, values in filters.items():
if values is not None and isinstance(values, list) and len(values) > 0:
# Map frontend key to backend field name
field_name = field_mapping.get(filter_key, filter_key)
if len(values) == 1:
# Single value filter
filter_clauses.append({"term": {field_name: values[0]}})
else:
# Multiple values filter
filter_clauses.append({"terms": {field_name: values}})
if filter_clauses:
has_user_filters = True
# If no user filters are active, exclude sample data from nudges
if not has_user_filters:
# Add a bool query with must_not to exclude sample data
filter_clauses.append({
"bool": {
"must_not": [
{"term": {"is_sample_data": "true"}}
]
}
})
logger.info("Excluding sample data from nudges (no user filters active)")
# Set the filter clauses if we have any
if filter_clauses:
filter_expression["filter"] = filter_clauses
# Add limit and score threshold to the filter expression (only if different from defaults)
if limit and limit != 10: # 10 is the default limit
filter_expression["limit"] = limit
if score_threshold and score_threshold != 0: # 0 is the default threshold
filter_expression["score_threshold"] = score_threshold
# Pass the complete filter expression as a single header to Langflow (only if we have something to send)
logger.info(
"Sending OpenRAG query filter to Langflow nudges",
filter_expression=filter_expression,
)
extra_headers["X-LANGFLOW-GLOBAL-VAR-OPENRAG-QUERY-FILTER"] = json.dumps(
filter_expression
)
logger.info(f"[NUDGES] Extra headers {extra_headers}")
# Ensure the Langflow client exists; try lazy init if needed # Ensure the Langflow client exists; try lazy init if needed
langflow_client = await clients.ensure_langflow_client() langflow_client = await clients.ensure_langflow_client()
if not langflow_client: if not langflow_client: