diff --git a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx index 6b1983f5e2..aa911d45e8 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx @@ -25,7 +25,7 @@ import { useSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowled import Switch from '@/app/components/base/switch' import Toast from '@/app/components/base/toast' -interface Props { +type Props = { datasetConfigs: DatasetConfigs onChange: (configs: DatasetConfigs, isRetrievalModeChange?: boolean) => void isInWorkflow?: boolean @@ -71,6 +71,7 @@ const ConfigContent: FC = ({ ? { ...rerankDefaultModel, provider: rerankDefaultModel.provider.provider, + plugin_id: rerankDefaultModel.provider.plugin_id, } : undefined, ) @@ -80,12 +81,14 @@ const ConfigContent: FC = ({ return { provider_name: datasetConfigs.reranking_model.reranking_provider_name, model_name: datasetConfigs.reranking_model.reranking_model_name, + plugin_id: datasetConfigs.reranking_model.reranking_plugin_id, } } else if (rerankDefaultModel) { return { provider_name: rerankDefaultModel.provider.provider, model_name: rerankDefaultModel.model, + plugin_id: rerankDefaultModel.provider.plugin_id, } } })() @@ -172,7 +175,7 @@ const ConfigContent: FC = ({ return false return datasetConfigs.reranking_enable - }, [canManuallyToggleRerank, datasetConfigs.reranking_enable]) + }, [canManuallyToggleRerank, datasetConfigs.reranking_enable, isRerankDefaultModelValid]) const handleDisabledSwitchClick = useCallback(() => { if (!currentRerankModel && !showRerankModel) @@ -300,13 +303,14 @@ const ConfigContent: FC = ({
{ onChange({ ...datasetConfigs, reranking_model: { reranking_provider_name: v.provider, reranking_model_name: v.model, + reranking_plugin_id: v.plugin_id, }, }) }} @@ -384,6 +388,7 @@ const ConfigContent: FC = ({ portalToFollowElemContentClassName='!z-[1002]' isAdvancedMode={true} mode={model?.mode} + pluginId={model?.plugin_id} provider={model?.provider} completionParams={model?.completion_params} modelId={model?.name} diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/model-parameter-trigger.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/model-parameter-trigger.tsx index 155ebe21ca..380d7363f6 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/model-parameter-trigger.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/model-parameter-trigger.tsx @@ -36,12 +36,13 @@ const ModelParameterTrigger: FC = ({ const language = useLanguage() const index = multipleModelConfigs.findIndex(v => v.id === modelAndParameter.id) - const handleSelectModel = ({ modelId, provider }: { modelId: string; provider: string }) => { + const handleSelectModel = ({ modelId, provider, pluginId }: { modelId: string; provider: string; pluginId: string }) => { const newModelConfigs = [...multipleModelConfigs] newModelConfigs[index] = { ...newModelConfigs[index], model: modelId, provider, + plugin_id: pluginId, } onMultipleModelConfigsChange(true, newModelConfigs) } @@ -58,6 +59,7 @@ const ModelParameterTrigger: FC = ({ } diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index af50fc65c3..c74881d7f8 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -72,7 +72,7 @@ import { SupportUploadFileTypes } from '@/app/components/workflow/types' import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' import { fetchFileUploadConfig } from '@/service/common' -interface PublishConfig { +type PublishConfig = { modelConfig: ModelConfig completionParams: FormValue } @@ -156,6 +156,7 @@ const Configuration: FC = () => { const setCompletionParams = (value: FormValue) => { const params = { ...value } + // eslint-disable-next-line ts/no-use-before-define if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) { params.stop = getTempStop() setTempStop([]) @@ -164,6 +165,7 @@ const Configuration: FC = () => { } const [modelConfig, doSetModelConfig] = useState({ + plugin_id: 'langgenius', provider: 'openai', model_id: 'gpt-3.5-turbo', mode: ModelModeType.unset, @@ -198,6 +200,7 @@ const Configuration: FC = () => { reranking_model: { reranking_provider_name: '', reranking_model_name: '', + reranking_plugin_id: '', }, top_k: DATASET_DEFAULT.top_k, score_threshold_enabled: false, @@ -279,6 +282,7 @@ const Configuration: FC = () => { reranking_model: restConfigs.reranking_model && { reranking_provider_name: restConfigs.reranking_model.reranking_provider_name, reranking_model_name: restConfigs.reranking_model.reranking_model_name, + reranking_plugin_id: restConfigs.reranking_model.reranking_plugin_id, }, retrieval_model, score_threshold_enabled, @@ -320,6 +324,7 @@ const Configuration: FC = () => { textGenerationModelList, } = useTextGenerationCurrentProviderAndModelAndModelList( { + plugin_id: modelConfig.plugin_id, provider: modelConfig.provider, model: modelConfig.model_id, }, @@ -350,6 +355,7 @@ const Configuration: FC = () => { const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true) const setPromptMode = async (mode: PromptMode) => { if (mode === PromptMode.advanced) { + // eslint-disable-next-line ts/no-use-before-define await migrateToDefaultPrompt() setCanReturnToSimpleMode(true) } @@ -545,6 +551,7 @@ const Configuration: FC = () => { const config = { modelConfig: { + plugin_id: model.plugin_id, provider: model.provider, model_id: model.name, mode: model.mode, @@ -763,8 +770,8 @@ const Configuration: FC = () => { handleMultipleModelConfigsChange( true, [ - { id: `${Date.now()}`, model: modelConfig.model_id, provider: modelConfig.provider, parameters: completionParams }, - { id: `${Date.now()}-no-repeat`, model: '', provider: '', parameters: {} }, + { id: `${Date.now()}`, model: modelConfig.model_id, plugin_id: modelConfig.plugin_id, provider: modelConfig.provider, parameters: completionParams }, + { id: `${Date.now()}-no-repeat`, model: '', plugin_id: '', provider: '', parameters: {} }, ], ) setAppSiderbarExpand('collapse') @@ -886,6 +893,7 @@ const Configuration: FC = () => { void + setModel: (model: { modelId: string; provider: string; pluginId: string; mode?: string; features?: string[] }) => void completionParams: FormValue onCompletionParamsChange: (newParams: FormValue) => void hideDebugWithMultipleModel?: boolean @@ -74,6 +75,7 @@ const ModelParameterModal: FC = ({ portalToFollowElemContentClassName, isAdvancedMode, modelId, + pluginId, provider, setModel, completionParams, @@ -88,13 +90,17 @@ const ModelParameterModal: FC = ({ const { t } = useTranslation() const { isAPIKeySet } = useProviderContext() const [open, setOpen] = useState(false) - const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules) + const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${pluginId}/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules) const { currentProvider, currentModel, activeTextGenerationModelList, } = useTextGenerationCurrentProviderAndModelAndModelList( - { provider, model: modelId }, + { + plugin_id: pluginId, + provider, + model: modelId, + }, ) const hasDeprecated = !currentProvider || !currentModel @@ -112,11 +118,12 @@ const ModelParameterModal: FC = ({ }) } - const handleChangeModel = ({ provider, model }: DefaultModel) => { + const handleChangeModel = ({ provider, model, plugin_id }: DefaultModel) => { const targetProvider = activeTextGenerationModelList.find(modelItem => modelItem.provider === provider) const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model) setModel({ modelId: model, + pluginId: plugin_id, provider, mode: targetModelItem?.model_properties.mode as string, features: targetModelItem?.features || [], @@ -201,7 +208,7 @@ const ModelParameterModal: FC = ({ {t('common.modelProvider.model').toLocaleUpperCase()}
= ({ defaultModel, ) - const handleSelect = (provider: string, model: ModelItem) => { + const handleSelect = (pluginId: string, provider: string, model: ModelItem) => { setOpen(false) if (onSelect) - onSelect({ provider, model: model.model }) + onSelect({ plugin_id: pluginId, provider, model: model.model }) } const handleToggle = () => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx index d62131ac4c..ce62494220 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx @@ -25,7 +25,7 @@ import Tooltip from '@/app/components/base/tooltip' type PopupItemProps = { defaultModel?: DefaultModel model: Model - onSelect: (provider: string, model: ModelItem) => void + onSelect: (pluginId: string, provider: string, model: ModelItem) => void } const PopupItem: FC = ({ defaultModel, @@ -39,11 +39,11 @@ const PopupItem: FC = ({ const updateModelList = useUpdateModelList() const updateModelProviders = useUpdateModelProviders() const currentProvider = modelProviders.find(provider => provider.provider === model.provider)! - const handleSelect = (provider: string, modelItem: ModelItem) => { + const handleSelect = (pluginId: string, provider: string, modelItem: ModelItem) => { if (modelItem.status !== ModelStatusEnum.active) return - onSelect(provider, modelItem) + onSelect(pluginId, provider, modelItem) } const handleOpenModelModal = () => { setShowModelModal({ @@ -80,7 +80,7 @@ const PopupItem: FC = ({ group relative flex items-center px-3 py-1.5 h-8 rounded-lg ${modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-gray-50' : 'cursor-not-allowed hover:bg-gray-50/60'} `} - onClick={() => handleSelect(model.provider, modelItem)} + onClick={() => handleSelect(model.plugin_id, model.provider, modelItem)} > void + onSelect: (pluginId: string, provider: string, model: ModelItem) => void } const Popup: FC = ({ defaultModel, diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 76607b29b1..038522df46 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -132,6 +132,7 @@ const Panel: FC> = ({ isInWorkflow isAdvancedMode={true} mode={model?.mode} + pluginId={model?.plugin_id} provider={model?.provider} completionParams={model?.completion_params} modelId={model?.name} diff --git a/web/app/components/workflow/nodes/llm/use-config.ts b/web/app/components/workflow/nodes/llm/use-config.ts index 33742b0726..7715383020 100644 --- a/web/app/components/workflow/nodes/llm/use-config.ts +++ b/web/app/components/workflow/nodes/llm/use-config.ts @@ -123,7 +123,7 @@ const useConfig = (id: string, payload: LLMNodeType) => { }, }) - const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => { + const handleModelChanged = useCallback((model: { provider: string; modelId: string; pluginId: string; mode?: string }) => { const newInputs = produce(inputRef.current, (draft) => { draft.model.provider = model.provider draft.model.name = model.modelId @@ -139,6 +139,7 @@ const useConfig = (id: string, payload: LLMNodeType) => { useEffect(() => { if (currentProvider?.provider && currentModel?.model && !model.provider) { handleModelChanged({ + pluginId: currentProvider?.plugin_id, provider: currentProvider?.provider, modelId: currentModel?.model, mode: currentModel?.model_properties?.mode as string, diff --git a/web/app/components/workflow/nodes/parameter-extractor/panel.tsx b/web/app/components/workflow/nodes/parameter-extractor/panel.tsx index e9d3856c71..edadc2e4ba 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/panel.tsx @@ -77,6 +77,7 @@ const Panel: FC> = ({ isInWorkflow isAdvancedMode={true} mode={model?.mode} + pluginId={model?.plugin_id} provider={model?.provider} completionParams={model?.completion_params} modelId={model?.name} diff --git a/web/app/components/workflow/nodes/question-classifier/panel.tsx b/web/app/components/workflow/nodes/question-classifier/panel.tsx index 523ec50019..4b2866204b 100644 --- a/web/app/components/workflow/nodes/question-classifier/panel.tsx +++ b/web/app/components/workflow/nodes/question-classifier/panel.tsx @@ -65,6 +65,7 @@ const Panel: FC> = ({ isInWorkflow isAdvancedMode={true} mode={model?.mode} + pluginId={model?.plugin_id} provider={model?.provider} completionParams={model.completion_params} modelId={model.name} diff --git a/web/app/components/workflow/nodes/question-classifier/types.ts b/web/app/components/workflow/nodes/question-classifier/types.ts index ca102b083e..ddc16b4501 100644 --- a/web/app/components/workflow/nodes/question-classifier/types.ts +++ b/web/app/components/workflow/nodes/question-classifier/types.ts @@ -1,6 +1,6 @@ import type { CommonNodeType, Memory, ModelConfig, ValueSelector, VisionSetting } from '@/app/components/workflow/types' -export interface Topic { +export type Topic = { id: string name: string } diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 811ec0d70c..ef26d9465f 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -37,7 +37,7 @@ export enum ControlMode { Hand = 'hand', } -export interface Branch { +export type Branch = { id: string name: string } @@ -68,7 +68,7 @@ export type CommonNodeType = { height?: number } & T & Partial> -export interface CommonEdgeType { +export type CommonEdgeType = { _hovering?: boolean _connectedNodeIsHovering?: boolean _connectedNodeIsSelected?: boolean @@ -82,14 +82,14 @@ export interface CommonEdgeType { export type Node = ReactFlowNode> export type SelectedNode = Pick -export interface NodeProps { id: string; data: CommonNodeType } -export interface NodePanelProps { +export type NodeProps = { id: string; data: CommonNodeType } +export type NodePanelProps = { id: string data: CommonNodeType } export type Edge = ReactFlowEdge -export interface WorkflowDataUpdater { +export type WorkflowDataUpdater = { nodes: Node[] edges: Edge[] viewport: Viewport @@ -97,7 +97,7 @@ export interface WorkflowDataUpdater { export type ValueSelector = string[] // [nodeId, key | obj key path] -export interface Variable { +export type Variable = { variable: string label?: string | { nodeType: BlockEnum @@ -112,14 +112,14 @@ export interface Variable { isParagraph?: boolean } -export interface EnvironmentVariable { +export type EnvironmentVariable = { id: string name: string value: any value_type: 'string' | 'number' | 'secret' } -export interface ConversationVariable { +export type ConversationVariable = { id: string name: string value_type: ChatVarType @@ -127,13 +127,13 @@ export interface ConversationVariable { description: string } -export interface GlobalVariable { +export type GlobalVariable = { name: string value_type: 'string' | 'number' description: string } -export interface VariableWithValue { +export type VariableWithValue = { key: string value: string } @@ -169,7 +169,8 @@ export type InputVar = { value_selector?: ValueSelector } & Partial -export interface ModelConfig { +export type ModelConfig = { + plugin_id: string provider: string name: string mode: string @@ -187,7 +188,7 @@ export enum EditionType { jinja2 = 'jinja2', } -export interface PromptItem { +export type PromptItem = { id?: string role?: PromptRole text: string @@ -200,12 +201,12 @@ export enum MemoryRole { assistant = 'assistant', } -export interface RolePrefix { +export type RolePrefix = { user: string assistant: string } -export interface Memory { +export type Memory = { role_prefix?: RolePrefix window: { enabled: boolean @@ -229,7 +230,7 @@ export enum VarType { any = 'any', } -export interface Var { +export type Var = { variable: string type: VarType children?: Var[] // if type is obj, has the children struct @@ -240,21 +241,21 @@ export interface Var { des?: string } -export interface NodeOutPutVar { +export type NodeOutPutVar = { nodeId: string title: string vars: Var[] isStartNode?: boolean } -export interface Block { +export type Block = { classification?: string type: BlockEnum title: string description?: string } -export interface NodeDefault { +export type NodeDefault = { defaultValue: Partial getAvailablePrevNodes: (isChatMode: boolean) => BlockEnum[] getAvailableNextNodes: (isChatMode: boolean) => BlockEnum[] @@ -294,19 +295,19 @@ export type OnNodeAdd = ( } ) => void -export interface CheckValidRes { +export type CheckValidRes = { isValid: boolean errorMessage?: string } -export interface RunFile { +export type RunFile = { type: string transfer_method: TransferMethod[] url?: string upload_file_id?: string } -export interface WorkflowRunningData { +export type WorkflowRunningData = { task_id?: string message_id?: string conversation_id?: string @@ -331,7 +332,7 @@ export interface WorkflowRunningData { tracing?: NodeTracing[] } -export interface HistoryWorkflowData { +export type HistoryWorkflowData = { id: string sequence_number: number status: string @@ -343,7 +344,7 @@ export enum ChangeType { remove = 'remove', } -export interface MoreInfo { +export type MoreInfo = { type: ChangeType payload?: { beforeKey: string @@ -363,7 +364,7 @@ export enum SupportUploadFileTypes { custom = 'custom', } -export interface UploadFileSetting { +export type UploadFileSetting = { allowed_file_upload_methods: TransferMethod[] allowed_file_types: SupportUploadFileTypes[] allowed_file_extensions?: string[] @@ -371,7 +372,7 @@ export interface UploadFileSetting { number_limits?: number } -export interface VisionSetting { +export type VisionSetting = { variable_selector: ValueSelector detail: Resolution } diff --git a/web/models/debug.ts b/web/models/debug.ts index 301248b234..64a6cb7f84 100644 --- a/web/models/debug.ts +++ b/web/models/debug.ts @@ -10,25 +10,25 @@ export enum PromptMode { advanced = 'advanced', } -export interface PromptItem { +export type PromptItem = { role?: PromptRole text: string } -export interface ChatPromptConfig { +export type ChatPromptConfig = { prompt: PromptItem[] } -export interface ConversationHistoriesRole { +export type ConversationHistoriesRole = { user_prefix: string assistant_prefix: string } -export interface CompletionPromptConfig { +export type CompletionPromptConfig = { prompt: PromptItem conversation_histories_role: ConversationHistoriesRole } -export interface BlockStatus { +export type BlockStatus = { context: boolean history: boolean query: boolean @@ -40,7 +40,7 @@ export enum PromptRole { assistant = 'assistant', } -export interface PromptVariable { +export type PromptVariable = { key: string name: string type: string // "string" | "number" | "select", @@ -55,7 +55,7 @@ export interface PromptVariable { icon_background?: string } -export interface CompletionParams { +export type CompletionParams = { max_tokens: number temperature: number top_p: number @@ -66,12 +66,12 @@ export interface CompletionParams { export type ModelId = 'gpt-3.5-turbo' | 'text-davinci-003' -export interface PromptConfig { +export type PromptConfig = { prompt_template: string prompt_variables: PromptVariable[] } -export interface MoreLikeThisConfig { +export type MoreLikeThisConfig = { enabled: boolean } @@ -79,7 +79,7 @@ export type SuggestedQuestionsAfterAnswerConfig = MoreLikeThisConfig export type SpeechToTextConfig = MoreLikeThisConfig -export interface TextToSpeechConfig { +export type TextToSpeechConfig = { enabled: boolean voice?: string language?: string @@ -88,7 +88,7 @@ export interface TextToSpeechConfig { export type CitationConfig = MoreLikeThisConfig -export interface AnnotationReplyConfig { +export type AnnotationReplyConfig = { id: string enabled: boolean score_threshold: number @@ -98,7 +98,7 @@ export interface AnnotationReplyConfig { } } -export interface ModerationContentConfig { +export type ModerationContentConfig = { enabled: boolean preset_response?: string } @@ -113,14 +113,15 @@ export type ModerationConfig = MoreLikeThisConfig & { } export type RetrieverResourceConfig = MoreLikeThisConfig -export interface AgentConfig { +export type AgentConfig = { enabled: boolean strategy: AgentStrategy max_iteration: number tools: ToolItem[] } // frontend use. Not the same as backend -export interface ModelConfig { +export type ModelConfig = { + plugin_id: string provider: string // LLM Provider: for example "OPENAI" model_id: string mode: ModelModeType @@ -138,16 +139,17 @@ export interface ModelConfig { dataSets: any[] agentConfig: AgentConfig } -export interface DatasetConfigItem { +export type DatasetConfigItem = { enable: boolean value: number } -export interface DatasetConfigs { +export type DatasetConfigs = { retrieval_model: RETRIEVE_TYPE reranking_model: { reranking_provider_name: string reranking_model_name: string + reranking_plugin_id: string } top_k: number score_threshold_enabled: boolean @@ -172,39 +174,39 @@ export interface DatasetConfigs { reranking_enable?: boolean } -export interface DebugRequestBody { +export type DebugRequestBody = { inputs: Inputs query: string completion_params: CompletionParams model_config: ModelConfig } -export interface DebugResponse { +export type DebugResponse = { id: string answer: string created_at: string } -export interface DebugResponseStream { +export type DebugResponseStream = { id: string data: string created_at: string } -export interface FeedBackRequestBody { +export type FeedBackRequestBody = { message_id: string rating: 'like' | 'dislike' content?: string from_source: 'api' | 'log' } -export interface FeedBackResponse { +export type FeedBackResponse = { message_id: string rating: 'like' | 'dislike' } // Log session list -export interface LogSessionListQuery { +export type LogSessionListQuery = { keyword?: string start?: string // format datetime(YYYY-mm-dd HH:ii) end?: string // format datetime(YYYY-mm-dd HH:ii) @@ -212,7 +214,7 @@ export interface LogSessionListQuery { limit: number // default 20. 1-100 } -export interface LogSessionListResponse { +export type LogSessionListResponse = { data: { id: string conversation_id: string @@ -226,7 +228,7 @@ export interface LogSessionListResponse { } // log session detail and debug -export interface LogSessionDetailResponse { +export type LogSessionDetailResponse = { id: string conversation_id: string model_provider: string @@ -240,7 +242,7 @@ export interface LogSessionDetailResponse { from_source: 'api' | 'log' } -export interface SavedMessage { +export type SavedMessage = { id: string answer: string } diff --git a/web/service/debug.ts b/web/service/debug.ts index 093cddfd62..887fde02c6 100644 --- a/web/service/debug.ts +++ b/web/service/debug.ts @@ -3,13 +3,13 @@ import type { IOnCompleted, IOnData, IOnError, IOnFile, IOnMessageEnd, IOnMessag import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug' import type { ModelModeType } from '@/types/app' import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' -export interface AutomaticRes { +export type AutomaticRes = { prompt: string variables: string[] opening_statement: string error?: string } -export interface CodeGenRes { +export type CodeGenRes = { code: string language: string[] error?: string @@ -82,8 +82,8 @@ export const generateRuleCode = (body: Record) => { }) } -export const fetchModelParams = (providerName: string, modelId: string) => { - return get(`workspaces/current/model-providers/${providerName}/models/parameter-rules`, { +export const fetchModelParams = (pluginID: string, providerName: string, modelId: string) => { + return get(`workspaces/current/model-providers/${pluginID}/${providerName}/models/parameter-rules`, { params: { model: modelId, },