App Mode Configuration#
Dify routes every app through a mode-specific configuration source and generator. The AppMode enum defines all recognized modes; the central dispatcher is AppGenerateService._dispatch_generate().
Mode β Config Source Map#
| 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 JSON snapshot | AgentAppGenerator |
COMPLETION | "completion" | AppModelConfig DB row | CompletionAppGenerator |
WORKFLOW | "workflow" | Workflow definition | WorkflowAppGenerator |
Legacy EasyUI Modes (CHAT, AGENT_CHAT, COMPLETION)#
Configuration is read from the AppModelConfig ORM row β a flat database record storing model selection, system prompt (pre_prompt), agent strategy (agent_mode), and feature flag JSON columns. Mode-specific config managers (e.g., ChatAppConfigManager, AgentChatAppConfigManager) load this row and convert it into typed config entities .
Legacy promotion: Before dispatching, _dispatch_generate() computes an effective mode. If app_model_config.agent_mode has function_call or react strategy enabled, the app is silently promoted to AGENT_CHAT and the DB row is updated β transparently migrating old chat-with-agent apps without a manual migration .
ADVANCED_CHAT is the exception among legacy modes: it reads Workflow.features_dict through AdvancedChatAppConfigManager, not AppModelConfig .
AGENT Mode (Agent V2)#
AppMode.AGENT diverges entirely from AppModelConfig. Its configuration is a versioned JSON snapshot validated against AgentSoulConfig . Key top-level fields:
| Field | Purpose |
|---|---|
model | LLM selection (plugin_id, model_provider, model) |
prompt | System prompt (system_prompt) |
tools | Dify tools + CLI tools |
knowledge | Knowledge sets |
env | Operator env vars / secret refs |
app_features | Feature flags (file upload, TTS, suggested questions, etc.) |
app_variables | User-facing input variables |
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 any LLM call. For debug runs it validates the draft; for published runs it validates the active snapshot. A missing agent or snapshot raises AgentAppGeneratorError .
Publish-time guard: AgentComposerService.publish_agent_app_draft() 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: AgentAppConfigManager.get_app_config() converts AgentSoulConfig into an AgentAppConfig (an EasyUIBasedAppConfig subclass) so the downstream pipeline handles persistence, feature flags, and prompt config identically to a regular chat app. Feature flags are merged: if a legacy AppModelConfig row exists, its flags are loaded first, then AgentSoulConfig.app_features is applied on top β Soul fields win on conflict, via merge_agent_app_features() .
Feature Availability Checks Per Mode#
Mode-gated feature checks live in MessageService.get_suggested_questions_after_answer(). The branching pattern is the canonical template for how mode-specific config is read at runtime :
ADVANCED_CHATβ readsworkflow.features_dict["suggested_questions_after_answer"]from the draft or published workflow .AGENTβ delegates toMessageService._get_agent_suggested_questions_config(), which callsAgentRuntimeConfigService.resolve_conversation_soul()in this priority order :- Debug draft (
AgentConfigDraft) ifinvoke_from == InvokeFrom.DEBUGGER - Conversation's bound snapshot via
AgentWorkspaceBinding - Current published Soul from
AgentRosterService - Legacy
AppModelConfig(merged viamerge_agent_app_features)
- Debug draft (
CHAT/AGENT_CHAT/COMPLETIONβ readsAppModelConfig.suggested_questions_after_answer_dictdirectly; falls back toConversation.model_configwhenoverride_model_configsis set .
Why the AGENT path is different: Agent debug conversations intentionally store app_model_config_id = NULL β their config comes from the Agent Soul, not an AppModelConfig row. Before PR #40963, the shared legacy lookup raised ValueError("did not find app model config"), producing HTTP 500. The AgentRuntimeConfigService resolution path was introduced to fix this .
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 promotion |
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 | get_suggested_questions_after_answer() β canonical mode-gated feature check |
api/services/agent/runtime_config_service.py | AgentRuntimeConfigService β Soul resolution for AGENT-mode features |