社区版Dify实现文生视频 LLM+ComfyUI+混元视频

社区版Dify实现文生视频 LLM+ComfyUI+混元视频

  • 一、 社区版Dify实现私有化混元视频效果
  • 二、为什么社区版Dify可以在对话框实现文生视频?
      • LLM+ComfyUI+混元视频 实现流程图(重点)
      • 1. 文生视频模型支持ComfyUI
      • 2. ComfyUI可以轻松导出API实现封装
      • 3. Dify 中可通过【代码运行】节点实现调用API
      • 4. Dify【直接回复节点】支持Markdown,可是轻易得到视频播放框
  • 三、Flask后端和【Dify 代码执行节点】代码和讲解
    • 1. Flask 后端代码
      • Flask后端的两个功能
    • 2. Dify 代码执行节点 代码
    • 3. Dify LLM节点 如何描述?
  • 四、Dify 安装和专栏的以往文章推荐

一、 社区版Dify实现私有化混元视频效果

在这里插入图片描述
在这里插入图片描述

二、为什么社区版Dify可以在对话框实现文生视频?

LLM+ComfyUI+混元视频 实现流程图(重点)

在这里插入图片描述
这个图就是我的Dify 实现LLM+ComfyUI+混元视频 的整个方案和思路,下面详细说说为什么可以这样做:(这部分可以结合我之前写的文章一起看,我会给出超链接)

1. 文生视频模型支持ComfyUI

其实文生视频的开源模型其实有很多,比如:Sora , Dynamicrafter,VideoCrafter, 混元视频, CogVideo 等等模型。
这些模型大部分都是支持ComfyUI,这里自己去找一找就好。本方法用的是 混元视频的ComfyUI

2. ComfyUI可以轻松导出API实现封装

这部分还不懂的强烈推荐看一下我前面写的 社区版Dify LLM+ComfyUI+代码执行 的方法,里面详细介绍了ComfyUI 的安装,以及调用的最基本的知识。
我这里简单点说就是 工作流其实就是一个JSON,可以通过网络请求实现你想生成的图。ComfyUI 就是一个(工作流)web 后台而已!

3. Dify 中可通过【代码运行】节点实现调用API

Dify 中安装了Sandbox ,支持运行python代码,既然可以跑代码,安装了requests库,那么也就是各种API都可以请求了,这也就是我为什么二次封装的原因,为了简化代码和过程,而不是直接请求ComfyUI。
但是,【代码执行节点】有总时间的约束限制(超时会报错),因为文生视频可能需要跑70~90 秒,但是Sandbox有代码运行时长限制 15S,通常会报timeout 错误!很简单,修改.env 配置文件里面的时间秒数限制即可。为此,可参考我的这一篇博客修改Dify Sandbox的一些配置:社区版Dify sandbox【Python代码执行】Run failed: error: timeout,if the sandbox service

4. Dify【直接回复节点】支持Markdown,可是轻易得到视频播放框

做过LLM开发的人都知道,LLM回复的前端是基于Markdown的,如果在对话框实现视频,安装整个格式输出即可,这就是我的【直接回复节点】的输出:

<video width="320" height="240" autoplay>
  <source src="视频的网络地址" type="video/mp4">
</video>

三、Flask后端和【Dify 代码执行节点】代码和讲解

请先根据 混元视频ComfyUI 安装好模型文件,先保证你的文生视频在ComfyUI 中正常运行。

1. Flask 后端代码

Flask后端的两个功能

第一个:接收 Dify 【代码执行节点】发送来的 文生图Prompt 来修改工作流JSON 文件。
第二个:发送文生图的工作流JSON(给ComfyUI来文生视频),然后等待生成的结果JSON(ComfyUI 告诉你,刚刚的那个请求完成了,生成的文件命名和路径等信息),解析然后得到视频链接(返回给【Dify 代码执行节点】)。

好了:结合我的注释来看Flask代码:

# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify
import websocket
import uuid
import json
import urllib.request
import urllib.parse
import random

import string
import datetime

app = Flask(__name__)

# 设置服务器地址
SERVER_ADDRESS = "你的ComfyUI服务地址:8188"
CLIENT_ID = str(uuid.uuid4())

