品牌电商网站,河北网站建设方案详细,西宁思帽网站建设,一个主机 多个网站社区版Dify实现文生视频 LLMComfyUI混元视频 一、 社区版Dify实现私有化混元视频效果二、为什么社区版Dify可以在对话框实现文生视频#xff1f;LLMComfyUI混元视频 实现流程图#xff08;重点#xff09;1. 文生视频模型支持ComfyUI2. ComfyUI可以轻松导出API实现封装3. Di… 社区版Dify实现文生视频 LLMComfyUI混元视频 一、 社区版Dify实现私有化混元视频效果二、为什么社区版Dify可以在对话框实现文生视频LLMComfyUI混元视频 实现流程图重点1. 文生视频模型支持ComfyUI2. ComfyUI可以轻松导出API实现封装3. Dify 中可通过【代码运行】节点实现调用API4. Dify【直接回复节点】支持Markdown可是轻易得到视频播放框 三、Flask后端和【Dify 代码执行节点】代码和讲解1. Flask 后端代码Flask后端的两个功能 2. Dify 代码执行节点 代码3. Dify LLM节点 如何描述 四、Dify 安装和专栏的以往文章推荐 一、 社区版Dify实现私有化混元视频效果 二、为什么社区版Dify可以在对话框实现文生视频
LLMComfyUI混元视频 实现流程图重点 这个图就是我的Dify 实现LLMComfyUI混元视频 的整个方案和思路下面详细说说为什么可以这样做这部分可以结合我之前写的文章一起看我会给出超链接
1. 文生视频模型支持ComfyUI
其实文生视频的开源模型其实有很多比如Sora DynamicrafterVideoCrafter, 混元视频 CogVideo 等等模型。 这些模型大部分都是支持ComfyUI这里自己去找一找就好。本方法用的是 混元视频的ComfyUI
2. ComfyUI可以轻松导出API实现封装
这部分还不懂的强烈推荐看一下我前面写的 社区版Dify LLMComfyUI代码执行 的方法里面详细介绍了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: timeoutif the sandbox service
4. Dify【直接回复节点】支持Markdown可是轻易得到视频播放框
做过LLM开发的人都知道LLM回复的前端是基于Markdown的如果在对话框实现视频安装整个格式输出即可这就是我的【直接回复节点】的输出
video width320 height240 autoplaysource src视频的网络地址 typevideo/mp4
/video三、Flask后端和【Dify 代码执行节点】代码和讲解
请先根据 混元视频ComfyUI 安装好模型文件先保证你的文生视频在ComfyUI 中正常运行。
1. Flask 后端代码
Flask后端的两个功能
第一个接收 Dify 【代码执行节点】发送来的 文生图Prompt 来修改工作流JSON 文件。 第二个发送文生图的工作流JSON给ComfyUI来文生视频然后等待生成的结果JSONComfyUI 告诉你刚刚的那个请求完成了生成的文件命名和路径等信息解析然后得到视频链接返回给【Dify 代码执行节点】。
好了结合我的注释来看Flask代码
# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify
import websocket
import uuid
import json
import urllib.request
import urllib.parse
import randomimport string
import datetimeapp 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 fhttp://{SERVER_ADDRESS}/promptreq urllib.request.Request(url, datadata)response urllib.request.urlopen(req)return json.loads(response.read())except Exception as e:print(fError in queue_prompt: {e})return Nonedef get_image(filename, subfolder, folder_type):try:params urllib.parse.urlencode({filename: filename, subfolder: subfolder, type: folder_type})url fhttp://{SERVER_ADDRESS}/view?{params}return urlexcept Exception as e:print(fError in get_image: {e})return Nonedef get_history(prompt_id):try:url fhttp://{SERVER_ADDRESS}/history/{prompt_id}with urllib.request.urlopen(url) as response:return json.loads(response.read())except Exception as e:print(fError in get_history: {e})return None# 等待程序生成生成后会返回一个JSON 读取生成的视频地址文件名
def get_images(ws, prompt):try:prompt_response queue_prompt(prompt)if not prompt_response:return Noneprompt_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(fError 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, encodingutf-8) as f:prompt json.load(f)prompt[25][inputs][noise_seed] noise_seedprompt[44][inputs][text] text_promptreturn promptexcept Exception as e:print(fError 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.jsontext_prompt data.get(text_prompt)print(999)if not text_prompt:return jsonify({error: text_prompt is required}), 400noise_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}), 500try:ws websocket.WebSocket()ws.connect(fws://{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}), 500except Exception as e:print(fError in WebSocket connection: {e})return jsonify({error: WebSocket connection failed}), 500finally:ws.close()if __name__ __main__:json_filepath hunyuan_00012.json # 你的混元视频APIapp.run(host0.0.0.0, port3083)
发送的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?filenamehunyuan_00061.mp4subfoldertypeoutput这个地址是Flask 后台返回给 【Dify 代码执行节点】随后这个【Dify 直接回复节点】按照这样
video width320 height240 autoplaysource srchttp://你的ComfyUI地址:8188/view?filenamehunyuan_00061.mp4subfoldertypeoutput typevideo/mp4
/video即可显示视频了
2. Dify 代码执行节点 代码 代码很简单
import requests
import json
from typing import Dictdef main(prompt) - Dict[str, str]:# 服务器地址url http://你的Flask后端地址:3083/generate_videos# 请求数据data {text_prompt: prompt,}# 发送 POST 请求并传递 JSON 数据response requests.post(url, jsondata)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 安装和专栏的以往文章推荐
Dify安装时会遇到的网络问题已成功安装Dify教程Dify 部署LLM 可以参考这里Dify实现Ollama3.2-vision多模态聊天社区版Dify ComfyUI 实现 Flux 文生图并且欢迎关注我的 社区版 Dify 开发专栏