diff --git a/api/controllers/console/workspace/workspace.py b/api/controllers/console/workspace/workspace.py index 34af80bca7..03dd4f6eb8 100644 --- a/api/controllers/console/workspace/workspace.py +++ b/api/controllers/console/workspace/workspace.py @@ -48,6 +48,7 @@ tenant_fields = { "in_trial": fields.Boolean, "trial_end_reason": fields.String, "custom_config": fields.Raw(attribute="custom_config"), + "beta_config": fields.Raw(attribute="beta_config"), } tenants_fields = { @@ -177,6 +178,31 @@ class CustomConfigWorkspaceApi(Resource): return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)} +class BetaConfigWorkspaceApi(Resource): + @setup_required + @login_required + @account_initialization_required + def post(self): + parser = reqparse.RequestParser() + parser.add_argument("beta_config", type=dict, location="json") + args = parser.parse_args() + + tenant = db.get_or_404(Tenant, current_user.current_tenant_id) + + db_config = tenant.beta_config_dict + param_config = args["beta_config"] or {} + + if not db_config: + tenant.beta_config_dict = param_config + else: + merged_config = {**db_config, **param_config} + tenant.beta_config_dict = merged_config + + db.session.commit() + + return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)} + + class WebappLogoWorkspaceApi(Resource): @setup_required @login_required @@ -239,5 +265,6 @@ api.add_resource(TenantApi, "/workspaces/current", endpoint="workspaces_current" api.add_resource(TenantApi, "/info", endpoint="info") # Deprecated api.add_resource(SwitchWorkspaceApi, "/workspaces/switch") # POST for switching tenant api.add_resource(CustomConfigWorkspaceApi, "/workspaces/custom-config") +api.add_resource(BetaConfigWorkspaceApi, "/workspaces/beta-config") api.add_resource(WebappLogoWorkspaceApi, "/workspaces/custom-config/webapp-logo/upload") api.add_resource(WorkspaceInfoApi, "/workspaces/info") # POST for changing workspace info diff --git a/api/models/account.py b/api/models/account.py index bb6a2a4735..4e81b7cc5d 100644 --- a/api/models/account.py +++ b/api/models/account.py @@ -199,6 +199,7 @@ class Tenant(Base): plan = db.Column(db.String(255), nullable=False, server_default=db.text("'basic'::character varying")) status = db.Column(db.String(255), nullable=False, server_default=db.text("'normal'::character varying")) custom_config = db.Column(db.Text) + beta_config = db.Column(db.Text) created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @@ -217,6 +218,14 @@ class Tenant(Base): def custom_config_dict(self, value: dict): self.custom_config = json.dumps(value) + @property + def beta_config_dict(self) -> dict: + return json.loads(self.beta_config) if self.beta_config else {} + + @beta_config_dict.setter + def beta_config_dict(self, value: dict): + self.beta_config = json.dumps(value) + class TenantAccountJoin(Base): __tablename__ = "tenant_account_joins" diff --git a/api/services/workspace_service.py b/api/services/workspace_service.py index 125e0c1b1e..034c6ad4fc 100644 --- a/api/services/workspace_service.py +++ b/api/services/workspace_service.py @@ -20,6 +20,7 @@ class WorkspaceService: "created_at": tenant.created_at, "trial_end_reason": None, "role": "normal", + "beta_config": tenant.beta_config_dict, } # Get role of user diff --git a/web/app/components/header/account-setting/beta-page/index.tsx b/web/app/components/header/account-setting/beta-page/index.tsx new file mode 100644 index 0000000000..8283e6ab71 --- /dev/null +++ b/web/app/components/header/account-setting/beta-page/index.tsx @@ -0,0 +1,45 @@ +import { useTranslation } from 'react-i18next' +import Switch from '@/app/components/base/switch' +import { + updateCurrentWorkspace, +} from '@/service/common' +import { useAppContext } from '@/context/app-context' + +const BetaPage = () => { + const { t } = useTranslation() + const { + currentWorkspace, + mutateCurrentWorkspace, + } = useAppContext() + const workflowVarCheck = currentWorkspace.beta_config?.workflow_var_check + + const handleSwitch = async (checked: boolean) => { + await updateCurrentWorkspace({ + url: '/workspaces/beta-config', + body: { + beta_config: { + workflow_var_check: checked, + }, + }, + }) + mutateCurrentWorkspace() + } + + return ( +
+
+
+
{t('common.beta.workflowVarCheck')}
+
{t('common.beta.workflowVarCheckTip')}
+
+ +
+
+ ) +} + +export default BetaPage diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx index b2a3c8245b..83098f6698 100644 --- a/web/app/components/header/account-setting/index.tsx +++ b/web/app/components/header/account-setting/index.tsx @@ -15,6 +15,8 @@ import { RiMoneyDollarCircleLine, RiPuzzle2Fill, RiPuzzle2Line, + RiTestTubeFill, + RiTestTubeLine, RiTranslate2, } from '@remixicon/react' import Button from '../../base/button' @@ -31,6 +33,7 @@ import { useProviderContext } from '@/context/provider-context' import { useAppContext } from '@/context/app-context' import MenuDialog from '@/app/components/header/account-setting/menu-dialog' import Input from '@/app/components/base/input' +import BatePage from './beta-page' const iconClassName = ` w-5 h-5 mr-2 @@ -56,8 +59,7 @@ export default function AccountSetting({ const [activeMenu, setActiveMenu] = useState(activeTab) const { t } = useTranslation() const { enableBilling, enableReplaceWebAppLogo } = useProviderContext() - const { isCurrentWorkspaceDatasetOperator } = useAppContext() - + const { isCurrentWorkspaceDatasetOperator, currentWorkspace } = useAppContext() const workplaceGroupItems = (() => { if (isCurrentWorkspaceDatasetOperator) return [] @@ -100,6 +102,12 @@ export default function AccountSetting({ icon: , activeIcon: , }, + { + key: currentWorkspace.role === 'owner' ? 'beta' : false, + name: t('common.beta.beta'), + icon: , + activeIcon: , + }, ].filter(item => !!item.key) as GroupItem[] })() @@ -220,6 +228,7 @@ export default function AccountSetting({ {activeMenu === 'api-based-extension' && } {activeMenu === 'custom' && } {activeMenu === 'language' && } + {activeMenu === 'beta' && } diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index ebef0e0c2c..13a6c53250 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -34,6 +34,7 @@ import { useDatasetsDetailStore } from '../datasets-detail-store/store' import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/types' import type { DataSet } from '@/models/datasets' import { fetchDatasets } from '@/service/datasets' +import { useAppContext } from '@/context/app-context' export const useChecklist = (nodes: Node[], edges: Edge[]) => { const { t } = useTranslation() @@ -45,6 +46,9 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { const workflowTools = useStore(s => s.workflowTools) const { data: strategyProviders } = useStrategyProviders() const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail) + const { + currentWorkspace, + } = useAppContext() const chatVarList = useStore(s => s.conversationVariables) const environmentVariables = useStore(s => s.environmentVariables) @@ -72,6 +76,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables) const errMessageMap = new Map() + const workflowVarCheck = currentWorkspace.beta_config?.workflow_var_check for (let i = 0; i < nodes.length; i++) { const node = nodes[i] @@ -120,7 +125,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { varErrorMessage: [], }) } - if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) { + if (workflowVarCheck && nodesExtraData[node.data.type as BlockEnum].checkVarValid) { const { errorMessage: varErrorMessages } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t) if (varErrorMessages?.length) { diff --git a/web/app/components/workflow/nodes/http/default.ts b/web/app/components/workflow/nodes/http/default.ts index 1c027a3fec..1b89ac1961 100644 --- a/web/app/components/workflow/nodes/http/default.ts +++ b/web/app/components/workflow/nodes/http/default.ts @@ -7,6 +7,7 @@ import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS, } from '@/app/components/workflow/blocks' +import { transformToBodyPayload } from './utils' const nodeDefault: NodeDefault = { defaultValue: { @@ -78,7 +79,9 @@ const nodeDefault: NodeDefault = { const body_warnings: string[] = [] if ([BodyType.binary].includes(payload.body.type)) { - const body_data = payload.body.data as BodyPayload + let body_data = payload.body.data + if (typeof body_data === 'string') + body_data = transformToBodyPayload(body_data, false) body_data.forEach((item) => { const key_warnings = getNotExistVariablesByText(item.key || '', varMap) if (key_warnings.length) @@ -89,7 +92,9 @@ const nodeDefault: NodeDefault = { }) } else { - const body_data = payload.body.data as BodyPayload + let body_data = payload.body.data + if (typeof body_data === 'string') + body_data = transformToBodyPayload(body_data, [BodyType.formData, BodyType.xWwwFormUrlencoded].includes(payload.body.type)) body_data.forEach((item) => { const key_warnings = getNotExistVariablesByText(item.key || '', varMap) if (key_warnings.length) diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index f3a7e09ab7..f7f008a854 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -273,6 +273,11 @@ const translation = { setBuilder: 'Set as builder (设置为构建器)', builder: '构建器', }, + beta: { + beta: 'Beta 功能', + workflowVarCheck: '严格模式-变量可用性检查', + workflowVarCheckTip: '启用后,cahflow/workflow 将会检查节点中变量是否可用。', + }, integrations: { connected: '登录方式', google: 'Google', diff --git a/web/models/common.ts b/web/models/common.ts index cb8fb7f2bf..705e66e56c 100644 --- a/web/models/common.ts +++ b/web/models/common.ts @@ -134,6 +134,9 @@ export type ICurrentWorkspace = Omit & { custom_config?: { remove_webapp_brand?: boolean replace_webapp_logo?: string + }, + beta_config?: { + workflow_var_check?: boolean } }