def queue_prompt(prompt):
    try:
        payload = {"prompt": prompt, "client_id": CLIENT_ID}
        data = json.dumps(payload).encode('utf-8')
        url = f"http://{SERVER_ADDRESS}/prompt"
        req = urllib.request.Request(url, data=data)
        response = urllib.request.urlopen(req)
        return json.loads(response.read())
    except Exception as e:
        print(f"Error in queue_prompt: {e}")
        return None

def get_image(filename, subfolder, folder_type):
    try:
        params = urllib.parse.urlencode({"filename": filename, "subfolder": subfolder, "type": folder_type})
        url = f"http://{SERVER_ADDRESS}/view?{params}"
        return url
    except Exception as e:
        print(f"Error in get_image: {e}")
        return None

def get_history(prompt_id):
    try:
        url = f"http://{SERVER_ADDRESS}/history/{prompt_id}"
        with urllib.request.urlopen(url) as response:
            return json.loads(response.read())
    except Exception as e:
        print(f"Error in get_history: {e}")
        return None

# 等待程序生成,生成后会返回一个JSON ,读取生成的视频地址文件名
def get_images(ws, prompt):
    try:
        prompt_response = queue_prompt(prompt)
        if not prompt_response:
            return None

        prompt_id = prompt_response['prompt_id']

        # 等待生成过程完成
        while True:
            out = ws.recv()
            if isinstance(out, str):
                message = json.loads(out)
                if message.get('type') == 'executing':
                    data = message['data']
                    if data.get('node') is None and data.get('prompt_id') == prompt_id:
                        break

        # 获取生成历史记录
        history = get_history(prompt_id)
        print(history)
        if history and prompt_id in history:
            for node_id, node_output in history[prompt_id]['outputs'].items():
                print(node_id,node_output)
                if 'gifs' in node_output:
                    for image in node_output['gifs']:
                        return get_image(image['filename'], image['subfolder'], image['type'])

    except Exception as e:
        print(f"Error in get_images: {e}")
        return None

# 在API的基础上再次封装修改 的内容,通常是Prompt,可灵活自定义设计
def update_prompt_from_file(filepath, text_prompt, noise_seed):
    """
    从文件加载 JSON 并更新提示信息。

    参数:
        filepath (str): JSON 文件路径。
        text_prompt (str): 新的文本提示。
        noise_seed (int): 随机种子值。

    返回:
        dict: 更新后的 JSON 数据。
    """
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            prompt = json.load(f)

        prompt["25"]["inputs"]["noise_seed"] = noise_seed
        prompt["44"]["inputs"]["text"] = text_prompt
        return prompt
    except Exception as e:
        print(f"Error in update_prompt_from_file: {e}")
        return None

# 生成随机数
def generate_random_15_digit_number():
    return random.randint(10**14, 10**15 - 1)


# Flask 路由
@app.route('/generate_videos', methods=['POST'])
def generate_videos():

    data = request.json
    text_prompt = data.get('text_prompt')
    print("999")
    if not text_prompt:
        return jsonify({"error": "text_prompt is required"}), 400


    noise_seed = generate_random_15_digit_number()

    # 更新提示
    prompt_json = update_prompt_from_file(json_filepath, text_prompt, noise_seed,)
    if not prompt_json:
        return jsonify({"error": "Failed to update prompt"}), 500

    try:
        ws = websocket.WebSocket()
        ws.connect(f"ws://{SERVER_ADDRESS}/ws?clientId={CLIENT_ID}")
        url = get_images(ws, prompt_json)
        print(url)
        if url:
            return jsonify({"image_url": url})
        else:
            return jsonify({"error": "Failed to generate image"}), 500
    except Exception as e:
        print(f"Error in WebSocket connection: {e}")
        return jsonify({"error": "WebSocket connection failed"}), 500
    finally:
        ws.close()

if __name__ == '__main__':
    json_filepath = "hunyuan_00012.json" # 你的混元视频API
    app.run(host='0.0.0.0', port=3083)


发送的JSON 就是工作流,返回的呢?如果好奇可以看:

{
  "8efd022e-fa4c-454d-b885-9aed9e3435a6": {
    "prompt": [
      40,
      "8efd022e-fa4c-454d-b885-9aed9e3435a6",
      {
        "10": {
          "inputs": {
            "vae_name": "hunyuan_video_vae_bf16.safetensors"
          },
          "class_type": "VAELoader",
          "_meta": {
            "title": "Load VAE"
          }
        },
        "11": {
          "inputs": {
            "clip_name1": "clip_l.safetensors",
            "clip_name2": "llava_llama3_fp8_scaled.safetensors",
            "type": "hunyuan_video"
          },
          "class_type": "DualCLIPLoader",
          "_meta": {
            "title": "DualCLIPLoader"
          }
        },
        // ... 其他节点配置
        "78": {
          "inputs": {
            "frame_rate": 35.0,
            "loop_count": 0,
            "filename_prefix": "hunyuan",
            "format": "video/h265-mp4",
            "pix_fmt": "yuv420p10le",
            "crf": 22,
            "save_metadata": true,
            "pingpong": false,
            "save_output": true,
            "images": ["73", 0]
          },
          "class_type": "VHS_VideoCombine",
          "_meta": {
            "title": "Video Combine 🎥🅥🅗🅢"
          }
        }
      },
      {
        "client_id": "7e3ec27b-c922-442b-96e6-d8afa853bd70"
      },
      ["78"]
    ],
    "outputs": {
      "78": {
        "gifs": [
          {
            "filename": "hunyuan_00032.mp4",
            "subfolder": "",
            "type": "output",
            "format": "video/h265-mp4",
            "frame_rate": 35.0,
            "workflow": "hunyuan_00032.png",
            "fullpath": "/**********/ComfyUI/ComfyUI-master-main/output/hunyuan_00032.mp4"
          }
        ]
      }
    },
    "status": {
      "status_str": "success",
      "completed": true,
      "messages": [
        [
          "execution_start",
          {
            "prompt_id": "8efd022e-fa4c-454d-b885-9aed9e3435a6",
            "timestamp": 1737096231965
          }
        ],
        [
          "execution_cached",
          {
            "nodes": ["10", "11", "12", "16", "17", "45", "67"],
            "prompt_id": "8efd022e-fa4c-454d-b885-9aed9e3435a6",
            "timestamp": 1737096231995
          }
        ],
        [
          "execution_success",
          {
            "prompt_id": "8efd022e-fa4c-454d-b885-9aed9e3435a6",
            "timestamp": 1737096309647
          }
        ]
      ]
    },
    "meta": {
      "78": {
        "node_id": "78",
        "display_node": "78",
        "parent_node": null,
        "real_node_id": "78"
      }
    }
  }
}

通过这个返回的JSON地址可以得到一个返回的视频链接:

http://你的ComfyUI地址:8188/view?filename=hunyuan_00061.mp4&subfolder=&type=output

这个地址是Flask 后台返回给 【Dify 代码执行节点】,随后这个【Dify 直接回复节点】按照这样:

<video width="320" height="240" autoplay>
  <source src="http://你的ComfyUI地址:8188/view?filename=hunyuan_00061.mp4&subfolder=&type=output" type="video/mp4">
</video>

即可显示视频了
在这里插入图片描述

2. Dify 代码执行节点 代码

在这里插入图片描述

代码很简单:

import requests
import json
from typing import Dict

def main(prompt) -> Dict[str, str]:
    # 服务器地址
    url = "http://你的Flask后端地址:3083/generate_videos"

    # 请求数据
    data = {
        "text_prompt": prompt,
    }

    # 发送 POST 请求并传递 JSON 数据
    response = requests.post(url, json=data)
    
    if response.status_code == 200:
        result = eval(response.text)["image_url"]
        return {'result': result}
    else:
        return {'error': 'Request failed with status code {}'.format(response.status_code)}

3. Dify LLM节点 如何描述?

这部分其实很灵活,你可以用很多种大模型,我是用deepseek,当然也可以用Ollama本地,等等。我在之前的文章也有写过。
这里就是转换一下英文的文生图Prompt即可:
在这里插入图片描述

