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,81 @@
/*
* 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 { useParams, useSearchParams } from 'react-router-dom';
import { useRequest } from 'ahooks';
import { type DynamicParams } from '@coze-arch/bot-typings/teamspace';
import { MemoryApi } from '@coze-arch/bot-api';
/**
* 已经迁移的旧渠道 id 到新渠道 id 的映射
* key(旧) -> value(新)
*/
const migratedConnectorIds: Record<string, string | undefined> = {
// 微信服务号
'10000114': '10000120',
// 微信订阅号
'10000115': '10000121',
};
export interface ConnectorOption {
label: string;
value: string;
/** 该渠道 id 是否已经迁移 */
migrated?: boolean;
}
export interface UseConnectorOptionsParams {
/** 是否包含已迁移的旧渠道,默认不包含 */
includeMigrated?: boolean;
}
export function useConnectorOptions({
includeMigrated = false,
}: UseConnectorOptionsParams = {}): ConnectorOption[] {
const { space_id } = useParams<DynamicParams>();
// 资源库 workflow 页面的 url 上没有 space_id 参数,需要从 searchParams 中获取
const [searchParams] = useSearchParams();
const spaceId = space_id ?? searchParams.get('space_id') ?? '';
const { data } = useRequest(
async () => {
const res = await MemoryApi.GetConnectorName({
SpaceId: spaceId,
Version: IS_RELEASE_VERSION ? 'release' : 'inhouse',
ListAll: true,
});
const connectors = res.ConnectorList;
return connectors?.map(i => {
const value = i.ConnectorID?.toString() ?? '';
if (migratedConnectorIds[value]) {
const target = connectors.find(
j => j.ConnectorID?.toString() === migratedConnectorIds[value],
);
if (target?.ConnectorName) {
return { label: target.ConnectorName, value, migrated: true };
}
}
return { label: i.ConnectorName ?? '', value };
});
},
{
refreshDeps: [spaceId],
// 设置缓存 key, 防止重复请求
cacheKey: `db_connector_name_${spaceId}`,
},
);
return (includeMigrated ? data : data?.filter(c => !c.migrated)) ?? [];
}

View File

