feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 };
|
||||
22
frontend/packages/agent-ide/plugin-setting/src/index.ts
Normal file
22
frontend/packages/agent-ide/plugin-setting/src/index.ts
Normal 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';
|
||||
17
frontend/packages/agent-ide/plugin-setting/src/typings.d.ts
vendored
Normal file
17
frontend/packages/agent-ide/plugin-setting/src/typings.d.ts
vendored
Normal 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' />
|
||||
Reference in New Issue
Block a user