四、Dify 安装和专栏的以往文章推荐

  1. Dify安装时会遇到的网络问题,已成功安装Dify教程
  2. Dify 部署LLM 可以参考这里,Dify实现Ollama3.2-vision多模态聊天
  3. 社区版Dify +ComfyUI 实现 Flux 文生图
  4. 并且欢迎关注我的 社区版 Dify 开发专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/955942.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数智化转型 | 星环科技Defensor 助力某银行数据分类分级

在数据驱动的金融时代&#xff0c;数据安全和隐私保护的重要性日益凸显。某银行作为数字化转型的先行者&#xff0c;面临着一项艰巨的任务&#xff1a;如何高效、准确地对分布在多个业务系统、业务库与数仓数湖中的约80万个字段进行数据分类和分级。该银行借助星环科技数据安全…

Spring boot启动原理及相关组件

优质博文&#xff1a;IT-BLOG-CN 一、Spring Boot应用启动 一个Spring Boot应用的启动通常如下&#xff1a; SpringBootApplication Slf4j public class ApplicationMain {public static void main(String[] args) {ConfigurableApplicationContext ctx SpringApplication.…

中国石油大学(华东)自动评教工具(涵盖爬虫的基础知识,适合练手)

我开发了一个用于自动评教的工具&#xff0c;大家可以试着用用&#xff0c;下面是链接。 https://github.com/restrain11/auto_teachingEvaluate 可以点个星吗&#xff0c;感谢&#xff01;&#x1fae1; 以下是我在开发过程中学到的知识 以及 碰到的部分问题 目录 动态爬虫和静…

PyTorch使用教程(2)-torch包

1、简介 torch包是PyTorch框架最外层的包&#xff0c;主要是包含了张量的创建和基本操作、随机数生成器、序列化、局部梯度操作的上下文管理器等等&#xff0c;内容很多。我们基础学习的时候&#xff0c;只有关注张量的创建、序列化&#xff0c;随机数、张量的数学数学计算等常…

机器学习-距离的度量方法

文章目录 一. 欧式距离二. 曼哈顿距离三. 切比雪夫距离四. 闵式距离1. p不同取值,表示不同距离2. 当 ( p → ∞ ) ( p \to \infty ) (p→∞) 时&#xff0c;为什么闵式距离变为切比雪夫距离 五. 总结 一. 欧式距离 欧式距离&#xff08;Euclidean distance&#xff09;:多维空…

ComfyUI 矩阵测试指南:用三种方法,速优项目效果

在ComfyUI中&#xff0c;矩阵测试也叫xyz图表测试&#xff0c;作用是通过控制变量的方式来对Lora模型以及各种参数开展测试&#xff0c;并进行有效区分。其中测试方法有很多种&#xff0c;可以通过借助插件也可以自行搭建工作流实现&#xff0c;下面介绍3种方式&#xff1a; 1…

内存与缓存:保姆级图文详解

文章目录 前言1、计算机存储设备1.1、硬盘、内存、缓存1.2、金字塔结构1.3、数据流通过程 2、数据结构内存效率3、数据结构缓存效率 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我持续创作的…

UllnnovationHub,一个开源的WPF控件库

目录 UllnnovationHub1.项目概述2.开发环境3.使用方法4.项目简介1.WPF原生控件1.Button2.GroupBox3.TabControl4.RadioButton5.SwitchButton6.TextBox7.PasswordBox8.CheckBox9.DateTimePicker10.Expander11.Card12.ListBox13.Treeview14.Combox15.Separator16.ListView17.Data…

【STM32-学习笔记-10-】BKP备份寄存器+时间戳

文章目录 BKP备份寄存器Ⅰ、BKP简介1. BKP的基本功能2. BKP的存储容量3. BKP的访问和操作4. BKP的应用场景5. BKP的控制寄存器 Ⅱ、BKP基本结构Ⅲ、BKP函数Ⅳ、BKP使用示例 时间戳一、Unix时间戳二、时间戳的转换&#xff08;time.h函数介绍&#xff09;Ⅰ、time()Ⅱ、mktime()…

Flowable 管理各业务流程:流程设计器 (获取流程模型 XML)、流程部署、启动流程、流程审批

