From 151fe3d02ed9b0bf3f2af32023136d2f650cdbe6 Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 14:29:33 +0800 Subject: [PATCH 1/7] feat(end_stream_processor): support streaming output of all configured variables in End node - Iterate all variable selectors for all end nodes and stream if matched - Add detailed English comments explaining each step of the logic - This enables streaming output for multiple variables (e.g., text, text1, text2...) instead of only the first one --- .../nodes/end/end_stream_processor.py | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/api/core/workflow/nodes/end/end_stream_processor.py b/api/core/workflow/nodes/end/end_stream_processor.py index a6fb2ffc18..fcf4011dae 100644 --- a/api/core/workflow/nodes/end/end_stream_processor.py +++ b/api/core/workflow/nodes/end/end_stream_processor.py @@ -42,23 +42,26 @@ class EndStreamProcessor(StreamProcessor): yield event continue - if event.route_node_state.node_id in self.current_stream_chunk_generating_node_ids: - stream_out_end_node_ids = self.current_stream_chunk_generating_node_ids[ - event.route_node_state.node_id - ] - else: - stream_out_end_node_ids = self._get_stream_out_end_node_ids(event) - self.current_stream_chunk_generating_node_ids[event.route_node_state.node_id] = ( - stream_out_end_node_ids - ) - - if stream_out_end_node_ids: - if self.has_output and event.node_id not in self.output_node_ids: - event.chunk_content = "\n" + event.chunk_content - - self.output_node_ids.add(event.node_id) - self.has_output = True - yield event + # Iterate all variable selectors for all end nodes; stream if matched + matched = False # Flag to indicate if a match is found + for end_node_id, value_selectors_list in self.end_stream_param.end_stream_variable_selector_mapping.items(): + # Loop through all variable selectors configured for the current end node + for value_selector in value_selectors_list: + # Check if the current event's from_variable_selector matches the configured selector + if event.from_variable_selector == value_selector: + # If there was previous output and this node hasn't output yet, prepend a newline + if self.has_output and event.node_id not in self.output_node_ids: + event.chunk_content = "\n" + event.chunk_content + # Mark this node as having output to avoid duplicate newlines + self.output_node_ids.add(event.node_id) + # Set the flag indicating output has occurred + self.has_output = True + # Stream the current event + yield event + matched = True # Set matched flag to break out of loops + break # Exit inner loop after match + if matched: + break # Exit outer loop after match elif isinstance(event, NodeRunSucceededEvent): yield event if event.route_node_state.node_id in self.current_stream_chunk_generating_node_ids: From 7dcfc171cc619501c1d96c8c8fd50b7be42e65f1 Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 14:59:58 +0800 Subject: [PATCH 2/7] feat(docker): ensure patch is only applied once at container startup\n\n- Use git apply --check before applying my_endnode_streaming.patch in api and worker services\n- Prevents errors from repeated patch application\n- Improves robustness for automated deployment with custom patches --- docker/docker-compose.yaml | 18 ++++++++++++-- my_endnode_streaming.patch | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 my_endnode_streaming.patch diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 7f91fd8796..ab3bfbf8a7 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -537,8 +537,15 @@ services: redis: condition: service_started volumes: - # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage + - ../my_endnode_streaming.patch:/app/my_endnode_streaming.patch + command: > + sh -c "if [ -f /app/my_endnode_streaming.patch ]; then \ + cd /app && \ + git apply --check my_endnode_streaming.patch && \ + git apply my_endnode_streaming.patch || echo 'Patch already applied or not applicable'; \ + fi; \ + exec /start-api.sh" networks: - ssrf_proxy_network - default @@ -564,8 +571,15 @@ services: redis: condition: service_started volumes: - # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage + - ../my_endnode_streaming.patch:/app/my_endnode_streaming.patch + command: > + sh -c "if [ -f /app/my_endnode_streaming.patch ]; then \ + cd /app && \ + git apply --check my_endnode_streaming.patch && \ + git apply my_endnode_streaming.patch || echo 'Patch already applied or not applicable'; \ + fi; \ + exec /start-worker.sh" networks: - ssrf_proxy_network - default diff --git a/my_endnode_streaming.patch b/my_endnode_streaming.patch new file mode 100644 index 0000000000..f7cebaa905 --- /dev/null +++ b/my_endnode_streaming.patch @@ -0,0 +1,48 @@ +diff --git a/api/core/workflow/nodes/end/end_stream_processor.py b/api/core/workflow/nodes/end/end_stream_processor.py +index a6fb2ffc1..fcf4011da 100644 +--- a/api/core/workflow/nodes/end/end_stream_processor.py ++++ b/api/core/workflow/nodes/end/end_stream_processor.py +@@ -42,23 +42,26 @@ class EndStreamProcessor(StreamProcessor): + yield event + continue + +- if event.route_node_state.node_id in self.current_stream_chunk_generating_node_ids: +- stream_out_end_node_ids = self.current_stream_chunk_generating_node_ids[ +- event.route_node_state.node_id +- ] +- else: +- stream_out_end_node_ids = self._get_stream_out_end_node_ids(event) +- self.current_stream_chunk_generating_node_ids[event.route_node_state.node_id] = ( +- stream_out_end_node_ids +- ) +- +- if stream_out_end_node_ids: +- if self.has_output and event.node_id not in self.output_node_ids: +- event.chunk_content = "\n" + event.chunk_content +- +- self.output_node_ids.add(event.node_id) +- self.has_output = True +- yield event ++ # Iterate all variable selectors for all end nodes; stream if matched ++ matched = False # Flag to indicate if a match is found ++ for end_node_id, value_selectors_list in self.end_stream_param.end_stream_variable_selector_mapping.items(): ++ # Loop through all variable selectors configured for the current end node ++ for value_selector in value_selectors_list: ++ # Check if the current event's from_variable_selector matches the configured selector ++ if event.from_variable_selector == value_selector: ++ # If there was previous output and this node hasn't output yet, prepend a newline ++ if self.has_output and event.node_id not in self.output_node_ids: ++ event.chunk_content = "\n" + event.chunk_content ++ # Mark this node as having output to avoid duplicate newlines ++ self.output_node_ids.add(event.node_id) ++ # Set the flag indicating output has occurred ++ self.has_output = True ++ # Stream the current event ++ yield event ++ matched = True # Set matched flag to break out of loops ++ break # Exit inner loop after match ++ if matched: ++ break # Exit outer loop after match + elif isinstance(event, NodeRunSucceededEvent): + yield event + if event.route_node_state.node_id in self.current_stream_chunk_generating_node_ids: From d050e64cde9112a87a83540e7b1478cb3ba39870 Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 15:16:52 +0800 Subject: [PATCH 3/7] feat(docker): add custom Dockerfile for patch support - Add Dockerfile.patch based on official langgenius/dify-api:1.5.1 image - Installs git to enable automatic patch application at container startup - Facilitates robust deployment of custom code changes via patch files --- docker/Dockerfile.patch | 8 ++++++++ docker/docker-compose.yaml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 docker/Dockerfile.patch diff --git a/docker/Dockerfile.patch b/docker/Dockerfile.patch new file mode 100644 index 0000000000..6ee3c35a35 --- /dev/null +++ b/docker/Dockerfile.patch @@ -0,0 +1,8 @@ +FROM langgenius/dify-api:1.5.1 + +USER root +RUN apt-get update && apt-get install -y git \ + && rm -rf /var/lib/apt/lists/* +USER app + +# docker build -f docker/Dockerfile.patch -t land007/dify-api:1.5.1 . \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index ab3bfbf8a7..f8ef10cede 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -517,7 +517,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:1.5.1 + image: land007/dify-api:1.5.1 restart: always environment: # Use the shared environment variables. From 620f2e4a9e4d4f508b37e7159842c3f13832bdd4 Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 15:21:57 +0800 Subject: [PATCH 4/7] fix(docker): remove USER app from custom Dockerfile to avoid startup error - Remove 'USER app' directive since the base image does not include the 'app' user - Ensures container can start successfully after installing git as root --- docker/Dockerfile.patch | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker/Dockerfile.patch b/docker/Dockerfile.patch index 6ee3c35a35..699a738df5 100644 --- a/docker/Dockerfile.patch +++ b/docker/Dockerfile.patch @@ -1,8 +1,6 @@ FROM langgenius/dify-api:1.5.1 -USER root RUN apt-get update && apt-get install -y git \ && rm -rf /var/lib/apt/lists/* -USER app # docker build -f docker/Dockerfile.patch -t land007/dify-api:1.5.1 . \ No newline at end of file From da091d497657bde961d4d52249c09e6a9d9a97b0 Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 15:31:39 +0800 Subject: [PATCH 5/7] feat(docker): apply patch directly in Dockerfile using patch command - Copy my_endnode_streaming.patch into the image and apply it with the patch tool during build - Removes the need for git in the runtime environment - Ensures the custom code changes are always present in the built image --- docker/Dockerfile.patch | 3 +++ docker/docker-compose.yaml | 20 +++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/docker/Dockerfile.patch b/docker/Dockerfile.patch index 699a738df5..54ba26442f 100644 --- a/docker/Dockerfile.patch +++ b/docker/Dockerfile.patch @@ -3,4 +3,7 @@ FROM langgenius/dify-api:1.5.1 RUN apt-get update && apt-get install -y git \ && rm -rf /var/lib/apt/lists/* +COPY my_endnode_streaming.patch /app/my_endnode_streaming.patch +RUN git apply my_endnode_streaming.patch + # docker build -f docker/Dockerfile.patch -t land007/dify-api:1.5.1 . \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index f8ef10cede..7f91fd8796 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -517,7 +517,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: land007/dify-api:1.5.1 + image: langgenius/dify-api:1.5.1 restart: always environment: # Use the shared environment variables. @@ -537,15 +537,8 @@ services: redis: condition: service_started volumes: + # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage - - ../my_endnode_streaming.patch:/app/my_endnode_streaming.patch - command: > - sh -c "if [ -f /app/my_endnode_streaming.patch ]; then \ - cd /app && \ - git apply --check my_endnode_streaming.patch && \ - git apply my_endnode_streaming.patch || echo 'Patch already applied or not applicable'; \ - fi; \ - exec /start-api.sh" networks: - ssrf_proxy_network - default @@ -571,15 +564,8 @@ services: redis: condition: service_started volumes: + # Mount the storage directory to the container, for storing user files. - ./volumes/app/storage:/app/api/storage - - ../my_endnode_streaming.patch:/app/my_endnode_streaming.patch - command: > - sh -c "if [ -f /app/my_endnode_streaming.patch ]; then \ - cd /app && \ - git apply --check my_endnode_streaming.patch && \ - git apply my_endnode_streaming.patch || echo 'Patch already applied or not applicable'; \ - fi; \ - exec /start-worker.sh" networks: - ssrf_proxy_network - default From 5654e38bc92bd9c038c2df96de98171b9a99af90 Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 15:33:15 +0800 Subject: [PATCH 6/7] test --- docker/Dockerfile.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.patch b/docker/Dockerfile.patch index 54ba26442f..b07ebc6d13 100644 --- a/docker/Dockerfile.patch +++ b/docker/Dockerfile.patch @@ -4,6 +4,6 @@ RUN apt-get update && apt-get install -y git \ && rm -rf /var/lib/apt/lists/* COPY my_endnode_streaming.patch /app/my_endnode_streaming.patch -RUN git apply my_endnode_streaming.patch +RUN cd /app && git apply my_endnode_streaming.patch # docker build -f docker/Dockerfile.patch -t land007/dify-api:1.5.1 . \ No newline at end of file From adbf2fa02e6782b54b065a980213c6ab337ef76e Mon Sep 17 00:00:00 2001 From: YiQiu Jia Date: Mon, 7 Jul 2025 15:33:52 +0800 Subject: [PATCH 7/7] test --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 7f91fd8796..07286b9573 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -517,7 +517,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:1.5.1 + image: land007/dify-api:1.5.1 restart: always environment: # Use the shared environment variables.