App Mode Configuration#
Dify supports multiple app modes, each with a distinct configuration source, dispatch path, and feature validation logic. The AppMode enum defines all recognized modes.
The most important architectural boundary: legacy modes (chat, agent-chat, advanced-chat) read configuration from AppModelConfig or Workflow.features_dict; the newer agent mode reads from an AgentSoulConfig JSON snapshot stored per-version in AgentConfigVersion.config_snapshot.
| Mode | Value | Config Source | Generator |
|---|---|---|---|
CHAT | "chat" | AppModelConfig (DB row) | ChatAppGenerator |
AGENT_CHAT | "agent-chat" | AppModelConfig (DB row) | AgentChatAppGenerator |
ADVANCED_CHAT | "advanced-chat" | Workflow.features_dict | AdvancedChatAppGenerator |
AGENT | "agent" | AgentSoulConfig (snapshot) | AgentAppGenerator |
COMPLETION | "completion" | AppModelConfig | CompletionAppGenerator |
WORKFLOW | "workflow" | Workflow definition | WorkflowAppGenerator |
Dispatch: AppGenerateService#
AppGenerateService._dispatch_generate() is the central router. Before the match/case branch, it computes an effective mode that handles legacy promotion :
effective_mode = (
AppMode.AGENT_CHAT
if app_model.is_agent_with_session(session=session) and app_model.mode != AppMode.AGENT_CHAT
else app_model.mode
)
is_agent_with_session() checks whether app_model_config.agent_mode has function_call or react strategy enabled. If so, it auto-updates the database row to AGENT_CHAT and returns True β transparently migrating legacy chat-with-agent apps without a manual migration .
Legacy EasyUI Modes: AppModelConfig#
For CHAT, AGENT_CHAT, and COMPLETION, configuration comes from the AppModelConfig ORM model β a flat database row storing model selection, system prompt (pre_prompt), agent mode settings (agent_mode), and feature flags. Mode-specific config managers (ChatAppConfigManager, AgentChatAppConfigManager) load this row and convert it into typed config entities.
ADVANCED_CHAT is the exception: it never reads AppModelConfig. Instead, AdvancedChatAppConfigManager reads Workflow.features_dict from a linked workflow and returns a WorkflowUIBasedAppConfig.
Feature availability checks in MessageService reflect this multi-mode split. For suggested questions after answer, get_suggested_questions_after_answer() dispatches based on mode :
ADVANCED_CHAT: reads fromworkflow.features_dictAGENT: resolves viaAgentRuntimeConfigService(introduced in PR #40963 to fix #39681), following a fallback chain:- Debug draft (
AgentConfigDraft) ifinvoke_from == InvokeFrom.DEBUGGER - Conversation's bound snapshot (
AgentConfigSnapshotorAgentConfigDraftviaAgentWorkspaceBinding) - Current published Soul from
AgentRosterService - Legacy
AppModelConfig(merged viamerge_agent_app_features)
- Debug draft (
- Other modes (
CHAT,AGENT_CHAT,COMPLETION): read fromapp_model_config.suggested_questions_after_answer_dict
Agent debug Conversations intentionally persist app_model_config_id = NULL because their configuration comes from the Agent Soul; the resolution service allows them to locate their draft config without a legacy AppModelConfig lookup.
AGENT Mode: AgentSoulConfig#
The AGENT mode (app type introduced alongside Agent V2) diverges entirely from AppModelConfig. Its configuration is a versioned JSON snapshot validated against AgentSoulConfig :
| Top-level field | Type | Purpose |
|---|---|---|
model | AgentSoulModelConfig | None | LLM selection: plugin_id, model_provider, model |
prompt | AgentSoulPromptConfig | System prompt (system_prompt) |
tools | AgentSoulToolsConfig | Dify tools + CLI tools |
knowledge | AgentSoulKnowledgeConfig | Knowledge sets (replaces legacy flat datasets) |
env | AgentSoulEnvConfig | Operator env vars and secret refs |
app_features | AgentSoulAppFeaturesConfig | Feature flags: file upload, TTS, word avoidance, etc. |
app_variables | list[AppVariableConfig] | User-facing input variables shown before conversation |
AgentSoulConfig uses extra="forbid", so any unknown field in the stored JSON causes a hard validation error .
Validation gate: AgentAppGenerator._resolve_agent() calls AgentSoulConfig.model_validate(snapshot.config_snapshot_dict) before allowing generation . For draft/debug runs it validates against the draft snapshot; for published runs it validates the active snapshot. If the agent or snapshot is missing, an AgentAppGeneratorError is raised before any LLM call.
Publish-time guard: AgentComposerService.publish_agent_app_draft() additionally checks agent_soul_has_model() and raises AgentModelNotConfiguredError (HTTP 400, error code agent_model_not_configured) if AgentSoulConfig.model is None .
Synthesizing Soul into the Chat Pipeline#
The AGENT mode reuses the existing chat/SSE pipeline through an adapter layer. AgentAppConfigManager.get_app_config() synthesizes an app_model_config-shaped dict from the Agent Soul:
- Model + prompt always come from the Agent Soul β
AgentSoulConfig.modelmaps to themodelkey;agent_soul.prompt.system_promptpopulatespre_prompt. - Feature flags are merged: if a legacy
AppModelConfigrow exists, its flags are loaded first, thenAgentSoulConfig.app_featuresis applied on top β Soul fields win on conflict . - User input form is built from
app_variablesviaagent_app_variables_to_user_input_form().
The result (AgentAppConfig) is an EasyUIBasedAppConfig subclass that downstream chat pipeline components handle identically to a regular chat app.
Key Source Files#
| File | Role |
|---|---|
api/models/model.py | AppMode enum, App, AppModelConfig ORM models |
api/models/agent_config_entities.py | AgentSoulConfig and all sub-config Pydantic models |
api/services/app_generate_service.py | _dispatch_generate() β mode router with effective-mode adjustment |
api/core/app/apps/agent_app/app_config_manager.py | AgentAppConfigManager β synthesizes Soul into chat-pipeline format |
api/core/app/apps/agent_app/app_feature_projection.py | merge_agent_app_features() β merges legacy flags with Soul feature flags |
api/services/message_service.py | Mode-gated feature checks (e.g., get_suggested_questions_after_answer) |