mirror of
https://github.com/langgenius/dify-official-plugins.git
synced 2026-07-21 17:45:23 -04:00
Does Gemini plugin support Veo2 or Veo3 video models? #913
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @WilliamChen-luckbob on GitHub (Jan 5, 2026).
Self Checks
1. Is this request related to a challenge you're experiencing? Tell me about your story.
it seems the official plugin of Gemini not support the video models? that's quite a problem...
2. Additional context or comments
No response
3. Can you help us with this feature?
@dosubot[bot] commented on GitHub (Jan 5, 2026):
The Gemini plugin does not currently support Veo2 or Veo3 video models. While the plugin offers some video capabilities for certain Gemini models (like Gemini 2.5 Flash), there are no references to Veo2 or Veo3 in the code, documentation, or issues. The plugin's multimodal support is limited to the Gemini models exposed by Google's GenAI API, and Veo2/Veo3 integration is not present or planned at this time reference reference. If you want to contribute support for Veo2 or Veo3, you would need to extend the model config files and provider logic to recognize and handle these models.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 5, 2026):
@dosu
I can help integrate support for Veo2 and Veo3, but I need you to provide me with some detailed development guidelines—such as the location of the configuration files for the Gemini model plugin, configuration requirements, which package(s) the code logic should be placed in, what code logic is required, and the requirements for final unit testing, among others. And also how to deploy the latest plugin and inplement it in my project.
@dosubot[bot] commented on GitHub (Jan 5, 2026):
Here’s a step-by-step guide for integrating Veo2 and Veo3 support into the Gemini plugin:
Model Configuration Files:
veo2.yaml,veo3.yaml) inmodels/gemini/models/llm/.gemini-2.5-flash.yamlas templates. Define the model name, label, model_type, features (includevideo), model_properties, parameter_rules, and pricing.models/gemini/models/llm/_position.yamlso they appear in the model list.Provider Logic:
models/gemini/models/llm/llm.py(GoogleLargeLanguageModel)._set_chat_parameters,_set_response_modalities) to support any new parameters or modalities.Provider Configuration:
models/gemini/provider/google.yaml.Manifest and Dependencies:
models/gemini/manifest.yamlto bump the plugin version after your changes.models/gemini/requirements.txtandpyproject.tomlaccordingly.Unit and Integration Testing:
models/gemini/models/tests/.TEST_INSTRUCTIONS.mdfor running and extending tests.Deployment and Usage:
Files URLif usingurlmode forMULTIMODAL_SEND_FORMATin the provider settings.If Veo2/Veo3 have unique API endpoints or parameters not covered by the current Gemini logic, you’ll need to adjust the provider code accordingly. Otherwise, the process above matches how new Gemini models are typically added.
References:
Let me know if you need a sample YAML or have questions about any step!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 5, 2026):
@dosu
Now, I have a question: I need to modify an existing plugin, not create a new plugin project. However, when I refer to the official documentation for creating a new model plugin, I notice that the plugin client doesn't seem to have an operation for this (modifying an existing one or run it). So, how should I conduct actual testing and running? After modifying the code, how do I install this plugin in my Dify environment?
@dosubot[bot] commented on GitHub (Jan 5, 2026):
To test and run a modified existing plugin in Dify, you should first make your code and config changes locally. Run unit and integration tests (for example, with
pytest) in the plugin repo to verify your changes before deployment.To install your modified plugin in your Dify environment, you need to package it (as a
dify_pkgbinary or similar format), then upload it using the plugin management system. Dify uses a dedicated plugin daemon service to manage plugins. After packaging, upload your plugin via the PluginInstaller (Python class) or through the Dify UI/API, and trigger an install or upgrade operation. There is no hot-reload—after each code change, you must repackage and reinstall the plugin for changes to take effect. The plugin daemon handles storage, installation, and versioning, so you don’t need to restart the whole Dify stack—just update the plugin package and reinstall or upgrade it (reference).For best practices: always test in a development or staging environment before deploying to production, and check for missing dependencies or installation errors using the plugin management tools.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 6, 2026):
@dosu
Another question: The API parameters of Veo3 and Veo3.1 differ from those of regular Gemini LLMs. Do I need to create a dedicated LLM node to utilize the features of Veo3.1?
@dosubot[bot] commented on GitHub (Jan 6, 2026):
You don't need to create a dedicated LLM node or class for Veo3.1, even if its API parameters differ from regular Gemini LLMs. The Gemini plugin is designed to handle new models by extending the YAML config and adding conditional logic in the shared provider class (GoogleLargeLanguageModel). Unique parameters (like those for Gemini 3 series) are typically supported by updating the model YAML and enhancing methods such as _set_chat_parameters or _set_response_modalities within the same class. Only if the model's API is fundamentally incompatible with the shared logic would a new node/class be needed, but that's not the current pattern for Gemini plugin development citation.
So, to support Veo3.1, update the YAML config for the model and extend the shared logic as needed for its parameters and features.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 9, 2026):
@dosu
How can I dynamically introduce parameters such as video length and negative prompts into the LLM without modifying the node? For example, if these two pieces of data are stored in the context variables, how should I pass them to the LLM?
@dosubot[bot] commented on GitHub (Jan 9, 2026):
You can dynamically introduce parameters like video length and negative prompts into the LLM without modifying the node by leveraging how Dify workflows and the Gemini plugin handle parameters:
In Dify, context variables (from the VariablePool) can be mapped to model parameters at runtime. The LLM node collects variables referenced in your workflow (via variable selectors, prompt config, or jinja2 variables) and merges them into the model_parameters dictionary before invoking the LLM. This means any value present in the context can be passed as a parameter to the LLM if you reference it in your workflow configuration or prompt template citation.
To support new parameters like video_length or negative_prompt, add them to the parameter_rules section of your Gemini model YAML config. Then, in your workflow, ensure these variables are available in the context and referenced in your node's completion_params or prompt config. The Gemini plugin will pick them up from model_parameters and use them if you handle them in the provider logic citation.
For advanced scenarios, you can register a callback (see core/model_runtime/callbacks/base_callback.py) to intercept and mutate model_parameters before the LLM call. This lets you inject or transform parameters from context variables dynamically, without touching the node or core logic citation.
In summary: expose your desired parameters in the model YAML, ensure your workflow extracts the relevant context variables, and map them to model_parameters (either directly or via a callback). The Gemini plugin will forward these to the LLM as long as they're present in model_parameters and handled in the config setters citation.
If you want a concrete example or YAML snippet for Veo3.x, let me know!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 9, 2026):
@dosu
During the process of setting up remote debugging for my self-hosted Dify instance, executing the application through the main function yields no error logs, and the debugging plugins are not visible in the Dify plugin management dashboard.
I did configure the api secret copied from dify plugin page, and the host and port are correct.
@dosubot[bot] commented on GitHub (Jan 9, 2026):
If your debugging plugin isn't showing up in the Dify dashboard and there are no error logs, here are the most common causes and fixes:
remotein your plugin's environment. If it's set tolocal, the plugin only emits heartbeat events and won't register with Dify or appear in the dashboard reference..envand Docker configs: avoid usinglocalhostor127.0.0.1for service endpoints in Docker. Use your host's internal IP orhost.docker.internalso containers can communicate reference.manifest.yamland provider implementation for required fields and methods (especially_validate_credentials). Formatting errors or missing methods can cause blank pages or missing plugins in the dashboard reference..difypkgfile and uploading it via the "Install via Local File" option in the Dify UI to rule out remote registration issues reference.If none of these steps work, please share your exact
.env, the command sequence you use to start the plugin, and any relevant logs for deeper diagnosis.To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 9, 2026):
@dosu
I start the service via Docker Compose, and the existing plugins in the marketplace can be installed without any issues. I have configured the relevant plugin debugging settings in the .env file of Dify as follows:
PLUGIN_DEBUGGING_HOST=0.0.0.0
PLUGIN_DEBUGGING_PORT=5003
EXPOSE_PLUGIN_DEBUGGING_HOST=0.0.0.0
EXPOSE_PLUGIN_DEBUGGING_PORT=5003
Subsequently, I modified the
docker-compose-template.yamlfile to proxy the 5003 port through Nginx (the 80 port was already set up with a reverse proxy, so I used another port to configure a reverse proxy for 5003).After that, I regenerate the yaml file by
./generate_docker_composeanddocker compose upagain to restart the whole docker.but the problem remains.
How can I verify that my plugin debugging service has started correctly?
@dosubot[bot] commented on GitHub (Jan 9, 2026):
To verify if your plugin debugging service is running correctly in Docker Compose:
If you see heartbeat logs with null session IDs, the service is running in local mode and won’t show up in the dashboard. If it’s in remote mode and still not visible, focus on network, environment variable, and log diagnostics.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 11, 2026):
@dosu
by curl
localhost:5003, there is an errorcurl: (56) Recv failure: Connection reset by peerreturned immediately@dosubot[bot] commented on GitHub (Jan 11, 2026):
The error
curl: (56) Recv failure: Connection reset by peeron localhost:5003 usually means the service isn't listening or is immediately closing the connection. In your setup, the plugin_daemon is exposing port 5003, but by default it's only attached to the default Docker network—not the internal ssrf_proxy_network used by other core services. This can break inter-container communication and cause connection resets. To fix this, add both networks to the plugin_daemon service in your docker-compose.yaml:Then restart your containers. Also, if you're proxying through Nginx, make sure you add a location block for the debugging port—by default, the Dify Nginx config does not proxy port 5003, so requests to that port will fail unless you add a rule similar to how
/e/proxies to port 5002 reference.After these changes, try curling again. If you still get the error, check the plugin_daemon logs for startup errors and verify with
docker compose psthat the service is healthy and the port is exposed reference.To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 11, 2026):
@dosu
I found that the externally exposed port in the plugin-daemon mount configuration of the Docker Compose file was set to 5003:5003 by default. Therefore, I removed the reverse proxy for the relevant port in the Nginx configuration. After regenerating the Docker Compose configuration file and restarting the service, I encountered this issue. I also tried setting the relevant host to 127.0.0.1 or localhost, but both of these resulted in the error:
curl: (56) Recv failure: Connection reset by peer.So I decided to use 0.0.0.0 as host and add this configuration in
plugin-daemonBut got an new error:
here are part of the settings of
.envanddocker-compose-template.yaml:.env
docker-compose-template.yaml
@dosubot[bot] commented on GitHub (Jan 11, 2026):
The error you’re seeing is expected: the plugin-daemon debugging service on port 5003 does not respond to plain HTTP GET requests or raw TCP connections. It expects authenticated HTTP requests to specific API endpoints with an
X-Api-Keyheader and JSON payloads—not a generic curl to/—so you’ll get a handshake error if you try that reference.To verify the service is running and accessible:
EXPOSE_PLUGIN_DEBUGGING_PORTandPLUGIN_DEBUGGING_PORTare set to 5003, and the Docker Compose mapping is5003:5003.defaultandssrf_proxy_network).PLUGIN_REMOTE_INSTALL_HOSTtoplugin_daemon(the Docker Compose service name), notlocalhost, andPLUGIN_REMOTE_INSTALL_PORTto5003reference.DIFY_INNER_API_KEYandINNER_API_KEY_FOR_PLUGINare the same in both containers.To test connectivity, use an HTTP client to call a real API endpoint (like
/plugin/{tenant}/management/list) with the required headers, not justcurl localhost:5003reference. If you need to check if the service is listening, check the plugin-daemon logs for a line likeLaunching gnet with 8 event-loops, listening on: tcp://0.0.0.0:5003reference.If you follow these steps and use the correct API endpoints, your debugging service should be accessible and work as expected.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 11, 2026):
@dosu
Well, I don’t believe Veo can reuse the existing logic in Gemini plugin—aside from initializing the genai function. Video generation relies on entirely different functions, parameters, and configuration classes.
As you can see from below:
normal generation:
video generation:
If I were to implement this logic within the
_generatemethod, it would result in an excessively large if-else block to accommodate both the existing generation logic and video generation.Is there a standardized approach to implementing video generation in the official Gemini plugin? For instance, could we create a dedicated _generate_video method exclusively for video generation?
@dosubot[bot] commented on GitHub (Jan 11, 2026):
There’s no standardized or official approach in the Gemini plugin for handling video generation as a distinct logic path. The current pattern is to use a single _generate method with conditional logic for different modalities, but this can get unwieldy for APIs like Veo’s video generation, which have fundamentally different parameters and workflows reference.
For maintainability, it’s idiomatic to introduce a dedicated _generate_video method within the provider class if you want to keep everything in one plugin. Alternatively, if the video API and workflow are very different, you can follow the Dify ecosystem’s precedent for new modalities (like image generation) and create a separate plugin or tool class for video generation reference. Both approaches are compatible with the plugin daemon and Dify’s management system.
If you want to keep things modular and future-proof, separating concerns—either by method or by plugin/tool—is preferred. You can register the new method or class in the manifest and config files, and test it using the same workflow as other plugins. If you want feedback on your proposed structure, consider opening a discussion or RFC with maintainers.
Let me know if you want a concrete example or guidance on where to place the new method or how to register a separate plugin/tool!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 12, 2026):
@dosu
When I directly drag a tested plugin package to upload a packaged plugin to the Dify page, the following error occurs:
PluginDaemonBadRequestError: plugin verification has been enabled, and the plugin you want to install has a bad signatureHere’s what I’ve done so far:
I tested the plugin via remote debugging, and it worked fine.
I then ran the command dify-plugin-windows-amd64.exe plugin package C:\code\repository-python\dify-official-plugins\tools\gemini_video to package the plugin.
After packaging, I dragged the generated .difypkg file to the Dify upload page, and the above error popped up.
@dosubot[bot] commented on GitHub (Jan 12, 2026):
This error happens because Dify enforces plugin signature verification by default. When you modify or repackage a plugin, the original signature becomes invalid, so the plugin daemon rejects it as having a "bad signature" details.
For development or testing, you can disable signature verification by setting these in your
.envfile (usually in your Dify Docker directory):Then restart your Dify containers. This will let you upload and install modified plugins without signature checks reference.
For production, you should sign your plugin with your own key pair and configure Dify to trust your public key. The steps are:
If you still see signature errors after these steps, check for packaging tool warnings, verify your plugin package structure, and review the plugin daemon logs for more details reference.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 12, 2026):
@dosu
That is exactly what I did. Where can I find the detailed log? I just got an API error.
@dosubot[bot] commented on GitHub (Jan 12, 2026):
You can find detailed logs for plugin installation and signature verification errors in your Docker Compose setup by running
docker compose logs plugin_daemon. These logs will show signature verification failures, Python tracebacks, or JSON decode errors from the plugin daemon during install attempts. You can also check the main Dify app logs withdocker compose logs apior by reading/app/logs/server.loginside theapicontainer for backend errors related to plugin upload and verification. If you want even more detail, setLOG_LEVEL=DEBUGandENABLE_REQUEST_LOGGING=truein your.envfile, then restart your containers. Look for keywords likesignature,verification, orbad signaturein the logs right after reproducing the error to pinpoint the cause.Reference 1
Reference 2
Reference 3
Reference 4
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 12, 2026):
@dosu
Here is all the detailed data I got:
/console/api/workspaces/current/plugin/upload/pkgthis request send the file to server but seems crashed before a signature check, maybe there are some bug in parameter?the file is OK when running
dify-plugin-windows-amd64.exe signature verify gemini_video.signed.difypkg -p plugin_pub.public.pemand the plugin_pub.public.pem is currectly set in specific directory.
@dosubot[bot] commented on GitHub (Jan 12, 2026):
Your plugin package passing local signature verification but failing in Dify usually means the plugin daemon can't find or use your public key for verification, or the environment isn't set up as expected.
Here’s what to check:
Exec into the plugin_daemon container and confirm that your public key (plugin_pub.public.pem) is present at the exact path set in the THIRD_PARTY_SIGNATURE_VERIFICATION_PUBLIC_KEYS environment variable. The default mount is /app/storage, so if you set THIRD_PARTY_SIGNATURE_VERIFICATION_PUBLIC_KEYS=/app/storage/public_keys/plugin_pub.public.pem, the file must exist there inside the container—not just on your host (docker-compose reference).
Inside the plugin_daemon container, run printenv to confirm FORCE_VERIFYING_SIGNATURE, ENFORCE_LANGGENIUS_PLUGIN_SIGNATURES, and THIRD_PARTY_SIGNATURE_VERIFICATION_PUBLIC_KEYS are set as you expect (.env.example reference).
Make sure the plugin package was not modified after signing, and that the public key you used for local verification is the same one referenced by the plugin daemon.
Try uploading the plugin again, then immediately run docker compose logs plugin_daemon to capture any detailed error messages about the signature verification process. Look for lines mentioning “signature”, “verification”, or “bad signature”.
Common issues are: the public key file is missing or in the wrong place inside the container, the environment variable path is wrong, or the plugin was repackaged after signing. If you still get the error, paste the relevant plugin_daemon log output here for further diagnosis.
Let me know what you find!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
Finally, I’m still in testing and found a big debugging issue with the Dify plugin’s structure:
First, I can’t run it in PyCharm debug mode at all — the program just freezes forever with no output, it’s completely stuck. But it works fine when run in pycharm normally, and it is normal connecting to dify.
Second, even without breakpoints, I tried packaging it and running it in Dify: there’s no trace of plugin logs anywhere, and it keeps throwing errors even though unit tests pass.
Here I need some help, how can I find my plugin's logs? I'm now using
docker compose logsbut the plugin logs never occurs, I have set all the logs to WARNING and above, but NOTHING showing in the logs at all.
This is making testing really tough for me.
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
Also, I’m trying to figure out how to use the existing structure to save videos to
/app/storage/tools/xxx. I checked other tools for reference but couldn’t find any code to configure this path setup.@dosubot[bot] commented on GitHub (Jan 13, 2026):
There’s no built-in helper or config for this—plugins/tools just construct the path manually. To save videos to /app/storage/tools/xxx, use standard Python code like:
This directory is writable by default if your Docker volume is mounted correctly. If you want to make the output path configurable, you can use an environment variable or a plugin config parameter, but most plugins just hardcode or build the path inline. Always call os.makedirs with exist_ok=True before writing, to avoid errors if the directory doesn’t exist.
There’s no convention for subdirectory naming—just use a unique name for your tool or plugin. If you need to persist files across restarts, /app/storage/tools/xxx is the right place.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
But when I checked the source code, I couldn’t find any code in the tools that explicitly saves the generated images/videos to /app/storage/tools/xxx. However, in practice, all files end up being saved here, and xxx is a UUID — I don’t know how this ID is generated either.
@dosubot[bot] commented on GitHub (Jan 13, 2026):
The reason you don’t see explicit code in most tools for saving files to
/app/storage/tools/xxxis because Dify handles this automatically via its core file management logic. When a tool or plugin saves a file (like an image or video), it typically calls a helper likeToolFileManager.create_file_by_raworcreate_file_by_url. These methods generate a unique filename usinguuid4().hexand combine it with the tenant ID to build a path liketools/{tenant_id}/{uuid}.{ext}. The file is then saved to/app/storage/tools/{tenant_id}/{uuid}.{ext}in your container see source code.So, the “xxx” UUID you see is actually part of the filename, not a directory. It’s generated automatically for each file to ensure uniqueness. The tenant ID is used as the directory under
/app/storage/tools, organizing files per tenant. This is why all generated files end up in that structure, even if you don’t see manual path handling in the plugin/tool code.To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
well ,can you help me to findout where to find the plugin's real log? when plugin running failes, I got this error:
But it is not actually what I need, I don't know which line is the cause, here is my code:
As you can see, my logging level is info, but nothing printed as expected, just a log with no key cause.
@dosubot[bot] commented on GitHub (Jan 13, 2026):
Your plugin logs should appear in the plugin_daemon logs—use
docker compose logs plugin_daemonor check the./volumes/plugin_daemondirectory on your host if you have it mounted. To make sure your logs are actually emitted, add this at the top of your plugin code (before any logging calls):If you want more detail, set
LOG_LEVEL=DEBUGandENABLE_REQUEST_LOGGING=Truein your.envand restart your containers. You can also setLOG_FILE=/app/logs/server.logto write logs to a file, which you can access withdocker compose logs apior by reading the file inside the container details.Your ValidationError is caused by a subtle Python syntax issue: you have a trailing comma after your yield statements, which makes them yield a tuple instead of a single message object. For example:
should be:
Remove the trailing commas from both yield statements. This will fix the Pydantic error and make debugging much easier reference.
If you still don’t see logs, double-check your logging config and make sure your plugin isn’t swallowing exceptions before logging them. For runtime errors, always check both plugin_daemon and api logs for tracebacks reference.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
Greate! the plugin is running, but plugin logs are still not showing at all.
Also, I have another problem, how to display a video in chat conversation?
As you can see, my testing code is showing below:
this row does not printing anything.
yield self.create_blob_message(blob=video.video_bytes, meta={"mime_type": "video/mp4"})I need the MP4 file showing in chatbox
also, no plugin logs in plugin_daemon container
here is the plugin_daemon log
@dosubot[bot] commented on GitHub (Jan 13, 2026):
For plugin logs: Python logging output from your plugin should appear in the plugin_daemon logs if logging is configured correctly. Make sure you call logging.basicConfig at the very top of your plugin (before any logging calls or imports that might configure logging). If logs still don’t show, try setting LOG_LEVEL=DEBUG and ENABLE_REQUEST_LOGGING=True in your .env, then restart all containers. Also, check if your plugin is running as a subprocess—sometimes logs are only visible in the plugin_daemon container if the plugin isn’t running in a separate process or if stdout isn’t captured.
For video display in chat: Dify’s chat UI does not support inline MP4 playback directly in the chat bubble. Instead, it shows files as downloadable/previewable attachments using the FileList component. To ensure your video appears as an attachment, yield a blob message with both mime_type and filename in the meta, like this:
This will make the video show up as a downloadable file in the chat. If you want users to preview the video, they’ll need to click the file in the FileList; inline playback is not currently supported in the chat bubble itself. If you want to provide a direct download or streaming link, you can also yield a text or link message with the file URL.
Let me know if you still don’t see logs after these changes, or if the video file isn’t appearing as an attachment in chat.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
actually, this code not showing anything. Here is my yield codes after downloading and saving the video from google.
the video is properly saved in
/app/storage/tools/{tenant_id}/{uuid}.{ext}and I checked it by xftp the video content is OK.the
create_text_messagefunction works well but thecreate_blob_messagefunction seems not working.@dosubot[bot] commented on GitHub (Jan 13, 2026):
The reason your video isn't showing as an attachment in chat is that simply yielding create_blob_message with the video bytes and meta is not enough. Dify's backend expects to handle the file registration and linking automatically: when you yield a blob message, the system saves the file, registers it, and then emits a BINARY_LINK message with a URL for the frontend to display as an attachment. However, this only works if the blob is bytes, the meta includes both "mime_type" and "filename", and your Dify version is 1.4.0 or above (which requires the key "filename", not "file_name") ref.
Your code looks correct, but here are a few things to check:
A minimal working example:
If this still doesn't work, check your Dify backend logs for errors related to file registration or message transformation. Also, note that the chat UI will show the video as a downloadable attachment, not as an inline player ref.
Let me know if you still don't see the file in chat after these checks!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 13, 2026):
@dosu
As you can see from the log, the blob message is truncated so dify is not able to show it.
Dify clearly does not support such blob operations directly, as confirmed by your references. They are not using create_blob_message to return a file, they just returned a raw file link with signature which they got from Gemini or somewhere else.
However, videos generated via Gemini cannot be downloaded unless the API key is directly appended to the URL parameters — a workaround that is unfeasible, since exposing my API key to end users poses severe security risks.
To date, Gemini has yet to resolve this issue, which means it is impossible to return either a downloadable link or a byte blob as a response.
Based on my observations, when a plugin runs within the Dify framework, regardless of the filename specified by the plugin during storage, the file will invariably be saved to the path /app/storage/tools/{tenant_id}/{uuid}.{ext} with a new UID. This behavior applies even to files that are identical in both content and original filename.
Moreover, there is no mechanism to map the UID of the saved file to the filename I originally intended. In other words, if I wish to expose file links to users through alternative approaches, I would need to implement a method to locate the corresponding files — a capability that the plugin does not appear to support.
Furthermore, integrating additional storage methods into the plugin would violate the Single Responsibility Principle of software design.
In addition, given the limitations of local disk capacity, uploading files to OSS would be a far more elegant and scalable solution.
So here I need some guide:
/app/storage/tools/xxx?@dosubot[bot] commented on GitHub (Jan 13, 2026):
Here’s how Dify handles plugin file storage and downloadable links:
Encode/decode process: When you yield a
create_blob_message, Dify base64-encodes the bytes for transmission and decodes them on the backend. However, just yielding a blob isn’t enough for chat file attachments—the backend expects to persist the file and register it as a tool file, then emit a message with a signed URL for download or preview. The meta should include both"mime_type"and"filename"(not"file_name"), and the blob must be raw bytes, not a file object or tuple reference.Configuring OSS for file storage: To save plugin-generated files to OSS (like Aliyun OSS, S3, etc.) instead of local disk, set the
STORAGE_TYPEenvironment variable in your.env(or Docker Compose) to the desired backend (e.g.,ALIYUN_OSS,S3, etc.), and provide the required credentials and bucket info (e.g.,ALIYUN_OSS_BUCKET_NAME,ALIYUN_OSS_ACCESS_KEY, etc.). Dify’s storage abstraction will then save all files—including plugin outputs—to OSS transparently reference.Getting the saved file URL: After the plugin yields a blob message, Dify saves the file and generates a signed temporary URL for secure access. This URL is constructed using the configured
FILES_URL(for external access) orINTERNAL_FILES_URL(for internal/plugin access), and includes a signature, timestamp, and nonce for security. The signed URL looks like:You can retrieve this URL from the message files attached to the chat message or by referencing the file in your plugin/tool output reference.
How to show a downloadable file link in chat:
"mime_type": "video/mp4"and"filename": "your_video.mp4"./app/storage/tools/xxx; let Dify handle it.Caveats:
If you want to customize file storage further or need to surface the signed URL directly in your plugin output, you can use ToolFileManager’s
sign_filemethod to generate the URL after saving the file.Let me know if you want a concrete code snippet for your plugin or have questions about OSS config!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 14, 2026):
@dosu
after uploaded an image and try to run video generation, there is an error:
Here is the setting:
Then it is OK to generate the video, but unable to download it from dify
because FILES_URL is not reachable by explorer
I don't like to expose another 5001 port , Dify is running in docker compose, so I think using nginx in the container would be better.
I have checked the nginx volume, it has a config as below:
So I have set
FILES_URL = https://[my_server_ip]/[exposed_port]/filesand trying to run the plugin but there is always a time out error when trying to run plugin,It seems that the FILES_URL is unreachable, something in docker is trying to visit the host server with public url, and get timed out
How to fix this?
@dosubot[bot] commented on GitHub (Jan 14, 2026):
This happens because when you set
FILES_URLto a public/external address (likehttps://[your_server_ip]/files), Docker containers (including your plugin) usually can't reach that address due to Docker's network isolation—so you get timeouts. When you setFILES_URLto an internal Docker address (likehttp://api:5001), it works for plugins but is not reachable from your browser, so users can't download files.The solution is to use both
FILES_URLandINTERNAL_FILES_URL:Set
FILES_URLin your.envto your public-facing URL (the one your browser uses), e.g.:This is what Dify will use for download links shown to users.
Set
INTERNAL_FILES_URLto the Docker network alias for Nginx (or the API service), e.g.:or, if your Nginx is listening on port 80 and is named
nginxin your Docker Compose:This is what Dify will use for plugins and internal services.
Make sure your
nginxservice is on the same Docker network asapiandplugin_daemon, and that your Nginx config proxies/filestohttp://api:5001(which you already have).Set
RESPECT_XFORWARD_HEADERS_ENABLED=truein your.envso Dify generates correct URLs behind the reverse proxy.Restart all containers after changing these settings.
To verify:
docker compose exec api bash), runcurl http://nginx/files/...to check internal access.https://[your-domain-or-ip]/files/...to check external access.This setup lets plugins access files via the internal Docker network (no timeouts), and users download files via the public URL (no need to expose port 5001) (reference, reference, reference).
If
nginxis not resolvable from your containers, you can use the service name defined in your Docker Compose, or tryhttp://host.docker.internalif you're on Docker Desktop.After this, Dify will generate the correct URLs for both plugins and users, and you should not see timeouts or protocol errors.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 14, 2026):
@dosu
Currently, the host machine only exposes port 12345, and this port is whitelisted exclusively for specified IP addresses. The port 80 of Nginx in the Docker environment is mapped to port 12345 of the host machine.
Now, when I try to configure it as you suggested, it simply fails to work and throws a 404 not found error.
The only configuration that enables successful video file saving is setting both attributes to http://api:5001.
@dosubot[bot] commented on GitHub (Jan 14, 2026):
This 404 error happens because, in Docker, containers can’t reach the host’s public IP/port (your Nginx on port 12345) by default. When you set both FILES_URL and INTERNAL_FILES_URL to the public URL (e.g.,
http://your-host:12345/files), plugins inside Docker try to access files via that public address, but Docker’s network isolation blocks this, resulting in 404s or timeouts (source).The only reason setting both to
http://api:5001works is because all traffic stays inside the Docker network, but then files aren’t accessible from outside.To fix this and make both plugin and browser access work through your single exposed port:
FILES_URLto your public-facing URL (e.g.,http://your-host:12345/files) so users get valid download links.INTERNAL_FILES_URLtohttp://nginx/files(or justhttp://nginxif you don’t want the/filespath), so plugins inside Docker access files via the Nginx service on the Docker network.nginxservice is on the same Docker networks asapiandplugin_daemon, and that your Nginx config proxies/filestohttp://api:5001.From inside a container, run
curl http://nginx/files/...(replace with a real file path) to confirm internal access works. From your browser, usehttp://your-host:12345/files/...to confirm external access.If you still get 404s, check the Nginx logs and confirm the
/fileslocation is correctly proxying to the api service. Avoid using localhost or your public IP for INTERNAL_FILES_URL—always use the Docker service name (nginx) for internal access (source).This setup allows plugins and internal services to access files via Docker networking, while users download files through your single exposed port.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 14, 2026):
@dosu
A new error, I have changed nothing but self.runtime.credentials will return a totally wrong api secret which I was never configured.
so the plugin continuing throwing 401 error when querying gemini api. The plugin was OK yesterday, but the secret configured for provider is wrong today.
Here is the code
I have tried times to delete the plugin and install again, remove the plugin node and old configure.
the secret is always wrong when trying to call
_gemini_api_key = self.runtime.credentials["gemini_api_key"].@dosubot[bot] commented on GitHub (Jan 14, 2026):
This is almost always caused by credential caching or a mismatch between the provider config in the UI and what’s actually stored in Dify’s backend. Even if you delete and reinstall the plugin, Dify may keep using a stale API key unless you explicitly update or re-save the Gemini API key in the provider settings UI. Here’s what to do:
If you still get the wrong key after all this, try deleting and recreating the Gemini provider and credential in the UI, then reconfigure the plugin node and retest. This will force Dify to use the new credential and clear any hidden state or cache issues (source).
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 14, 2026):
@dosu
This is actually what I did:
After running docker compose down && docker compose up -d, I reconfigured the credentials. Once the configuration was completed, the plugin page kept prompting that the credential had been removed and was not using the latest version.
I then selected the credential I had just configured, yet the same error still occurred when running the plugin.
Could it be that the provider is passing a PKCS1_OAEP-encrypted credential to the plugin?
For context: My plugin uses local storage, while the API uses Aliyun OSS. I chose this setup because I do not want the uploaded files and generated files to be stored on the local machine—so I configured the API to use Aliyun OSS, but kept the plugins on local storage.
@dosubot[bot] commented on GitHub (Jan 14, 2026):
Dify does not use PKCS1_OAEP or any RSA encryption when passing credentials to plugins—credentials are sent as plaintext JSON from the backend to the plugin daemon, so encryption is not the cause of your issue (source). Mixing Aliyun OSS for API storage and local storage for plugins does not affect how credentials are delivered to plugins; credential passing is independent of storage backend.
The persistent credential mismatch you’re seeing is almost always due to credential caching, a provider_name mismatch, or database desynchronization—especially after recent Dify updates. Dify recently changed the provider_name for Gemini from
langgenius/google/googletolanggenius/gemini/google. If your database still has Gemini credentials with the old provider_name, the backend won’t find or update them, and you’ll get errors or see stale credentials (details and fix).To resolve this:
api/services/plugin/data_migration.py) to update all provider_name fields in your database.This will ensure your plugin receives the correct, updated credential. If the problem persists after these steps, a full environment or database reset may be needed to clear out stale state (migration and troubleshooting reference).
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 15, 2026):
@dosu
after updating dify to 1.11.3, the oss failed to upload the file with error:
I have double-checked the .env file and docker-compose-template.yaml to verify if any modifications have been made. I have also configured my custom settings (such as OSS keys) as required.
Additionally, I noticed that some SSL strategies were changed between versions 1.11.1 and 1.11.3, so I kept the SSL configuration consistent with version 1.11.3—only the service host and ports have been modified.
What could be causing this issue?
@dosubot[bot] commented on GitHub (Jan 15, 2026):
This SSL error is usually caused by a TLS handshake or protocol mismatch between the Dify container (using oss2/requests/urllib3) and your Aliyun OSS endpoint. Upgrading Dify may have updated Python, oss2, or requests, which can introduce stricter SSL/TLS requirements or drop support for older/insecure protocols, making some endpoints incompatible (source).
Here’s how to troubleshoot and resolve it:
Double-check your ALIYUN_OSS_ENDPOINT: It must start with
https://and match your bucket’s region and protocol. If you use an internal endpoint, make sure it supports TLS 1.2+.Test connectivity from inside the API container:
docker compose exec api bashcurl -v https://your-oss-endpointIf your OSS endpoint uses a custom or self-signed certificate, copy the root CA into the container and run
update-ca-certificates, then restart the container (source).As a diagnostic step, set
HTTP_REQUEST_NODE_SSL_VERIFY=Falsein your.envand restart the API container. If uploads work, the issue is with SSL verification (certificate trust or protocol mismatch). Only use this for testing, not production.Check for proxy/firewall issues: Make sure no HTTP_PROXY or HTTPS_PROXY variables are set unless you intend to use a proxy, and that nothing is blocking outbound 443 traffic from the container.
If the above doesn’t help, create a minimal Python script using httpx or oss2 inside the container to test direct uploads to OSS. This can help isolate whether the issue is with Dify, the OSS endpoint, or the underlying libraries (source).
If disabling SSL verification or using a minimal script works, but Dify still fails in production mode, consider downgrading to Dify 1.11.1 to confirm if this is a regression in the new version or its dependencies.
Let me know what you find after these steps, especially if curl or a minimal script also fails inside the container. This will help pinpoint whether it’s a Dify config issue, a dependency regression, or an OSS endpoint compatibility problem.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 15, 2026):
@dosu
hmmm....
It seems a mistake in updating the code... For several restarts, the error not showing again and I didn't change anything either, but another error occurs.
Here is the log:
I have already commented out the cloudbox config, but it seems dify 1.11.3 will force to generate a cloudbox request? Mine is aliyun-oss, not cloudbox.
this is all the config of api, there is no configs in docker-compose-template.yaml I think.
@dosubot[bot] commented on GitHub (Jan 15, 2026):
This error happens because Dify 1.11.3 always passes the ALIYUN_CLOUDBOX_ID environment variable to the OSS client—even if you comment it out in your
.env. If ALIYUN_CLOUDBOX_ID is set (even to a placeholder likeyour-cloudbox-idfrom.env.example), Dify signs requests as a Cloudbox request, which standard Aliyun OSS endpoints reject with “Invalid signing product in Authorization header” see code.To fix this, explicitly set
ALIYUN_CLOUDBOX_ID=(empty value) in your.envfile—do not just comment it out. Then restart all Dify containers to clear the environment. This will prevent Dify from using Cloudbox signing logic and restore standard OSS uploads.If your OSS endpoint does not support v4 signing, try setting
ALIYUN_OSS_AUTH_VERSION=v2as a fallback.After restarting, verify that uploads succeed and the Authorization header no longer references
oss-cloudboxoryour-cloudbox-id.To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 15, 2026):
@dosu
Won't help
Set this config. Then restart all Dify containers to clear the environment.
the same error remains. Is there any caches in dify?
a "your-cloudbox-id" pattern is still in my error log.
@dosubot[bot] commented on GitHub (Jan 15, 2026):
This happens because the Dify Docker stack uses the default value from
.env.example(ALIYUN_CLOUDBOX_ID=your-cloudbox-id) unless you explicitly override it. If you setALIYUN_CLOUDBOX_ID=(empty) only in your.env, but then regeneratedocker-compose.yamlusing thegenerate_docker_composescript, the script still injects the default unless you also clear it in.env.example. The${KEY:-default}syntax treats an empty value as "unset" and falls back to the default from.env.example—so the placeholder persists in your containers even after a restart details.How to fix:
.envand.env.exampleso thatALIYUN_CLOUDBOX_ID=(empty) in both files.docker-compose.yamlusing thegenerate_docker_composescript.docker-compose.yamland confirm thatALIYUN_CLOUDBOX_IDis set to an empty value in the environment section for all relevant services.docker compose downto fully stop and remove all containers, thendocker compose up -dto recreate them.apicontainer and runprintenv | grep ALIYUN_CLOUDBOX_IDto confirm the value is empty at runtime.This will ensure the placeholder is not injected and OSS signing will use the correct logic reference. No additional cache or volume wipe is needed—just make sure the environment is correct before you restart the stack.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 15, 2026):
@dosu
You really misled me a lot T_T
I tried several times, and finally found that commenting out ALIYUN_CLOUDBOX_ID was the right fix.
I didn't realize that the configurations in .env also need to be written into docker-compose.yaml — I just thought Docker would automatically reference the .env file at runtime....
That’s why I always felt like Dify’s configuration items had a caching effect — I didn’t realize that I also need to run generate_docker_compose every time I modify the .env file. Now everything works fine.
@dosubot[bot] commented on GitHub (Jan 15, 2026):
Totally understandable—this trips up a lot of people with Dify’s Docker setup. In this project, Docker Compose doesn’t read .env directly at runtime. Instead, you have to run the generate_docker_compose script after editing .env (or .env.example), which injects your environment variables into docker-compose.yaml. Docker Compose then uses only what’s in docker-compose.yaml when starting containers. If you skip the generation step, your changes won’t take effect, which feels like “caching” or stale config reference reference.
The correct workflow is: edit .env → run generate_docker_compose → restart containers. This ensures your containers get the updated environment. Don’t edit docker-compose.yaml directly, since it gets overwritten by the script.
If you ever change environment variables, just remember to regenerate and restart. Let me know if you want a quick checklist or have any other config questions!
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
@WilliamChen-luckbob commented on GitHub (Jan 29, 2026):
@dosu
My PR has passed the automated review with no further updates. Could you advise if any action is required from my side, or if the plugin should be submitted to dify-plugin rather than dify-official-plugin?
#2469
@dosubot[bot] commented on GitHub (Jan 29, 2026):
If your PR has passed automated review and there are no maintainer comments requesting changes, you don't need to take further action right now—just wait for human review and approval, which can take some time.
For plugin submissions: if your manifest.yaml author is set to "langgenius", your plugin is eligible for dify-official-plugins. If you want the author to be your own name, company, or brand, you should submit to the community plugins repo (dify-plugins) instead. Official plugins require the author to be "langgenius" to be accepted in dify-official-plugins (source).
If your PR meets the author requirement and only contains one plugin, you’re in the right place and just need to wait for maintainer review. If you want your own author name or brand, submit to dify-plugins instead.
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other