feat(cache): cache model schemas in the daemon - #781
Closed
GareArc wants to merge 2 commits into
Closed
Conversation
The generator still wrote to internal/core/plugin_daemon, which was renamed to io_tunnel, so `go run cmd/codegen/main.go` failed partway through and left the tree half-regenerated. Regenerating with the corrected path also settles import ordering in the files that were reformatted by hand after the last run.
Dify caches model schemas under a key built from the plugin id with no version, so an enterprise-side upgrade leaves stale schemas behind and Dify has no way to learn about it. Cache them here instead, keyed on the plugin unique identifier, which pins version and checksum. An upgrade lands on a different key, so a stale entry is unreachable and nothing needs to invalidate it. GetAIModelSchema moves out of the generated dispatchers, following the InvokeAgentStrategy precedent, so a cache hit can answer without creating a session or invoking the plugin. Remote and debugging plugins keep one identifier across code changes and are never cached. TTL defaults to 60 minutes via PLUGIN_MODEL_SCHEMA_CACHE_TTL; since the key is content-addressed it only bounds memory.
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Follow-up to #780. Same ownership problem, different cache — not required to fix #3547, so it ships separately.
Dify caches model schemas under a key built from the plugin id with no version:
providerresolves to the plugin id, so an upgrade does not change the key. Nothing invalidates it either — the onlyredis_client.deleteon that path fires when a cached blob fails to parse asAIModelEntity, which catches a shape change but not a value change. An enterprise-side plugin upgrade therefore serves stale schemas for up toPLUGIN_MODEL_SCHEMA_CACHE_TTL(1h by default), and Dify has no way to learn about it.What changed
Cache them here instead, keyed on
PluginUniqueIdentifier, which pins version and checksum. An upgrade lands on a different key, so a stale entry is unreachable and nothing needs to invalidate this cache — the same content-addressed trickCombinedGetPluginDeclarationalready uses. Credentials are folded in as a digest, as Dify does today.GetAIModelSchemamoves out of the generated dispatchers, following theInvokeAgentStrategyprecedent already commented intodefinitions.go. The generated service body callsbaseSSEWithSession, which creates a session before the callback runs; a cache hit that still creates a session and dispatches to the plugin would not be saving anything. The hand-written service answers a hit directly in the same SSE wire format.On a miss the response stream is teed via
Filter+OnClose, storing only if exactly one chunk arrived with a non-nil schema, so an error mid-stream caches nothing.Remote and debugging plugins keep one identifier across code changes and are never cached — otherwise a developer iterating on a plugin would be served their own stale build.
TTL defaults to 60 minutes via
PLUGIN_MODEL_SCHEMA_CACHE_TTL; since the key is content-addressed it only bounds memory.Separate first commit
The generator still wrote to
internal/core/plugin_daemon, which was renamed toio_tunnel, sogo run cmd/codegen/main.gofailed partway and left the tree half-regenerated. Regenerating with the corrected path also settles import ordering in files that had been reformatted by hand after the last run.Type of Change
Essential Checklist
Testing
4 tests in
pkg/utils/cache/helpercover the round trip, keyed-on-version, keyed-on-credentials, and the remote-plugin skip.go build,go vetandgofmtare clean, and/model/schemais verified to register exactly once per router group.Not verified locally: the endpoint end-to-end, which needs a running daemon with an installed plugin. The SSE cache-hit path is the piece most worth exercising against a real request before this leaves draft.
Additional Information
Only useful once
PLUGIN_MODEL_SCHEMA_CACHE_ENABLED(langgenius/dify#39632) is turned off; until then Dify never calls through on a hit of its own cache.Independent of #780 — the two touch the same four config files but no shared logic, so they can merge in either order.