文章目录 引言I 表结构主要表前缀及其用途核心表II 流程设计器(Flowable BPMN模型编辑器插件)Flowable-UIvue插件III 流程部署部署步骤例子:根据流程模型ID部署IV 启动流程启动步骤ACT_RE_PROCDEF:流程定义相关信息例子:根据流程 ID 启动流程V 流程审批审批步骤Flowable 审…

java根据模板导出word,并在word中插入echarts相关统计图片以及表格

引入依赖创建word模板创建ftl模板文件保存的ftl可能会出现占位符分割的问题&#xff0c;需要处理将ftl文件中的图片的Base64删除&#xff0c;并使用占位符代替插入表格&#xff0c;并指定表格的位置在图片下方 Echarts转图片根据模板生成word文档DocUtil导出word文档 生成的wor…

晨辉面试抽签和评分管理系统之十:如何搭建自己的数据库服务器,使用本软件的网络版

晨辉面试抽签和评分管理系统&#xff08;下载地址:www.chenhuisoft.cn&#xff09;是公务员招录面试、教师资格考试面试、企业招录面试等各类面试通用的考生编排、考生入场抽签、候考室倒计时管理、面试考官抽签、面试评分记录和成绩核算的面试全流程信息化管理软件。提供了考生…

Asp .Net Core 实现微服务:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现

什么是 Ocelot ? Ocelot是一个开源的ASP.NET Core微服务网关&#xff0c;它提供了API网关所需的所有功能&#xff0c;如路由、认证、限流、监控等。 Ocelot是一个简单、灵活且功能强大的API网关&#xff0c;它可以与现有的服务集成&#xff0c;并帮助您保护、监控和扩展您的…

mongoose 支持https踩坑纪实

简述 mongoose是C编写的嵌入式web服务&#xff0c;它能够支持https协议&#xff0c;可以简单的部署&#xff0c;但要做到完美部署&#xff0c;不是那么容易。 部署方法 本人使用的是最新的7.16版&#xff0c;以前版本似乎是要通过修改 头文件中的 MG_ENABLE_SSL 宏定义&…

深入内核讲明白Android Binder【二】

深入内核讲明白Android Binder【二】 前言一、Binder通信内核源码整体思路概述1. 客户端向服务端发送数据流程概述1.1 binder_ref1.2 binder_node1.3 binder_proc1.4 binder_thread 2. 服务端的binder_node是什么时候被创建的呢&#xff1f;2.1 Binder驱动程序为服务创建binder…

玩转大语言模型——使用graphRAG+Ollama构建知识图谱

系列文章目录 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用graphRAGOllama构建知识图谱 文章目录 系列文章目录前言下载和安装用下载项目的方式下载并安装用pip方式下载并安装 生成知…

微透镜阵列精准全检,白光干涉3D自动量测方案提效70%

广泛应用的微透镜阵列 微透镜是一种常见的微光学元件&#xff0c;通过设计微透镜&#xff0c;可对入射光进行扩散、光束整形、光线均分、光学聚焦、集成成像等调制&#xff0c;进而实现许多传统光学元器件难以实现的特殊功能。 微透镜阵列&#xff08;Microlens Array&#x…

新星杯-ESP32智能硬件开发--ESP32系统

本博文内容导读&#x1f4d5;&#x1f389;&#x1f525; 1、ESP32芯片和系统架构进行描述&#xff0c;给出ESP32系统的地址映射规则。 2、介绍ESP32复位及时钟定时具体功能&#xff0c;方便后续开发。 3、介绍基于ESP32开发板使用的底层操作系统&#xff0c;对ESP32应用程序开…

python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)图像变换

形态变换 图像处理中的形态学操作是处理图像结构的有效方法。以下是一些常见的形态学操作的介绍及其在 OpenCV 中的实现示例。 1. 腐蚀&#xff08;Erosion&#xff09; 腐蚀操作通过消除图像边界来减少图像中的白色区域&#xff08;前景&#xff09;&#xff0c;使物体的边…

Linux 音视频入门到实战专栏(视频篇)视频编解码 MPP

文章目录 一、MPP 介绍二、获取和编译RKMPP库三、视频解码四、视频编码 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; &#x1f4e2;本篇将介绍如何调用alsa api来进行音频数据的播放和录制。 一、MPP 介绍 瑞芯微提供的媒体处理软件平台…