Agent Runtime Layer Provider Registration#
The dify-agent FastAPI server assembles a fixed set of LayerProvider objects at startup via create_default_layer_providers() in compositor_factory.py. These providers are passed to the RunScheduler during the FastAPI lifespan and service all subsequent run requests in-process. The key architectural rule is that DifyRuntimeLayer is conditionally registered β it is only appended when a valid RuntimeBackendProfile is available . If no backend is configured, the provider is simply absent from the set, with no startup error.
Provider Registration Flow#
create_app() wires providers in two steps:
-
ServerSettings.build_runtime_backend_profile()resolvesDIFY_AGENT_RUNTIME_BACKENDand the matching endpoint env var into aRuntimeBackendProfile, or returnsNonewhen thelocalbackend is selected butDIFY_AGENT_LOCAL_SANDBOX_ENDPOINTis not set . -
create_default_layer_providers()receives the profile (orNone) and builds the provider tuple. All providers exceptDifyRuntimeLayerare always registered.DifyRuntimeLayeris conditionally appended:if runtime_backend_profile is not None: providers.extend([LayerProvider.from_factory(layer_type=DifyRuntimeLayer, ...)])
The always-present providers are :
PromptLayer,PydanticAIHistoryLayer,DifyOutputLayer,DifyAskHumanLayer,DifyConfigLayerβ registered withLayerProvider.from_layer_type()DifyExecutionContextLayer,DifyShellLayer,DifyPluginToolsLayer,DifyCoreToolsLayer,DifyKnowledgeBaseLayerβ registered withLayerProvider.from_factory()(server credentials injected via closure)
DifyRuntimeLayer: Blocked Direct Construction#
DifyRuntimeLayer.from_config() raises TypeError at call time:
DifyRuntimeLayer requires a server-injected ExecutionBindingBackend
The only valid construction path is from_config_with_backend(), which accepts an ExecutionBindingBackend from the RuntimeBackendProfile. This backend is never included in per-run HTTP payloads β it is injected exclusively by the provider factory closure .
The RuntimeLease itself is lazy: it is only materialized inside resource_context(), an async context manager that opens and closes the lease around a run. Accessing DifyRuntimeLayer.lease outside this window raises RuntimeError.
DifyShellLayer's Dependency on DifyRuntimeLayer#
DifyShellLayer declares DifyRuntimeLayer as a required typed dependency via DifyShellLayerDeps:
class DifyShellLayerDeps(LayerDeps):
execution_context: PlainLayer[...] | None
runtime: DifyRuntimeLayer # required, not optional
DifyShellLayer never owns its sandbox connection. Every shell tool call resolves the active lease through _require_resource() , which reads self.deps.runtime.lease. The HOME path and workspace cwd are also read from the lease's layout .
Like DifyRuntimeLayer, DifyShellLayer.from_config() raises TypeError . The factory always calls from_config_with_settings(), which injects server-owned settings (shell_redact_patterns, agent_stub_api_base_url, agent_stub_token_factory) that are not present in the per-run config payload .
Runtime Failure Pattern: Missing DifyRuntimeLayer Provider#
Because the DifyRuntimeLayer provider is absent when no backend is configured, any run that includes a dify.runtime or dify.shell layer config will fail at run time, not at startup. The Compositor resolves layer type IDs against the registered provider set when building a run. If no provider matches dify.runtime, the Compositor raises an error during the build phase for that specific run request. Deployments running with runtime_backend=local but without DIFY_AGENT_LOCAL_SANDBOX_ENDPOINT will silently succeed at startup but reject any agent run that requires shell or runtime resources.
The same deferred-failure pattern appears on the upstream API side: if AGENT_BACKEND_BASE_URL is not set, the agent backend client factory raises ValueError: base_url is required when creating a real Agent backend client at the first run request, not at startup .
Key Files#
| File | Purpose |
|---|---|
runtime/compositor_factory.py | create_default_layer_providers() β provider set assembly and conditional DifyRuntimeLayer registration |
layers/runtime/layer.py | DifyRuntimeLayer β lease lifecycle, forced factory construction |
layers/shell/layer.py | DifyShellLayer β DifyRuntimeLayer dependency, _require_resource() |
server/settings.py | ServerSettings.build_runtime_backend_profile() β backend profile construction or None |