feat(web): add first-token timeout to the workflow model parameter panel

Append a synthetic FIRST_TOKEN_TIMEOUT_PARAMETER_RULE (int, seconds, opt-in)
to the model parameter panel, mirroring how the stop rule is injected. The
rule renders only in workflow advanced mode (isInWorkflow), covering the LLM,
question classifier and parameter extractor panels; easy-UI app orchestration
pages never show it. The value is stored in completion_params and consumed by
the Dify backend before the request reaches the provider.
This commit is contained in:
L1nSn0w
2026-07-14 16:57:21 +08:00
parent de11f571b8
commit 989c42153c
3 changed files with 61 additions and 17 deletions
@@ -388,6 +388,23 @@ describe('ModelParameterModal', () => {
expect(screen.getByText(/debugAsSingleModel/i)).toBeInTheDocument()
})
it('should append the first token timeout parameter only in workflow advanced mode', () => {
render(<ModelParameterModal {...defaultProps} isAdvancedMode isInWorkflow />)
openSettings()
expect(screen.getByTestId('param-first_token_timeout')).toBeInTheDocument()
})
it('should not append the first token timeout parameter outside workflow', () => {
render(<ModelParameterModal {...defaultProps} isAdvancedMode />)
openSettings()
expect(screen.getByTestId('param-stop')).toBeInTheDocument()
expect(screen.queryByTestId('param-first_token_timeout')).not.toBeInTheDocument()
})
it('should render the empty loading fallback when rules resolve to an empty list', () => {
parameterRules = []
isRulesLoading = true
@@ -9,7 +9,11 @@ import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
import Loading from '@/app/components/base/loading'
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE } from '@/config'
import {
FIRST_TOKEN_TIMEOUT_PARAMETER_RULE,
PROVIDER_WITH_PRESET_TONE,
STOP_PARAMETER_RULE,
} from '@/config'
import { useModelParameterRules } from '@/service/use-common'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '../hooks'
import ModelSelector from '../model-selector'
@@ -216,22 +220,24 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
<Loading />
</div>
) : (
[...parameterRules, ...(isAdvancedMode ? [STOP_PARAMETER_RULE] : [])].map(
(parameter) => (
<ParameterItem
key={`${modelId}-${parameter.name}`}
parameterRule={parameter}
value={completionParams?.[parameter.name]}
onChange={(v) => handleParamChange(parameter.name, v)}
onSwitch={(checked, assignValue) =>
handleSwitch(parameter.name, checked, assignValue)
}
isInWorkflow={isInWorkflow}
nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes}
/>
),
)
[
...parameterRules,
...(isAdvancedMode ? [STOP_PARAMETER_RULE] : []),
...(isAdvancedMode && isInWorkflow ? [FIRST_TOKEN_TIMEOUT_PARAMETER_RULE] : []),
].map((parameter) => (
<ParameterItem
key={`${modelId}-${parameter.name}`}
parameterRule={parameter}
value={completionParams?.[parameter.name]}
onChange={(v) => handleParamChange(parameter.name, v)}
onSwitch={(checked, assignValue) =>
handleSwitch(parameter.name, checked, assignValue)
}
isInWorkflow={isInWorkflow}
nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes}
/>
))
)}
</div>
)}
+21
View File
@@ -328,6 +328,27 @@ export const STOP_PARAMETER_RULE: ModelParameterRule = {
},
}
// Synthetic rule consumed by the Dify backend before the request reaches the
// model provider; only enforced for workflow LLM-compatible nodes.
export const FIRST_TOKEN_TIMEOUT_PARAMETER_RULE: ModelParameterRule = {
default: 60,
min: 1,
max: 1800,
precision: 0,
help: {
en_US:
'Max seconds to wait for the first streamed token. On timeout the node fails; combine with node retry to re-run it automatically.',
zh_Hans: '等待首个流式 Token 的最长秒数。超时后节点失败,可配合节点重试自动重跑。',
},
label: {
en_US: 'First token timeout',
zh_Hans: '首个 Token 超时',
},
name: 'first_token_timeout',
required: false,
type: 'int',
}
export const PARTNER_STACK_CONFIG = {
cookieName: 'partner_stack_info',
saveCookieDays: 90,