feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
/* stylelint-disable no-duplicate-selectors */
.icon-button-16 {
cursor: pointer;
&:hover {
border-radius: 4px;
}
:global {
.semi-button {
&.semi-button-size-small {
height: 16px;
/* stylelint-disable declaration-no-important */
padding: 1px !important;
}
}
}
&.icon-disabled {
cursor: default;
opacity: 0.2;
}
}
.agent-skill-setting-modal-frame {
z-index: 1050 !important;
:global {
.semi-modal {
height: 84%;
max-height: 756px;
}
.semi-modal-body {
height: calc(100% - 72px);
}
.semi-modal-content {
padding: 0;
}
.semi-modal-header {
margin: 0 !important;
padding: 24px;
//border-bottom: 1px solid rgba(29, 28, 37, 0.08);
background: #F7F7FA;
}
.semi-modal-body {
padding: 0 !important;
}
.semi-navigation-list-wrapper {
padding-top: 24px;
}
.semi-navigation {
padding-right: 12px;
padding-left: 12px;
background: #F0F0F5;
border-right: 0;
}
.semi-navigation-item-selected {
font-weight: 600 !important;
background: rgba(28, 28, 36, 5%);
border-radius: 8px;
}
.semi-navigation-list > .semi-navigation-item {
font-weight: 400;
}
.semi-table-placeholder {
display: none;
}
.semi-table-row-cell {
border-bottom: 0 !important;
}
.semi-table-thead > .semi-table-row > .semi-table-row-head {
border-bottom: 1px solid rgba(56, 55, 67, 8%) !important;
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useMemo, useState } from 'react';
import { useRequest } from 'ahooks';
import {
type BindSubjectInfo,
type BizCtxInfo,
} from '@coze-studio/mockset-shared';
import { I18n } from '@coze-arch/i18n';
import { useSpaceStore } from '@coze-arch/bot-studio-store';
import { useFlags } from '@coze-arch/bot-flags';
import {
type PluginInfoForPlayground,
type PluginApi,
} from '@coze-arch/bot-api/plugin_develop';
import { PluginDevelopApi } from '@coze-arch/bot-api';
import { ToolItemActionSetting } from '@coze-agent-ide/tool';
import { PartMain, type SettingSlot } from './part-main';
export interface IAgentSkillPluginSettingModalProps {
botId?: string;
apiInfo?: PluginApi;
devId?: string;
plugin?: PluginInfoForPlayground;
bindSubjectInfo?: BindSubjectInfo;
bizCtx?: BizCtxInfo;
disabled?: boolean;
slotList?: SettingSlot[];
}
const useAgentSkillPluginSettingModalController = (
config: IAgentSkillPluginSettingModalProps,
) => {
const [FLAGS] = useFlags();
const [visible, setVisible] = useState(!!0);
const commonParams = useMemo(
() => ({
bot_id: config?.botId || '',
dev_id: config?.devId || '',
plugin_id: config?.apiInfo?.plugin_id || '',
api_name: config?.apiInfo?.name || '',
space_id: useSpaceStore.getState().getSpaceId(),
}),
[config],
);
const { data: responseData, loading: isCheckingResponse } = useRequest(
async () => {
const resp = await PluginDevelopApi.GetBotDefaultParams(commonParams);
return resp.response_params;
},
{
refreshDeps: [commonParams],
// 社区版暂不支持该功能
ready: visible && FLAGS['bot.devops.plugin_mockset'],
},
);
// mock-set 支持设置禁用,端插件类型不支持
// 没有 response的 也不能 开启 mock-set
const isDisabledMockSet = useMemo(
() => !responseData?.length || isCheckingResponse,
[responseData, isCheckingResponse],
);
return {
isDisabledMockSet,
pluginInfo: config.plugin,
doVisible: setVisible,
visible,
};
};
export const PluginSettingEnter = (
props: IAgentSkillPluginSettingModalProps,
) => {
const { doVisible, visible, pluginInfo, isDisabledMockSet } =
useAgentSkillPluginSettingModalController(props);
return (
<>
<PartMain
devId={props.devId}
botId={props.botId}
isDisabledMockSet={isDisabledMockSet}
pluginInfo={pluginInfo}
// @ts-expect-error -- linter-disable-autofix
apiInfo={props.apiInfo}
// @ts-expect-error -- linter-disable-autofix
bindSubjectInfo={props.bindSubjectInfo}
// @ts-expect-error -- linter-disable-autofix
bizCtx={props.bizCtx}
doVisible={doVisible}
visible={visible}
slotList={props.slotList}
/>
<ToolItemActionSetting
tooltips={I18n.t('plugin_bot_ide_plugin_setting_icon_tip')}
onClick={() => doVisible(!0)}
disabled={props.disabled}
data-testid="bot.editor.tool.added-tool-plugin-action-setting"
/>
</>
);
};

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useRef, useState } from 'react';
import {
type BindSubjectInfo,
type BizCtxInfo,
} from '@coze-studio/mockset-shared';
import { I18n } from '@coze-arch/i18n';
import { Nav, UIModal } from '@coze-arch/bot-semi';
import { useFlags } from '@coze-arch/bot-flags';
import {
type PluginInfoForPlayground,
type PluginApi,
} from '@coze-arch/bot-api/plugin_develop';
import { PartParams } from './part-params-set';
import { PartMockSet } from './part-mock-set';
import s from './index.module.less';
export interface SettingSlot {
key: string;
label: string;
reactNode: React.ReactNode;
}
interface IProp {
// mock-set
bindSubjectInfo: BindSubjectInfo;
bizCtx: BizCtxInfo;
isDisabledMockSet?: boolean;
// params
botId?: string;
devId?: string;
pluginInfo?: PluginInfoForPlayground;
apiInfo: PluginApi;
// modal
visible: boolean;
doVisible: (v: boolean) => void;
slotList?: SettingSlot[];
}
const MOCK_SET = 'MockSet';
const PARA = 'Parameters';
const usePartMainController = (pluginInfo: PluginInfoForPlayground) => {
const [FLAGS] = useFlags();
const [selectedKey, doSetSelectKey] = useState(
FLAGS['bot.devops.plugin_mockset']
? MOCK_SET.toLocaleLowerCase()
: PARA.toLocaleLowerCase(),
);
const keyOptions = [
{
label: I18n.t('bot_ide_plugin_setting_modal_parameter_tab'),
value: PARA.toLowerCase(),
},
];
// 社区版暂不支持该功能
if (FLAGS['bot.devops.plugin_mockset']) {
keyOptions.unshift({
label: I18n.t('bot_ide_plugin_setting_modal_mockset_tab'),
value: MOCK_SET.toLowerCase(),
});
}
return {
selectedKey,
doSetSelectKey,
keyOptions,
};
};
const PartMain = ({
botId,
devId,
doVisible,
visible,
pluginInfo,
apiInfo,
bindSubjectInfo,
bizCtx,
isDisabledMockSet = !!0,
slotList,
}: IProp) => {
const { keyOptions, selectedKey, doSetSelectKey } =
// @ts-expect-error -- linter-disable-autofix
usePartMainController(pluginInfo);
const contentRef = useRef<HTMLDivElement>();
return (
<UIModal
width={1138}
className={s['agent-skill-setting-modal-frame']}
visible={visible}
footer={null}
onCancel={() => doVisible(!!0)}
title={
<div className="absolute w-[240px] left-[0] top-[0] p-[24px] bg-[#f0f0f5]">
{I18n.t('basic_setting')}
</div>
}
>
<div className="flex h-[100%]">
<Nav
className="h-[100%]"
selectedKeys={selectedKey ? [selectedKey] : [keyOptions?.[0].value]}
onSelect={({ itemKey }) => doSetSelectKey(itemKey as string)}
>
{keyOptions.map(k => (
<Nav.Item key={k.value} text={k.label} itemKey={k.value} />
))}
{slotList?.map(item => (
<Nav.Item key={item.key} itemKey={item.key} text={item.label} />
))}
</Nav>
<div
className="p-[24px] flex-[1] bg-[#F7F7FA] h-[100%]"
// @ts-expect-error -- linter-disable-autofix
ref={contentRef}
>
{selectedKey === MOCK_SET.toLowerCase() && visible ? (
<PartMockSet
// @ts-expect-error -- linter-disable-autofix
contentRef={contentRef}
bindSubjectInfo={bindSubjectInfo}
bizCtx={bizCtx}
readonly={isDisabledMockSet}
/>
) : null}
{selectedKey === PARA.toLowerCase() && visible ? (
<PartParams
// @ts-expect-error -- linter-disable-autofix
contentRef={contentRef}
apiName={apiInfo?.name}
botId={botId}
pluginId={apiInfo?.plugin_id}
devId={devId}
/>
) : null}
{visible
? slotList?.find(item => item.key === selectedKey)?.reactNode
: null}
</div>
</div>
</UIModal>
);
};
export { PartMain };

View File

@@ -0,0 +1,303 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @coze-arch/max-line-per-function */
import { useMemo, type MutableRefObject } from 'react';
import { useSize } from 'ahooks';
import { I18n } from '@coze-arch/i18n';
import { type ColumnProps } from '@coze-arch/bot-semi/Table';
import {
Popconfirm,
Radio,
Space,
Spin,
Switch,
Table,
Tooltip,
Typography,
UIButton,
UIIconButton,
} from '@coze-arch/bot-semi';
import { type MockSet, TrafficScene } from '@coze-arch/bot-api/debugger_api';
import { IconAlertCircle } from '@douyinfe/semi-icons';
import {
builtinSuccessCallback,
MockSetEditModal,
} from '@coze-studio/mockset-edit-modal-adapter';
import {
IconAdd,
IconDeleteOutline,
IconEdit,
IconInfo,
} from '@coze-arch/bot-icons';
import { type MockSetSelectProps } from '@coze-agent-ide/plugin-shared';
import { isRealData } from '@coze-agent-ide/bot-plugin-mock-set/util';
import { useMockSetInSettingModalController } from '@coze-agent-ide/bot-plugin-mock-set/hook/use-mock-set-in-setting-modal';
// @ts-expect-error -- linter-disable-autofix
const FormTitle = titleInfo => (
<div className="flex items-start">
<span className="text-[rgba(28,_29,_36,_0.80)] text-[12px] not-italic font-medium leading-[16px] whitespace-nowrap">
{titleInfo.name}
</span>
{titleInfo.required ? (
<Typography.Text className="text-red text-[12px] not-italic font-medium leading-[16px]">
{' * '}
</Typography.Text>
) : null}
{titleInfo.toolTipText ? (
<Tooltip content={titleInfo.toolTipText}>
<IconInfo
className="ml-[4px] w-[14px] h-[14px]"
style={{
color: '#5f5f5f9e',
}}
/>
</Tooltip>
) : null}
</div>
);
const GAP = 200;
const PartMockSet = ({
bindSubjectInfo,
bizCtx,
readonly,
contentRef,
}: MockSetSelectProps & {
contentRef: MutableRefObject<HTMLDivElement>;
}) => {
const {
isEnabled,
doEnabled,
doSetCreateModal,
showCreateModal,
doHandleView,
selectedMockSet,
mockSetData,
isSettingLoading,
isListLoading,
doChangeMock,
initialInfo,
doSetDeleteId,
deleteRenderTitle,
doConfirmDelete,
} = useMockSetInSettingModalController({ bindSubjectInfo, bizCtx, readonly });
const size = useSize(contentRef);
const scroll = useMemo(() => ({ y: (size?.height ?? 0) - GAP }), [size]);
const columns: Array<ColumnProps<MockSet>> = [
{
title: () => <FormTitle name={I18n.t('analytic_query_name')} required />,
key: 'name',
width: 200,
render: record => (
<Typography.Text
component="span"
ellipsis={{
showTooltip: {
type: 'tooltip',
opts: { style: { maxWidth: '100%' } },
},
}}
>
{record.name}
</Typography.Text>
),
},
{
title: () => <FormTitle name={I18n.t('Description')} />,
key: 'description',
width: 360,
render: record => (
<Typography.Text
component="div"
ellipsis={{
showTooltip: {
opts: {
style: { wordBreak: 'break-word' },
},
},
}}
style={{ maxWidth: '100%' }}
>
{record.description ? record.description : '-'}
</Typography.Text>
),
},
{
title: () => <FormTitle name={I18n.t('use_in_bot')} />,
width: 95,
render: record => {
const isInValid =
!isRealData(record) &&
(record?.schemaIncompatible || !record?.mockRuleQuantity);
const getTooltipInfo = () => {
if (record?.schemaIncompatible) {
return I18n.t('tool_updated_check_mockset_compatibility');
} else if ((record?.mockRuleQuantity || 0) <= 0) {
return I18n.t('mockset_is_empty_add_data_before_use');
}
return '';
};
return isInValid ? (
<Tooltip content={getTooltipInfo()} position="left">
<Radio
disabled={isInValid}
checked={selectedMockSet?.id === record.id}
onChange={() => {
if (selectedMockSet?.id !== record.id) {
doChangeMock(record);
}
}}
/>
</Tooltip>
) : (
<Radio
disabled={isInValid}
checked={selectedMockSet?.id === record.id}
onChange={() => {
if (selectedMockSet?.id !== record.id) {
doChangeMock(record);
}
}}
/>
);
},
},
{
title: () => <FormTitle name={I18n.t('Actions')} />,
key: 'action',
width: 60,
render: record => (
<Space spacing={8}>
<Tooltip content={I18n.t('binding_edit_card')}>
<UIIconButton
disabled={readonly}
onClick={() => doHandleView(record)}
icon={<IconEdit />}
type="secondary"
/>
</Tooltip>
<Tooltip content={I18n.t('Delete')}>
<Popconfirm
zIndex={1051}
okType="danger"
style={{ width: 400 }}
icon={
<IconAlertCircle style={{ color: 'red' }} size="extra-large" />
}
trigger="click"
position="bottomRight"
title={deleteRenderTitle}
content={I18n.t('operation_cannot_be_reversed')}
onConfirm={() => doConfirmDelete()}
onCancel={() => doSetDeleteId(undefined)}
>
<UIIconButton
disabled={readonly}
icon={<IconDeleteOutline />}
type="secondary"
onClick={() => doSetDeleteId(record.id)}
/>
</Popconfirm>
</Tooltip>
</Space>
),
},
];
if (isListLoading) {
return <Spin spinning style={{ height: '400px', width: '100%' }} />;
}
return (
<>
<div className="flex items-center justify-between text-[#1C1D24] text-[16px] not-italic font-semibold leading-[22px] mb-[12px] h-[32px]">
<span>MockSet</span>
<Tooltip
position="left"
className={readonly ? undefined : 'hidden'}
content={I18n.t(
'cannot_enable_mock_set_due_to_no_configured_return_value',
)}
>
<Switch
checked={isEnabled}
onChange={doEnabled}
disabled={readonly}
loading={isSettingLoading}
/>
</Tooltip>
</div>
{!isEnabled && (
<p className="text-[rgba(29,_28,_36,_0.60)] text-[14px] leading-[20px]">
{I18n.t('mock_enable_switch')}
</p>
)}
{isEnabled ? (
<div className="border-[1px] border-solid border-[rgba(46,46,57,0.08)] rounded-[8px] pl-[16px] pr-[16px] py-[0]">
<Table
bordered={!!0}
// loading={isListLoading}
scroll={scroll}
pagination={false}
columns={columns}
dataSource={mockSetData ?? []}
rowKey={'id'}
expandAllRows={true}
empty={<div></div>}
/>
<div className="pt-[12px] mb-[12px] [border-top:1px_solid_rgba(_56,55,67_,0.08)]">
<UIButton
icon={<IconAdd />}
type="tertiary"
onClick={() => doSetCreateModal(!0)}
>
{I18n.t('binding_add_card')}
</UIButton>
</div>
</div>
) : null}
{showCreateModal ? (
<MockSetEditModal
zIndex={9999}
visible={showCreateModal}
onCancel={() => doSetCreateModal(false)}
onSuccess={(info, config) => {
const { id } = info || {};
doSetCreateModal(false);
builtinSuccessCallback(config);
doHandleView({ id }, config);
}}
initialInfo={initialInfo}
needResetPopoverContainer={
bizCtx.trafficScene === TrafficScene.CozeWorkflowDebug
}
/>
) : null}
</>
);
};
export { PartMockSet };

View File

@@ -0,0 +1,137 @@
.icon-button-16 {
cursor: pointer;
&:hover {
border-radius: 4px;
}
:global {
.semi-button {
&.semi-button-size-small {
height: 16px;
/* stylelint-disable declaration-no-important */
padding: 1px !important;
}
}
}
&.icon-disabled {
cursor: default;
opacity: 0.2;
}
}
.params-modal {
.name-wrap {
display: inline-block;
vertical-align: middle;
}
.name {
font-size: 14px;
font-weight: 600;
line-height: 20px;
color: #1D1C24;
text-align: left;
}
.desc {
font-size: 12px;
line-height: 16px;
color: rgba(29, 28, 36, 60%);
text-align: left;
}
:global {
/* stylelint-disable-next-line selector-class-pattern */
.semi-radioGroup {
margin-bottom: 24px;
}
/* stylelint-disable-next-line selector-class-pattern */
.semi-radio-addon-buttonRadio {
border-radius: 4px;
}
.semi-radio-content {
flex: 1;
}
.semi-modal-body {
display: flex;
flex-direction: column;
}
.semi-modal-content {
min-height: 400px;
}
.semi-table-row {
&:has(.disable) {
display: none;
}
}
.semi-table-thead>.semi-table-row>.semi-table-row-head {
box-sizing: border-box;
height: 16px;
padding: 12px;
font-size: 12px;
font-weight: 500;
line-height: 16px;
color: rgba(29, 28, 36, 80%);
border-bottom-width: 1px;
// &:first-child {
// padding-left: 32px;
// }
&:last-child {
padding-right: 0;
}
}
.semi-table-body {
margin-top: 16px;
// min-height: 400px;
}
.semi-table-tbody>.semi-table-row>.semi-table-row-cell {
overflow: hidden;
padding: 13px 0;
padding-left: 12px;
text-overflow: ellipsis;
white-space: nowrap;
background-image: none;
border-bottom: none;
.semi-switch {
left: 0 !important;
}
&:first-child {
padding-right: 12px;
}
}
.semi-table-tbody>.semi-table-row:hover>.semi-table-row-cell {
background-color: transparent;
}
.semi-table-expand-icon {
margin-right: 8px;
vertical-align: middle;
}
.semi-table-placeholder {
border-bottom: 0;
}
}
}

View File

@@ -0,0 +1,390 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type MutableRefObject, useMemo, useEffect, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import classnames from 'classnames';
import { useMemoizedFn, useSize } from 'ahooks';
import { withSlardarIdButton } from '@coze-studio/bot-utils';
import { logger } from '@coze-arch/logger';
import { I18n } from '@coze-arch/i18n';
import { type ColumnProps } from '@coze-arch/bot-semi/Table';
import { type OptionProps } from '@coze-arch/bot-semi/Select';
import {
Radio,
RadioGroup,
Spin,
Switch,
Table,
Tooltip,
Typography,
} from '@coze-arch/bot-semi';
import {
ParameterType,
type APIParameter,
} from '@coze-arch/bot-api/plugin_develop';
import { MemoryApi } from '@coze-arch/bot-api';
import { useBotSkillStore } from '@coze-studio/bot-detail-store/bot-skill';
import { IconInfo } from '@coze-arch/bot-icons';
import {
useParametersInSettingModalController,
type SettingParamsProps,
} from '@coze-agent-ide/bot-plugin-tools/useParametersInSettingModalController';
import {
ROWKEY,
childrenRecordName,
} from '@coze-agent-ide/bot-plugin-tools/pluginModal/config';
import { DefaultValueInput } from '@coze-agent-ide/bot-plugin-tools/defaultValueInput';
import { Button, Toast } from '@coze-arch/coze-design';
import s from './index.module.less';
const GAP = 250;
/* eslint-disable @coze-arch/max-line-per-function */
// eslint-disable-next-line max-lines-per-function
const PartParams = (
props: SettingParamsProps & { contentRef: MutableRefObject<HTMLDivElement> },
) => {
const {
doUpdateNodeWithData,
getColumnClass,
doUpdateParams,
loaded,
doSetActive,
activeTab,
responseParams,
requestParams,
doSetReqParams,
isUpdateLoading,
} = useParametersInSettingModalController(props);
const size = useSize(props.contentRef);
// 变量事例值列表
const [variableOption, setVariableOption] = useState<OptionProps[]>([]);
const { variables } = useBotSkillStore(
useShallow(state => ({
variables: state.variables,
})),
);
useEffect(() => {
getReferenceList();
}, []);
const getReferenceList = async () => {
const res = await MemoryApi.GetSysVariableConf();
const varList = variables?.map(item => {
const example = res.conf?.find(conf => conf.key === item.key);
return {
label: item.key, // 变量名
value: example?.example ?? item.default_value ?? '', // 事例值,无则默认值
};
});
setVariableOption(varList);
};
const columns = useMemo(() => {
const res: ColumnProps[] = [
{
title: I18n.t('Create_newtool_s4_name'),
key: 'name',
className: 'no-wrap',
render: (record: APIParameter & { deep?: number }) => (
<div
className={classnames(getColumnClass(record), s.nameWrap)}
style={{
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- ui
maxWidth: `calc(100% - ${20 * (record.deep || 1) + 9}px)`,
}}
>
<Typography.Text
className={s.name}
component="span"
ellipsis={{
showTooltip: {
opts: { style: { wordBreak: 'break-word' } },
},
}}
>
{record?.name}
</Typography.Text>
<br />
{!!record?.desc && (
<Typography.Text
className={s.desc}
component="span"
ellipsis={{
showTooltip: {
opts: { style: { wordBreak: 'break-word' } },
},
}}
>
{record?.desc}
</Typography.Text>
)}
</div>
),
},
{
title: I18n.t('Create_newtool_s4_type'),
key: 'type',
className: 'no-wrap',
width: 120,
render: (record: APIParameter) =>
record?.type && (
<span className={getColumnClass(record)}>
{ParameterType[record?.type]}
</span>
),
},
{
title: I18n.t('bot_edit_page_plugin_tool_param_required'),
key: 'default',
className: 'no-wrap',
width: 104,
render: (record: APIParameter) => (
<span className={getColumnClass(record)}>
{record?.is_required
? I18n.t('bot_edit_page_plugin_tool_param_required')
: I18n.t('bot_edit_page_plugin_tool_param_not_required')}
</span>
),
},
];
if (activeTab === 0) {
res.push(
...[
{
title: I18n.t(
'plugin_edit_tool_default_value_config_item_default_value',
),
key: 'local_default',
className: 'no-wrap',
width: 240,
render: (record: APIParameter) => {
if (record.local_default === undefined) {
return <span className={getColumnClass(record)} />;
}
return (
<span className={getColumnClass(record)}>
<DefaultValueInput
record={record}
data={requestParams}
defaultKey="local_default"
disableKey="local_disable"
canReference={true}
referenceOption={variableOption}
setData={newData => {
doSetReqParams(newData);
}}
/>
</span>
);
},
},
{
title: () => (
<div>
{I18n.t('plugin_edit_tool_default_value_config_item_enable')}
<Tooltip
content={I18n.t(
'plugin_bot_ide_plugin_setting_modal_item_enable_tip',
)}
>
<IconInfo
style={{
color: '#5f5f5f9e',
position: 'relative',
top: 3,
left: 2,
}}
/>
</Tooltip>
</div>
),
key: 'local_disable',
width: 70,
className: 'no-wrap',
render: (record: APIParameter) => {
if (record.local_default === undefined) {
return <span className={getColumnClass(record)} />;
}
const switchNode = (
<Switch
style={{ position: 'relative', top: 3, left: 12 }}
defaultChecked={!record.local_disable}
disabled={
record.is_required &&
!record.local_default &&
!record.variable_ref
}
onChange={e => {
doUpdateNodeWithData({
record,
key: 'local_disable',
value: !e,
});
}}
/>
);
return (
<span className={getColumnClass(record)}>
{record.is_required && !record.local_default ? (
<Tooltip
content={I18n.t(
'plugin_edit_tool_default_value_config_item_enable_disable_tip',
)}
>
{switchNode}
</Tooltip>
) : (
switchNode
)}
</span>
);
},
},
],
);
}
if (activeTab === 1) {
const targetIndex = res.findIndex(c => c.key === 'default');
res.splice(targetIndex, 1);
res.push({
title: () => (
<div>
{I18n.t('plugin_edit_tool_default_value_config_item_enable')}
<Tooltip content={I18n.t('plugin_bot_ide_output_param_enable_tip')}>
<IconInfo
style={{
color: '#5f5f5f9e',
position: 'relative',
top: 3,
left: 2,
}}
/>
</Tooltip>
</div>
),
key: 'local_disable',
width: 70,
className: 'no-wrap',
render: (record: APIParameter) => {
if (record.local_default === undefined) {
return <span className={getColumnClass(record)} />;
}
const switchNode = (
<Switch
style={{ position: 'relative', top: 3, left: 12 }}
defaultChecked={!record.local_disable}
onChange={e => {
doUpdateNodeWithData({
record,
key: 'local_disable',
value: !e,
isForResponse: true,
});
}}
/>
);
return <span className={getColumnClass(record)}>{switchNode}</span>;
},
});
}
return res;
}, [activeTab, requestParams, doUpdateNodeWithData]);
const scroll = useMemo(() => ({ y: (size?.height ?? 0) - GAP }), [size]);
const doSave = useMemoizedFn(async () => {
try {
await doUpdateParams();
Toast.success({ zIndex: 1051, content: I18n.t('Save_success') });
} catch (error) {
// @ts-expect-error -- linter-disable-autofix
logger.error({ error, eventName: 'update_bot_default_params_error' });
Toast.error({
zIndex: 1051,
content: withSlardarIdButton(
I18n.t('card_builder_api_http_delete_error'),
),
});
}
});
return (
<>
{loaded ? (
<div className={classnames(s['params-modal'], 'relative h-full')}>
<div className="flex justify-center">
<RadioGroup
className="flex"
style={{ alignSelf: 'center', width: 420, height: 32 }}
type="button"
buttonSize="middle"
defaultValue={activeTab}
onChange={e => {
doSetActive(e.target.value);
}}
>
<Radio value={0} className="flex-[1]">
{I18n.t(
'plugin_bot_ide_plugin_setting_modal_input_param_title',
)}
</Radio>
<Radio value={1} className="flex-[1]">
{I18n.t(
'plugin_bot_ide_plugin_setting_modal_output_param_title',
)}
</Radio>
</RadioGroup>
</div>
<Table
pagination={false}
columns={columns}
dataSource={activeTab === 0 ? requestParams : responseParams}
rowKey={ROWKEY}
childrenRecordName={childrenRecordName}
expandAllRows={true}
scroll={scroll}
empty={
<div className={s.empty}>
{I18n.t('plugin_form_no_result_desc')}
</div>
}
/>
<Button
loading={isUpdateLoading}
className="absolute right-[0] bottom-[0] h-[32px] !min-w-[98px]"
onClick={doSave}
>
{I18n.t('Save')}
</Button>
</div>
) : (
<Spin spinning style={{ height: '400px', width: '100%' }} />
)}
</>
);
};
export { PartParams };

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
PluginSettingEnter,
type IAgentSkillPluginSettingModalProps,
} from './components/agent-skill-setting-modal';
export { type SettingSlot } from './components/agent-skill-setting-modal/part-main';

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types='@coze-arch/bot-typings' />