@@ -0,0 +1,85 @@
/*
* 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 { useEffect, useState } from 'react';
import { DataNamespace, dataReporter } from '@coze-data/reporter';
import { REPORT_EVENTS } from '@coze-arch/report-events';
import {
type GetModeConfigResponse,
BotTableRWMode,
} from '@coze-arch/bot-api/memory';
import { MemoryApi } from '@coze-arch/bot-api';
export interface ExpertModeConfig {
isExpertMode: boolean;
maxTableNum: number;
maxColumnNum: number;
readAndWriteModes: BotTableRWMode[];
}
export const useExpertModeConfig = (params: {
botId: string;
}): ExpertModeConfig => {
const { botId } = params;
const defaultConfig = {
isExpertMode: false,
maxTableNum: 1,
maxColumnNum: 10,
readAndWriteModes: [BotTableRWMode.LimitedReadWrite],
};
const [expertConfig, setExpertConfig] =
useState<ExpertModeConfig>(defaultConfig);
useEffect(() => {
(async () => {
if (!botId) {
return;
}
let res: GetModeConfigResponse | undefined;
try {
res = await MemoryApi.GetModeConfig({
bot_id: botId,
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 复制历史文件
} catch (error: any) {
dataReporter.errorEvent(DataNamespace.DATABASE, {
eventName: REPORT_EVENTS.DatabaseGetExpertConfig,
error,
});
}
if (res) {
const result: ExpertModeConfig = {
isExpertMode: res.mode === 'expert',
maxColumnNum: Number(res.max_column_num),
maxTableNum: Number(res.max_table_num),
readAndWriteModes:
Number(res.max_table_num) > 1
? [
BotTableRWMode.LimitedReadWrite,
BotTableRWMode.UnlimitedReadWrite,
]
: defaultConfig.readAndWriteModes,
};
setExpertConfig(result);
}
})();
}, [botId]);
return expertConfig;
};

View File

@@ -0,0 +1,27 @@
/*
* 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 } from 'react';
import { type TableData } from '../components/database-table-data/type';
export const useGetTableInstantaneousData = (tableData: TableData) => {
// 缓存 Data 数据,用于在事件中获取数据
const dataRef = useRef<TableData>(tableData);
dataRef.current = tableData;
return () => dataRef.current;
};

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, useMemo } from 'react';
import { userStoreService } from '@coze-studio/user-store';
import { type DatabaseInfo } from '@coze-studio/bot-detail-store';
import { useDataNavigate } from '@coze-data/knowledge-stores';
import { ModalMode } from '@coze-data/database-v2-base/components/base-info-modal';
import { useDatabaseCreateTableModal } from '@coze-data/database-v2-adapter/components/create-table-modal';
import {
useDatabaseInfoModal,
type FormData,
} from '@coze-data/database-v2-adapter/components/base-info-modal';
import { useSpaceStore } from '@coze-arch/bot-studio-store';
import {
BotTableRWMode,
type SingleDatabaseResponse,
} from '@coze-arch/bot-api/memory';
export const enum Step {
BASE_INFO = 0,
CREATE_TABLE = 1,
}
export const useLibraryCreateDatabaseModal = ({
projectID,
onFinish,
}: {
projectID?: string;
onFinish?: (databaseID: string, draftId: string) => void;
enterFrom?: 'library' | 'project';
}) => {
const step = useRef<Step>(Step.BASE_INFO);
const resourceNavigate = useDataNavigate();
const spaceId = useSpaceStore(store => store.getSpaceId());
const userId = userStoreService.useUserInfo()?.user_id_str;
const [databaseBaseInfo, setDatabaseBaseInfo] = useState<FormData>({
name: '',
description: '',
icon_uri: [
{
uri: '',
url: '',
uid: '',
},
],
});
const tableInitData: DatabaseInfo = useMemo(
() => ({
tableId: '',
name: databaseBaseInfo?.name,
desc: databaseBaseInfo?.description,
icon_uri: databaseBaseInfo.icon_uri?.[0]?.uri,
readAndWriteMode: BotTableRWMode.LimitedReadWrite,
tableMemoryList: [],
}),
[databaseBaseInfo],
);
const handleBaseInfoSubmit = (data: FormData) => {
setDatabaseBaseInfo(data);
step.current = Step.CREATE_TABLE;
closeDatabaseInfoModal();
open();
};
const handleCreateTableSubmit = (createRes: SingleDatabaseResponse) => {
const { id, draft_id } = createRes.database_info ?? {};
if (id && draft_id) {
if (onFinish) {
// bot 绑定数据库需要 draft_id ,其他场景一般只需要用 id
onFinish(id, draft_id);
return;
} else {
resourceNavigate.toResource?.('database', id, {
page_modal: 'normal',
from: 'create',
});
close();
}
}
};
// onReturn
const handleCreateTableModalClose = () => {
step.current = Step.BASE_INFO;
open();
closeCreateTableModal();
};
// onClose
const handleCloseCreateTable = () => {
console.log('open');
step.current = Step.BASE_INFO;
open();
};
const close = () => {
closeDatabaseInfoModal();
closeCreateTableModal();
step.current = Step.BASE_INFO;
};
const {
modal: databaseInfoModal,
open: openDatabaseInfoModal,
close: closeDatabaseInfoModal,
} = useDatabaseInfoModal({
onSubmit: handleBaseInfoSubmit,
initValues: databaseBaseInfo,
mode: ModalMode.CREATE,
});
const {
modal: createTableModal,
open: openCreateTableModal,
close: closeCreateTableModal,
} = useDatabaseCreateTableModal({
onClose: handleCloseCreateTable,
onReturn: handleCreateTableModalClose,
onSubmit: handleCreateTableSubmit,
initValue: tableInitData,
showDatabaseBaseInfo: true,
onlyShowDatabaseInfoRWMode: true,
extraParams: {
spaceId,
creatorId: userId,
},
projectID,
});
const open = () => {
if (step.current === Step.BASE_INFO) {
openDatabaseInfoModal();
} else {
openCreateTableModal();
}
};
const modal = (
<>
{databaseInfoModal}
{createTableModal}
</>
);
return {
modal,
open,
close,
};
};

View File

@@ -0,0 +1,45 @@
/*
* 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 { useBlocker } from 'react-router-dom';
import { I18n } from '@coze-arch/i18n';
import { Modal } from '@coze-arch/coze-design';
export const useRouteLeavingGuard = (when: boolean) => {
const blocker = useBlocker(
({ currentLocation, nextLocation }) =>
when && currentLocation.pathname !== nextLocation.pathname,
);
const modal = (
<Modal
title={I18n.t('db2_027')}
visible={blocker.state === 'blocked'}
onOk={() => blocker.proceed?.()}
onCancel={() => blocker.reset?.()}
okText={I18n.t('db2_004')}
cancelText={I18n.t('db2_028')}
closeOnEsc={true}
>
{I18n.t('db2_029')}
</Modal>
);
return {
modal,
};
};

View File

@@ -0,0 +1,40 @@
/*
* 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 { produce } from 'immer';
import {
RowServiceStatus,
type TableData,
} from '../components/database-table-data/type';
export const useTableData = (_tableData: TableData) => {
const [tableData, setTableData] = useState(_tableData);
const filteredTableData = useMemo(
() =>
produce(tableData, draft => {
draft.dataList = draft.dataList.filter(
item => item.status !== RowServiceStatus.Deleted,
);
}),
[tableData],
);
return { tableData: filteredTableData, setTableData };
};