feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
107
frontend/packages/studio/stores/bot-detail/README.md
Normal file
107
frontend/packages/studio/stores/bot-detail/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# @coze-studio/bot-detail-store
|
||||
|
||||
bot detail store
|
||||
|
||||
## Overview
|
||||
|
||||
This package is part of the Coze Studio monorepo and provides state management functionality. It includes hook, store, manager and more.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Add this package to your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@coze-studio/bot-detail-store": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
rush update
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { /* exported functions/components */ } from '@coze-studio/bot-detail-store';
|
||||
|
||||
// Example usage
|
||||
// TODO: Add specific usage examples
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Hook
|
||||
- Store
|
||||
- Manager
|
||||
- Modal
|
||||
|
||||
## API Reference
|
||||
|
||||
### Exports
|
||||
|
||||
- `avatarBackgroundWebSocket`
|
||||
- `useBotDetailIsReadonly`
|
||||
- `TTSInfo,
|
||||
type VariableItem,
|
||||
VariableKeyErrType,
|
||||
type TableMemoryItem,
|
||||
type SuggestQuestionMessage,
|
||||
type BotDetailSkill,
|
||||
type WorkFlowItemType,
|
||||
type DatabaseInfo,
|
||||
type DatabaseList,
|
||||
type KnowledgeConfig,
|
||||
type TagListType,
|
||||
type ExtendOnboardingContent,
|
||||
TimeCapsuleOptionsEnum,`
|
||||
- `updateHeaderStatus`
|
||||
- `initBotDetailStore`
|
||||
- `useBotDetailStoreSet`
|
||||
- `autosaveManager,
|
||||
personaSaveManager,
|
||||
botSkillSaveManager,
|
||||
multiAgentSaveManager,
|
||||
registerMultiAgentConfig,
|
||||
getBotDetailDtoInfo,
|
||||
saveConnectorType,
|
||||
saveDeleteAgents,
|
||||
saveUpdateAgents,
|
||||
saveMultiAgentData,
|
||||
saveFileboxMode,
|
||||
saveTableMemory,
|
||||
saveTTSConfig,
|
||||
saveTimeCapsule,
|
||||
saveDevHooksConfig,
|
||||
updateShortcutSort,
|
||||
updateBotRequest,`
|
||||
- `getBotDetailIsReadonly`
|
||||
- `uniqMemoryList`
|
||||
- `verifyBracesAndToast`
|
||||
|
||||
*And more...*
|
||||
|
||||
For detailed API documentation, please refer to the TypeScript definitions.
|
||||
|
||||
## Development
|
||||
|
||||
This package is built with:
|
||||
|
||||
- TypeScript
|
||||
- Modern JavaScript
|
||||
- Vitest for testing
|
||||
- ESLint for code quality
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is part of the Coze Studio monorepo. Please follow the monorepo contribution guidelines.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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 { TabStatus } from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
vi.stubGlobal('IS_DEV_MODE', false);
|
||||
vi.stubGlobal('IS_OVERSEA', false);
|
||||
// vi.mock('zustand');
|
||||
|
||||
vi.mock('@coze-arch/bot-semi', () => ({
|
||||
Toast: {
|
||||
success: vi.fn(),
|
||||
},
|
||||
UIToast: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
vi.mock('@coze-arch/bot-studio-store', () => ({}));
|
||||
vi.mock('@coze-arch/bot-flags', () => ({
|
||||
getFlags: () =>
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get: () => true,
|
||||
},
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-utils', () => ({
|
||||
safeJSONParse: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/i18n', () => ({
|
||||
I18n: { t: vi.fn() },
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-error', () => ({
|
||||
CustomError: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
reporter: {
|
||||
errorEvent: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-studio-store', () => ({
|
||||
useSpaceStore: {
|
||||
getState: () => ({
|
||||
space: {
|
||||
id: 'fake space id',
|
||||
},
|
||||
getSpaceId: () => 'fake space id',
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-space-api', () => ({
|
||||
SpaceApi: {
|
||||
CreateChatflowAgent: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ agentData: {} })
|
||||
.mockResolvedValueOnce({
|
||||
agentData: {
|
||||
agentInfo: {
|
||||
id: 'fake agent ID',
|
||||
},
|
||||
branch: 'fake branch',
|
||||
// same_with_online: true,
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ agentData: {} })
|
||||
.mockResolvedValueOnce({
|
||||
agentData: {
|
||||
agentInfo: {
|
||||
id: 'fake agent ID',
|
||||
},
|
||||
branch: 'fake branch',
|
||||
same_with_online: true,
|
||||
},
|
||||
}),
|
||||
|
||||
CopyChatflowAgent: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ agentData: {} })
|
||||
.mockResolvedValueOnce({
|
||||
agentData: {
|
||||
agentInfo: {
|
||||
id: 'fake agent ID',
|
||||
},
|
||||
branch: 'fake branch',
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ agentData: {} })
|
||||
.mockResolvedValueOnce({
|
||||
agentData: {
|
||||
agentInfo: {
|
||||
id: 'fake agent ID',
|
||||
},
|
||||
branch: 'fake branch',
|
||||
same_with_online: true,
|
||||
},
|
||||
}),
|
||||
GetDraftBotDisplayInfo: vi
|
||||
.fn()
|
||||
.mockRejectedValueOnce('error')
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ data: {} })
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
tab_display_info: {
|
||||
plugin_tab_status: TabStatus.Close,
|
||||
workflow_tab_status: TabStatus.Open,
|
||||
knowledge_tab_status: TabStatus.Default,
|
||||
},
|
||||
},
|
||||
}),
|
||||
UpdateDraftBotDisplayInfo: vi.fn().mockResolvedValueOnce({ code: 0 }),
|
||||
},
|
||||
}));
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
DeveloperApi: {
|
||||
UpdateDraftBotDisplayInfo: vi
|
||||
.fn()
|
||||
.mockResolvedValue({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {},
|
||||
})
|
||||
.mockResolvedValue({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {},
|
||||
})
|
||||
.mockResolvedValue({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {},
|
||||
})
|
||||
.mockResolvedValue({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {},
|
||||
})
|
||||
.mockResolvedValue({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {},
|
||||
})
|
||||
.mockResolvedValue({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: {},
|
||||
}),
|
||||
},
|
||||
PlaygroundApi: {
|
||||
BatchCreateAgent: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ code: 0, data: [{ agent_id: 'fake' }] })
|
||||
.mockResolvedValueOnce({
|
||||
code: 0,
|
||||
data: [{ agent_id: 'fake', reference_id: 'fake reference id' }],
|
||||
same_with_online: false,
|
||||
branch: 2,
|
||||
}),
|
||||
BatchCreateAgentV2: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ code: 0, data: [{ agent_id: 'fake' }] })
|
||||
.mockResolvedValueOnce({
|
||||
code: 0,
|
||||
data: [{ agent_id: 'fake', reference_id: 'fake reference id' }],
|
||||
same_with_online: false,
|
||||
branch: 2,
|
||||
}),
|
||||
CreateAgentV2: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({
|
||||
code: 0,
|
||||
same_with_online: true,
|
||||
branch: 'fake branch',
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
code: 0,
|
||||
same_with_online: true,
|
||||
branch: 'fake branch',
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
code: 0,
|
||||
same_with_online: true,
|
||||
branch: 'fake branch',
|
||||
data: {
|
||||
agent_id: 'fake agent id',
|
||||
},
|
||||
}),
|
||||
CopyAgentV2: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ data: null })
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
agent_id: 'fake agent ID',
|
||||
},
|
||||
branch: 'fake branch',
|
||||
}),
|
||||
|
||||
MGetBotByVersion: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({
|
||||
data: [
|
||||
{
|
||||
id: 'fake reference id',
|
||||
name: 'fake name',
|
||||
icon_url: 'fake icon url',
|
||||
},
|
||||
],
|
||||
})
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({ code: 1 })
|
||||
.mockResolvedValueOnce({
|
||||
data: [
|
||||
{
|
||||
id: 'fake reference id',
|
||||
name: 'fake name',
|
||||
icon_url: 'fake icon url',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-studio/bot-utils', () => ({
|
||||
withSlardarIdButton: vi.fn(),
|
||||
}));
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 { renderHook } from '@testing-library/react';
|
||||
import { BotPageFromEnum } from '@coze-arch/bot-typings/common';
|
||||
|
||||
import { usePageRuntimeStore } from '../../src/store/page-runtime';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import {
|
||||
useCollaborationStore,
|
||||
EditLockStatus,
|
||||
} from '../../src/store/collaboration';
|
||||
import { useBotDetailIsReadonly } from '../../src/hooks/use-bot-detail-readonly';
|
||||
|
||||
describe('useBotDetailIsReadonly', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('useBotDetailIsReadonly', () => {
|
||||
const pageRuntime = {
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
pageFrom: BotPageFromEnum.Bot,
|
||||
};
|
||||
const collaboration = {
|
||||
editLockStatus: EditLockStatus.Offline,
|
||||
};
|
||||
useCollaborationStore.getState().setCollaboration(collaboration);
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setPageRuntimeBotInfo({ ...pageRuntime, editable: false });
|
||||
const { result: r1 } = renderHook(() => useBotDetailIsReadonly());
|
||||
expect(r1.current).toBeTruthy();
|
||||
usePageRuntimeStore.getState().clear();
|
||||
useCollaborationStore.getState().clear();
|
||||
|
||||
useCollaborationStore.getState().setCollaboration(collaboration);
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setPageRuntimeBotInfo({ ...pageRuntime, isPreview: true });
|
||||
const { result: r2 } = renderHook(() => useBotDetailIsReadonly());
|
||||
expect(r2.current).toBeTruthy();
|
||||
usePageRuntimeStore.getState().clear();
|
||||
useCollaborationStore.getState().clear();
|
||||
|
||||
useCollaborationStore.getState().setCollaboration({
|
||||
...collaboration,
|
||||
editLockStatus: EditLockStatus.Lose,
|
||||
});
|
||||
usePageRuntimeStore.getState().setPageRuntimeBotInfo(pageRuntime);
|
||||
const { result: r3 } = renderHook(() => useBotDetailIsReadonly());
|
||||
expect(r3.current).toBeTruthy();
|
||||
usePageRuntimeStore.getState().clear();
|
||||
useCollaborationStore.getState().clear();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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 {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
vi,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { createReportEvent } from '@coze-arch/report-events';
|
||||
import { type BotMonetizationConfigData } from '@coze-arch/idl/benefit';
|
||||
import { type GetDraftBotInfoAgwData } from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
import { getBotDataService } from '../../src/services/get-bot-data-service';
|
||||
import { initBotDetailStore } from '../../src/init/init-bot-detail-store';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@coze-arch/report-events', () => ({
|
||||
REPORT_EVENTS: {
|
||||
botDebugGetRecord: 'botDebugGetRecord',
|
||||
botGetDraftBotInfo: 'botGetDraftBotInfo',
|
||||
},
|
||||
createReportEvent: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../src/services/get-bot-data-service');
|
||||
|
||||
const mockBotInfoStore = {
|
||||
botId: 'test-bot-id',
|
||||
version: '1.0',
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockPageRuntimeStore = {
|
||||
setPageRuntimeBotInfo: vi.fn(),
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockBotDetailStoreSet = {
|
||||
clear: vi.fn(),
|
||||
};
|
||||
const mockCollaborationStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockPersonaStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockModelStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockBotSkillStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockMultiAgentStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockMonetizeConfigStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockQueryCollectStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
const mockAuditInfoStore = {
|
||||
initStore: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock('../src/store/audit-info', () => ({
|
||||
useAuditInfoStore: {
|
||||
getState: vi.fn(() => mockAuditInfoStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/query-collect', () => ({
|
||||
useQueryCollectStore: {
|
||||
getState: vi.fn(() => mockQueryCollectStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/persona', () => ({
|
||||
usePersonaStore: {
|
||||
getState: vi.fn(() => mockPersonaStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/page-runtime', () => ({
|
||||
usePageRuntimeStore: {
|
||||
getState: vi.fn(() => mockPageRuntimeStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/multi-agent', () => ({
|
||||
useMultiAgentStore: {
|
||||
getState: vi.fn(() => mockMultiAgentStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/monetize-config-store', () => ({
|
||||
useMonetizeConfigStore: {
|
||||
getState: vi.fn(() => mockMonetizeConfigStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/model', () => ({
|
||||
useModelStore: {
|
||||
getState: vi.fn(() => mockModelStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/index', () => ({
|
||||
useBotDetailStoreSet: mockBotDetailStoreSet,
|
||||
}));
|
||||
vi.mock('../src/store/collaboration', () => ({
|
||||
useCollaborationStore: {
|
||||
getState: vi.fn(() => mockCollaborationStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(() => mockBotSkillStore),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/store/bot-info', () => ({
|
||||
useBotInfoStore: {
|
||||
getState: vi.fn(() => mockBotInfoStore),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('initBotDetailStore', () => {
|
||||
const mockSuccessReportEvent = { success: vi.fn(), error: vi.fn() };
|
||||
const mockErrorGetBotInfoReportEvent = { success: vi.fn(), error: vi.fn() };
|
||||
|
||||
const mockBotData: GetDraftBotInfoAgwData = {
|
||||
bot_info: { bot_id: 'test-bot-id', name: 'Test Bot' },
|
||||
// Add other necessary fields for GetDraftBotInfoAgwData
|
||||
} as GetDraftBotInfoAgwData; // Cast to avoid filling all fields for test
|
||||
const mockMonetizeConfig: BotMonetizationConfigData = {
|
||||
// Add necessary fields for BotMonetizationConfigData
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
(createReportEvent as Mock)
|
||||
.mockReturnValueOnce(mockSuccessReportEvent) // For botDebugGetRecord
|
||||
.mockReturnValueOnce(mockErrorGetBotInfoReportEvent); // For botGetDraftBotInfo
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should initialize stores correctly for "bot" scene with version', async () => {
|
||||
(getBotDataService as Mock).mockResolvedValue({
|
||||
botData: mockBotData,
|
||||
monetizeConfig: mockMonetizeConfig,
|
||||
});
|
||||
|
||||
const params = { version: '2.0', scene: 'bot' as const };
|
||||
await initBotDetailStore(params);
|
||||
|
||||
expect(mockSuccessReportEvent.success).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize stores correctly for "market" scene without version', async () => {
|
||||
(getBotDataService as Mock).mockResolvedValue({
|
||||
botData: mockBotData,
|
||||
monetizeConfig: mockMonetizeConfig,
|
||||
});
|
||||
|
||||
const params = { scene: 'market' as const };
|
||||
await initBotDetailStore(params);
|
||||
|
||||
expect(mockErrorGetBotInfoReportEvent.success).toHaveBeenCalled();
|
||||
expect(mockSuccessReportEvent.success).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle errors from getBotDataService', async () => {
|
||||
const error = new Error('Failed to fetch bot data');
|
||||
(getBotDataService as Mock).mockRejectedValue(error);
|
||||
|
||||
await expect(initBotDetailStore()).rejects.toThrow(error);
|
||||
|
||||
expect(getBotDataService).toHaveBeenCalled();
|
||||
expect(mockErrorGetBotInfoReportEvent.error).toHaveBeenCalledWith({
|
||||
reason: 'get new draft bot info fail',
|
||||
error,
|
||||
});
|
||||
expect(mockSuccessReportEvent.error).toHaveBeenCalledWith({
|
||||
reason: 'init fail',
|
||||
error,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use default scene "bot" if not provided', async () => {
|
||||
(getBotDataService as Mock).mockResolvedValue({
|
||||
botData: mockBotData,
|
||||
monetizeConfig: mockMonetizeConfig,
|
||||
});
|
||||
await initBotDetailStore({}); // Empty params
|
||||
|
||||
expect(getBotDataService).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
scene: 'bot',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemTypeExtra } from '../../../../../src/save-manager/types';
|
||||
import { chatBackgroundConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/chat-background';
|
||||
|
||||
describe('chatBackgroundConfig', () => {
|
||||
it('应该具有正确的配置属性', () => {
|
||||
// 验证配置的基本属性
|
||||
expect(chatBackgroundConfig).toHaveProperty('key');
|
||||
expect(chatBackgroundConfig).toHaveProperty('selector');
|
||||
expect(chatBackgroundConfig).toHaveProperty('debounce');
|
||||
expect(chatBackgroundConfig).toHaveProperty('middleware');
|
||||
|
||||
// 验证 middleware 存在且有 onBeforeSave 属性
|
||||
expect(chatBackgroundConfig.middleware).toBeDefined();
|
||||
if (chatBackgroundConfig.middleware) {
|
||||
expect(chatBackgroundConfig.middleware).toHaveProperty('onBeforeSave');
|
||||
}
|
||||
|
||||
// 验证属性值
|
||||
expect(chatBackgroundConfig.key).toBe(ItemTypeExtra.ChatBackGround);
|
||||
expect(chatBackgroundConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
expect(typeof chatBackgroundConfig.selector).toBe('function');
|
||||
|
||||
// 验证 onBeforeSave 是函数
|
||||
if (
|
||||
chatBackgroundConfig.middleware &&
|
||||
chatBackgroundConfig.middleware.onBeforeSave
|
||||
) {
|
||||
expect(typeof chatBackgroundConfig.middleware.onBeforeSave).toBe(
|
||||
'function',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('selector 应该返回 store 的 backgroundImageInfoList 属性', () => {
|
||||
// 创建模拟 store
|
||||
const mockStore = {
|
||||
backgroundImageInfoList: [
|
||||
{ id: 'bg1', url: 'http://example.com/bg1.jpg' },
|
||||
],
|
||||
};
|
||||
|
||||
// 调用 selector 函数
|
||||
// 注意:这里我们假设 selector 是一个函数,如果它是一个复杂对象,可能需要调整测试
|
||||
const { selector } = chatBackgroundConfig;
|
||||
let result;
|
||||
|
||||
if (typeof selector === 'function') {
|
||||
result = selector(mockStore as any);
|
||||
// 验证结果
|
||||
expect(result).toBe(mockStore.backgroundImageInfoList);
|
||||
} else {
|
||||
// 如果 selector 不是函数,跳过这个测试
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('middleware.onBeforeSave 应该正确转换数据', () => {
|
||||
// 创建模拟数据
|
||||
const mockData = [
|
||||
{ id: 'bg1', url: 'http://example.com/bg1.jpg' },
|
||||
{ id: 'bg2', url: 'http://example.com/bg2.jpg' },
|
||||
];
|
||||
|
||||
// 确保 middleware 和 onBeforeSave 存在
|
||||
if (
|
||||
chatBackgroundConfig.middleware &&
|
||||
chatBackgroundConfig.middleware.onBeforeSave
|
||||
) {
|
||||
// 调用 onBeforeSave 函数
|
||||
const result = chatBackgroundConfig.middleware.onBeforeSave(mockData);
|
||||
|
||||
// 验证结果
|
||||
expect(result).toEqual({
|
||||
background_image_info_list: mockData,
|
||||
});
|
||||
} else {
|
||||
// 如果 middleware 或 onBeforeSave 不存在,跳过这个测试
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import { workflowsConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/workflows';
|
||||
import { voicesInfoConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/voices-info';
|
||||
import { variablesConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/variables';
|
||||
import { taskInfoConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/task-info';
|
||||
import { suggestionConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/suggestion-config';
|
||||
import { pluginConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/plugin';
|
||||
import { onboardingConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/onboarding-content';
|
||||
import { layoutInfoConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/layout-info';
|
||||
import { knowledgeConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/knowledge';
|
||||
import { chatBackgroundConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/chat-background';
|
||||
import { registers } from '../../../../../src/save-manager/auto-save/bot-skill/configs';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/workflows',
|
||||
() => ({
|
||||
workflowsConfig: { key: 'workflows', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/voices-info',
|
||||
() => ({
|
||||
voicesInfoConfig: { key: 'voicesInfo', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/variables',
|
||||
() => ({
|
||||
variablesConfig: { key: 'variables', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/task-info',
|
||||
() => ({
|
||||
taskInfoConfig: { key: 'taskInfo', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/suggestion-config',
|
||||
() => ({
|
||||
suggestionConfig: { key: 'suggestionConfig', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/plugin',
|
||||
() => ({
|
||||
pluginConfig: { key: 'plugin', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/onboarding-content',
|
||||
() => ({
|
||||
onboardingConfig: { key: 'onboarding', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/layout-info',
|
||||
() => ({
|
||||
layoutInfoConfig: { key: 'layoutInfo', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/knowledge',
|
||||
() => ({
|
||||
knowledgeConfig: { key: 'knowledge', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/chat-background',
|
||||
() => ({
|
||||
chatBackgroundConfig: { key: 'chatBackground', selector: vi.fn() },
|
||||
}),
|
||||
);
|
||||
|
||||
describe('bot-skill configs', () => {
|
||||
it('应该正确注册所有配置', () => {
|
||||
// 验证 registers 数组包含所有配置
|
||||
expect(registers).toContain(pluginConfig);
|
||||
expect(registers).toContain(chatBackgroundConfig);
|
||||
expect(registers).toContain(onboardingConfig);
|
||||
expect(registers).toContain(knowledgeConfig);
|
||||
expect(registers).toContain(layoutInfoConfig);
|
||||
expect(registers).toContain(suggestionConfig);
|
||||
expect(registers).toContain(taskInfoConfig);
|
||||
expect(registers).toContain(variablesConfig);
|
||||
expect(registers).toContain(workflowsConfig);
|
||||
expect(registers).toContain(voicesInfoConfig);
|
||||
|
||||
// 验证 registers 数组长度
|
||||
expect(registers.length).toBe(10);
|
||||
});
|
||||
|
||||
it('每个配置都应该有 key 和 selector 属性', () => {
|
||||
registers.forEach(config => {
|
||||
expect(config).toHaveProperty('key');
|
||||
expect(config).toHaveProperty('selector');
|
||||
expect(typeof config.key).toBe('string');
|
||||
expect(typeof config.selector).toBe('function');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { knowledgeConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/knowledge';
|
||||
|
||||
describe('knowledgeConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(knowledgeConfig).toHaveProperty('key');
|
||||
expect(knowledgeConfig).toHaveProperty('selector');
|
||||
expect(knowledgeConfig).toHaveProperty('debounce');
|
||||
expect(knowledgeConfig).toHaveProperty('middleware');
|
||||
expect(knowledgeConfig.key).toBe(ItemType.DataSet);
|
||||
// 验证 debounce 配置
|
||||
if (typeof knowledgeConfig.debounce === 'object') {
|
||||
expect(knowledgeConfig.debounce).toHaveProperty('default');
|
||||
expect(knowledgeConfig.debounce).toHaveProperty('dataSetInfo.min_score');
|
||||
expect(knowledgeConfig.debounce).toHaveProperty('dataSetInfo.top_k');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemTypeExtra } from '../../../../../src/save-manager/types';
|
||||
import { layoutInfoConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/layout-info';
|
||||
|
||||
describe('layoutInfoConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(layoutInfoConfig).toHaveProperty('key');
|
||||
expect(layoutInfoConfig).toHaveProperty('selector');
|
||||
expect(layoutInfoConfig).toHaveProperty('debounce');
|
||||
expect(layoutInfoConfig).toHaveProperty('middleware');
|
||||
expect(layoutInfoConfig.key).toBe(ItemTypeExtra.LayoutInfo);
|
||||
expect(layoutInfoConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { onboardingConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/onboarding-content';
|
||||
|
||||
describe('onboardingConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(onboardingConfig).toHaveProperty('key');
|
||||
expect(onboardingConfig).toHaveProperty('selector');
|
||||
expect(onboardingConfig).toHaveProperty('debounce');
|
||||
expect(onboardingConfig).toHaveProperty('middleware');
|
||||
expect(onboardingConfig.key).toBe(ItemType.ONBOARDING);
|
||||
// 验证 debounce 配置
|
||||
if (typeof onboardingConfig.debounce === 'object') {
|
||||
expect(onboardingConfig.debounce).toHaveProperty('default');
|
||||
expect(onboardingConfig.debounce).toHaveProperty('prologue');
|
||||
expect(onboardingConfig.debounce).toHaveProperty('suggested_questions');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { pluginConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/plugin';
|
||||
|
||||
describe('pluginConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(pluginConfig).toHaveProperty('key');
|
||||
expect(pluginConfig).toHaveProperty('selector');
|
||||
expect(pluginConfig).toHaveProperty('debounce');
|
||||
expect(pluginConfig).toHaveProperty('middleware');
|
||||
expect(pluginConfig.key).toBe(ItemType.APIINFO);
|
||||
expect(pluginConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { suggestionConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/suggestion-config';
|
||||
|
||||
describe('suggestionConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(suggestionConfig).toHaveProperty('key');
|
||||
expect(suggestionConfig).toHaveProperty('selector');
|
||||
expect(suggestionConfig).toHaveProperty('debounce');
|
||||
expect(suggestionConfig).toHaveProperty('middleware');
|
||||
expect(suggestionConfig.key).toBe(ItemType.SUGGESTREPLY);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { taskInfoConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/task-info';
|
||||
|
||||
describe('taskInfoConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(taskInfoConfig).toHaveProperty('key');
|
||||
expect(taskInfoConfig).toHaveProperty('selector');
|
||||
expect(taskInfoConfig).toHaveProperty('debounce');
|
||||
expect(taskInfoConfig).toHaveProperty('middleware');
|
||||
expect(taskInfoConfig.key).toBe(ItemType.TASK);
|
||||
expect(taskInfoConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { variablesConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/variables';
|
||||
|
||||
describe('variablesConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(variablesConfig).toHaveProperty('key');
|
||||
expect(variablesConfig).toHaveProperty('selector');
|
||||
expect(variablesConfig).toHaveProperty('debounce');
|
||||
expect(variablesConfig).toHaveProperty('middleware');
|
||||
expect(variablesConfig.key).toBe(ItemType.PROFILEMEMORY);
|
||||
expect(variablesConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { voicesInfoConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/voices-info';
|
||||
|
||||
describe('voicesInfoConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(voicesInfoConfig).toHaveProperty('key');
|
||||
expect(voicesInfoConfig).toHaveProperty('selector');
|
||||
expect(voicesInfoConfig).toHaveProperty('debounce');
|
||||
expect(voicesInfoConfig).toHaveProperty('middleware');
|
||||
expect(voicesInfoConfig.key).toBe(ItemType.PROFILEMEMORY);
|
||||
expect(voicesInfoConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemType } from '../../../../../src/save-manager/types';
|
||||
import { workflowsConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/workflows';
|
||||
|
||||
describe('workflowsConfig', () => {
|
||||
it('should have correct configuration properties', () => {
|
||||
expect(workflowsConfig).toHaveProperty('key');
|
||||
expect(workflowsConfig).toHaveProperty('selector');
|
||||
expect(workflowsConfig).toHaveProperty('debounce');
|
||||
expect(workflowsConfig).toHaveProperty('middleware');
|
||||
expect(workflowsConfig.key).toBe(ItemType.WORKFLOW);
|
||||
expect(workflowsConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import { botSkillSaveManager } from '../../../../src/save-manager/auto-save/bot-skill';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('@coze-studio/autosave', () => {
|
||||
const mockStartFn = vi.fn();
|
||||
const mockCloseFn = vi.fn();
|
||||
|
||||
return {
|
||||
AutosaveManager: vi.fn().mockImplementation(() => ({
|
||||
start: mockStartFn,
|
||||
close: mockCloseFn,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {},
|
||||
}));
|
||||
|
||||
vi.mock('../../../../src/save-manager/auto-save/request', () => ({
|
||||
saveRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../../../src/save-manager/auto-save/bot-skill/configs', () => ({
|
||||
registers: [
|
||||
{ key: 'plugin', selector: vi.fn() },
|
||||
{ key: 'knowledge', selector: vi.fn() },
|
||||
],
|
||||
}));
|
||||
|
||||
describe('botSkillSaveManager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该是 AutosaveManager 的实例', () => {
|
||||
// 验证 botSkillSaveManager 是 AutosaveManager 的实例
|
||||
expect(botSkillSaveManager).toBeDefined();
|
||||
// 由于我们模拟了 AutosaveManager,我们不能直接检查实例类型
|
||||
// 但可以检查它是否具有 AutosaveManager 实例应有的属性和方法
|
||||
expect(botSkillSaveManager).toHaveProperty('start');
|
||||
expect(botSkillSaveManager).toHaveProperty('close');
|
||||
});
|
||||
|
||||
it('应该具有 start 和 close 方法', () => {
|
||||
// 验证 botSkillSaveManager 具有 start 和 close 方法
|
||||
expect(botSkillSaveManager.start).toBeDefined();
|
||||
expect(botSkillSaveManager.close).toBeDefined();
|
||||
expect(typeof botSkillSaveManager.start).toBe('function');
|
||||
expect(typeof botSkillSaveManager.close).toBe('function');
|
||||
});
|
||||
|
||||
it('调用 start 方法应该正常工作', () => {
|
||||
// 调用 start 方法
|
||||
botSkillSaveManager.start();
|
||||
// 由于我们已经模拟了 start 方法,这里只需验证它可以被调用而不会抛出错误
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('调用 close 方法应该正常工作', () => {
|
||||
// 调用 close 方法
|
||||
botSkillSaveManager.close();
|
||||
// 由于我们已经模拟了 close 方法,这里只需验证它可以被调用而不会抛出错误
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
import { personaSaveManager } from '../../../src/save-manager/auto-save/persona';
|
||||
import { modelSaveManager } from '../../../src/save-manager/auto-save/model';
|
||||
import { autosaveManager } from '../../../src/save-manager/auto-save/index';
|
||||
import { botSkillSaveManager } from '../../../src/save-manager/auto-save/bot-skill';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../../src/save-manager/auto-save/persona', () => ({
|
||||
personaSaveManager: {
|
||||
start: vi.fn(),
|
||||
close: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/auto-save/model', () => ({
|
||||
modelSaveManager: {
|
||||
start: vi.fn(),
|
||||
close: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/auto-save/bot-skill', () => ({
|
||||
botSkillSaveManager: {
|
||||
start: vi.fn(),
|
||||
close: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('autosave manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// 正确模拟 console.log
|
||||
vi.spyOn(console, 'log').mockImplementation(() => {
|
||||
// 什么都不做
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// 恢复原始的 console.log
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('应该在启动时调用所有管理器的 start 方法', () => {
|
||||
autosaveManager.start();
|
||||
|
||||
// 验证 console.log 被调用
|
||||
expect(console.log).toHaveBeenCalledWith('start:>>');
|
||||
|
||||
// 验证所有管理器的 start 方法被调用
|
||||
expect(personaSaveManager.start).toHaveBeenCalledTimes(1);
|
||||
expect(botSkillSaveManager.start).toHaveBeenCalledTimes(1);
|
||||
expect(modelSaveManager.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('应该在关闭时调用所有管理器的 close 方法', () => {
|
||||
autosaveManager.close();
|
||||
|
||||
// 验证 console.log 被调用
|
||||
expect(console.log).toHaveBeenCalledWith('close:>>');
|
||||
|
||||
// 验证所有管理器的 close 方法被调用
|
||||
expect(personaSaveManager.close).toHaveBeenCalledTimes(1);
|
||||
expect(botSkillSaveManager.close).toHaveBeenCalledTimes(1);
|
||||
expect(modelSaveManager.close).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { DebounceTime } from '@coze-studio/autosave';
|
||||
|
||||
import { useModelStore } from '../../../../src/store/model';
|
||||
import { ItemType } from '../../../../src/save-manager/types';
|
||||
import { modelConfig } from '../../../../src/save-manager/auto-save/model/config';
|
||||
|
||||
// Mock the useModelStore
|
||||
vi.mock('@/store/model', () => ({
|
||||
useModelStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('modelConfig', () => {
|
||||
it('should have correct static configuration properties', () => {
|
||||
expect(modelConfig.key).toBe(ItemType.OTHERINFO);
|
||||
expect(typeof modelConfig.selector).toBe('function');
|
||||
// Example selector call
|
||||
const mockStore = { config: { model: 'test-model' } };
|
||||
// @ts-expect-error -- Mocking the store
|
||||
expect(modelConfig.selector(mockStore as any)).toEqual({
|
||||
model: 'test-model',
|
||||
});
|
||||
|
||||
expect(modelConfig.debounce).toEqual({
|
||||
default: DebounceTime.Immediate,
|
||||
temperature: DebounceTime.Medium,
|
||||
max_tokens: DebounceTime.Medium,
|
||||
'ShortMemPolicy.HistoryRound': DebounceTime.Medium,
|
||||
});
|
||||
expect(modelConfig.middleware).toBeDefined();
|
||||
expect(typeof modelConfig.middleware?.onBeforeSave).toBe('function');
|
||||
});
|
||||
|
||||
it('middleware.onBeforeSave should call transformVo2Dto and return correct structure', () => {
|
||||
const mockDataSource = { model: 'gpt-4', temperature: 0.7 };
|
||||
const mockTransformedDto = { model_id: 'gpt-4', temperature: 0.7 };
|
||||
const mockTransformVo2Dto = vi.fn().mockReturnValue(mockTransformedDto);
|
||||
|
||||
(useModelStore.getState as Mock).mockReturnValue({
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
|
||||
const result = modelConfig.middleware?.onBeforeSave?.(
|
||||
mockDataSource as any,
|
||||
);
|
||||
|
||||
expect(useModelStore.getState).toHaveBeenCalled();
|
||||
expect(mockTransformVo2Dto).toHaveBeenCalledWith(mockDataSource);
|
||||
expect(result).toEqual({
|
||||
model_info: mockTransformedDto,
|
||||
});
|
||||
});
|
||||
|
||||
it('selector should return the config part of the store', () => {
|
||||
const mockState = {
|
||||
config: { model: 'test-model', temperature: 0.5 },
|
||||
anotherProperty: 'test',
|
||||
};
|
||||
// @ts-expect-error -- Mocking the store
|
||||
expect(modelConfig.selector(mockState as any)).toEqual(mockState.config);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { useBotInfoStore } from '../../../src/store/bot-info';
|
||||
import { saveFetcher } from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveRequest } from '../../../src/save-manager/auto-save/request';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
PlaygroundApi: {
|
||||
UpdateDraftBotInfoAgw: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-info', () => ({
|
||||
useBotInfoStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/utils/storage', () => ({
|
||||
storage: {
|
||||
baseVersion: 'mock-base-version',
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('auto-save request', () => {
|
||||
const mockBotId = 'mock-bot-id';
|
||||
const mockPayload = { some_field: 'some_value' };
|
||||
const mockItemType = ItemType.TABLE;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
botId: mockBotId,
|
||||
});
|
||||
|
||||
(PlaygroundApi.UpdateDraftBotInfoAgw as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该使用正确的参数调用 saveFetcher', async () => {
|
||||
await saveRequest(mockPayload, mockItemType);
|
||||
|
||||
// 验证 saveFetcher 被调用
|
||||
expect(saveFetcher).toHaveBeenCalledTimes(1);
|
||||
// 验证 saveFetcher 的第二个参数是正确的 itemType
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
mockItemType,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateDraftBotInfoAgw 被调用,并且参数正确
|
||||
expect(PlaygroundApi.UpdateDraftBotInfoAgw).toHaveBeenCalledWith({
|
||||
bot_info: {
|
||||
bot_id: mockBotId,
|
||||
...mockPayload,
|
||||
},
|
||||
base_commit_version: 'mock-base-version',
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
await expect(saveRequest(mockPayload, mockItemType)).rejects.toThrow(
|
||||
mockError,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { type HookInfo } from '@coze-arch/idl/playground_api';
|
||||
import { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveDevHooksConfig } from '../../../src/save-manager/manual-save/dev-hooks';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
updateBotRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('dev-hooks save manager', () => {
|
||||
const mockDevHooks = {
|
||||
hooks: [{ id: 'hook-1', name: 'Test Hook', enabled: true }],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
devHooks: mockDevHooks,
|
||||
});
|
||||
|
||||
(updateBotRequest as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该正确保存 dev hooks 配置', async () => {
|
||||
const newConfig = {
|
||||
hooks: [{ id: 'hook-1', name: 'Updated Hook', enabled: false }],
|
||||
} as any as HookInfo;
|
||||
await saveDevHooksConfig(newConfig);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
hook_info: newConfig,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemType.HOOKINFO,
|
||||
);
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
const newConfig = {
|
||||
hooks: [{ id: 'hook-1', name: 'Updated Hook', enabled: false }],
|
||||
} as any as HookInfo;
|
||||
|
||||
await expect(saveDevHooksConfig(newConfig)).rejects.toThrow(mockError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveFileboxMode } from '../../../src/save-manager/manual-save/filebox';
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn((fn, itemType) => fn()),
|
||||
updateBotRequest: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('filebox save manager', () => {
|
||||
const mockFilebox = {
|
||||
mode: 'read',
|
||||
files: [{ id: 'file-1', name: 'test.txt' }],
|
||||
};
|
||||
|
||||
const mockTransformVo2Dto = {
|
||||
filebox: vi.fn(filebox => filebox),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
filebox: mockFilebox,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should correctly save filebox mode', async () => {
|
||||
const newMode = 'read';
|
||||
await saveFileboxMode(newMode as any);
|
||||
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemType.TABLE,
|
||||
);
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
filebox_info: mockFilebox,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle errors thrown by saveFetcher', async () => {
|
||||
(saveFetcher as any).mockImplementation(() => Promise.resolve());
|
||||
|
||||
await saveFileboxMode('read' as any);
|
||||
expect(saveFetcher).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveTableMemory } from '../../../src/save-manager/manual-save/memory-table';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
updateBotRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('memory-table save manager', () => {
|
||||
const mockDatabaseList = [
|
||||
{ id: 'db1', name: 'Database 1' },
|
||||
{ id: 'db2', name: 'Database 2' },
|
||||
];
|
||||
|
||||
const mockTransformVo2Dto = {
|
||||
databaseList: vi.fn(databaseList => ({ transformed: databaseList })),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
databaseList: mockDatabaseList,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
|
||||
(updateBotRequest as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该正确保存内存表变量', async () => {
|
||||
await saveTableMemory();
|
||||
|
||||
// 验证 transformVo2Dto.databaseList 被调用
|
||||
expect(mockTransformVo2Dto.databaseList).toHaveBeenCalledWith(
|
||||
mockDatabaseList,
|
||||
);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
database_list: { transformed: mockDatabaseList },
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemType.TABLE,
|
||||
);
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
await expect(saveTableMemory()).rejects.toThrow(mockError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { useMultiAgentStore } from '../../../src/store/multi-agent';
|
||||
import { useBotInfoStore } from '../../../src/store/bot-info';
|
||||
import { saveFetcher } from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import {
|
||||
saveUpdateAgents,
|
||||
saveDeleteAgents,
|
||||
saveMultiAgentData,
|
||||
saveConnectorType,
|
||||
} from '../../../src/save-manager/manual-save/multi-agent';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
PlaygroundApi: {
|
||||
UpdateAgentV2: vi.fn(),
|
||||
UpdateMultiAgent: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-studio-store', () => ({
|
||||
useSpaceStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/multi-agent', () => ({
|
||||
useMultiAgentStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-info', () => ({
|
||||
useBotInfoStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/utils/storage', () => ({
|
||||
storage: {
|
||||
baseVersion: 'mock-base-version',
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('multi-agent save manager', () => {
|
||||
const mockBotId = 'mock-bot-id';
|
||||
const mockSpaceId = 'mock-space-id';
|
||||
// 创建一个符合 Agent 类型的模拟对象
|
||||
const mockAgent = {
|
||||
id: 'agent-1',
|
||||
name: 'Agent 1',
|
||||
description: 'Test agent',
|
||||
prompt: 'Test prompt',
|
||||
model: { model_name: 'gpt-4' },
|
||||
skills: {
|
||||
knowledge: [],
|
||||
pluginApis: [],
|
||||
workflows: [],
|
||||
devHooks: {},
|
||||
},
|
||||
system_info_all: [],
|
||||
bizInfo: { id: 'biz-1' },
|
||||
jump_config: { enabled: false },
|
||||
suggestion: { enabled: false },
|
||||
};
|
||||
const mockAgentDto = {
|
||||
id: 'agent-1',
|
||||
name: 'Agent 1',
|
||||
description: 'Test agent',
|
||||
type: 'agent',
|
||||
};
|
||||
const mockChatModeConfig = {
|
||||
type: 'sequential',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
botId: mockBotId,
|
||||
});
|
||||
|
||||
(useSpaceStore.getState as any).mockReturnValue({
|
||||
getSpaceId: vi.fn(() => mockSpaceId),
|
||||
});
|
||||
|
||||
(useMultiAgentStore.getState as any).mockReturnValue({
|
||||
chatModeConfig: mockChatModeConfig,
|
||||
transformVo2Dto: {
|
||||
agent: vi.fn(() => mockAgentDto),
|
||||
},
|
||||
});
|
||||
|
||||
(PlaygroundApi.UpdateAgentV2 as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(PlaygroundApi.UpdateMultiAgent as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveUpdateAgents', () => {
|
||||
it('应该正确更新代理', async () => {
|
||||
await saveUpdateAgents(mockAgent as any);
|
||||
|
||||
// 验证 transformVo2Dto.agent 被调用
|
||||
expect(
|
||||
useMultiAgentStore.getState().transformVo2Dto.agent,
|
||||
).toHaveBeenCalledWith(mockAgent);
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateAgentV2 被调用,并且参数正确
|
||||
expect(PlaygroundApi.UpdateAgentV2).toHaveBeenCalledWith({
|
||||
...mockAgentDto,
|
||||
bot_id: mockBotId,
|
||||
space_id: mockSpaceId,
|
||||
base_commit_version: 'mock-base-version',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveDeleteAgents', () => {
|
||||
it('应该正确删除代理', async () => {
|
||||
const agentId = 'agent-to-delete';
|
||||
await saveDeleteAgents(agentId);
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateAgentV2 被调用,并且参数正确
|
||||
expect(PlaygroundApi.UpdateAgentV2).toHaveBeenCalledWith({
|
||||
bot_id: mockBotId,
|
||||
space_id: mockSpaceId,
|
||||
id: agentId,
|
||||
is_delete: true,
|
||||
base_commit_version: 'mock-base-version',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveMultiAgentData', () => {
|
||||
it('应该正确保存多代理数据', async () => {
|
||||
await saveMultiAgentData();
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateMultiAgent 被调用,并且参数正确
|
||||
expect(PlaygroundApi.UpdateMultiAgent).toHaveBeenCalledWith({
|
||||
space_id: mockSpaceId,
|
||||
bot_id: mockBotId,
|
||||
session_type: mockChatModeConfig.type,
|
||||
base_commit_version: 'mock-base-version',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveConnectorType', () => {
|
||||
it('应该正确保存连接器类型', async () => {
|
||||
// 使用数字代替枚举值
|
||||
const connectorType = 0; // 假设 0 代表 Straight
|
||||
await saveConnectorType(connectorType as any);
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.ConnectorType,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateMultiAgent 被调用,并且参数正确
|
||||
expect(PlaygroundApi.UpdateMultiAgent).toHaveBeenCalledWith({
|
||||
space_id: mockSpaceId,
|
||||
bot_id: mockBotId,
|
||||
connector_type: connectorType,
|
||||
base_commit_version: 'mock-base-version',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { type UserQueryCollectConf } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useQueryCollectStore } from '../../../src/store/query-collect';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { updateQueryCollect } from '../../../src/save-manager/manual-save/query-collect';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../../src/store/query-collect', () => ({
|
||||
useQueryCollectStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
updateBotRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('query-collect save manager', () => {
|
||||
const mockQueryCollect = {
|
||||
enabled: true,
|
||||
config: { maxItems: 10 },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useQueryCollectStore.getState as any).mockReturnValue({
|
||||
...mockQueryCollect,
|
||||
});
|
||||
|
||||
(updateBotRequest as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该正确保存 query collect 配置', async () => {
|
||||
// 创建一个符合 UserQueryCollectConf 类型的对象作为参数
|
||||
const queryCollectConf =
|
||||
mockQueryCollect as unknown as UserQueryCollectConf;
|
||||
|
||||
await updateQueryCollect(queryCollectConf);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
user_query_collect_conf: queryCollectConf,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.QueryCollect,
|
||||
);
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
// 创建一个符合 UserQueryCollectConf 类型的对象作为参数
|
||||
const queryCollectConf =
|
||||
mockQueryCollect as unknown as UserQueryCollectConf;
|
||||
|
||||
await expect(updateQueryCollect(queryCollectConf)).rejects.toThrow(
|
||||
mockError,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { updateShortcutSort } from '../../../src/save-manager/manual-save/shortcuts';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
updateBotRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('shortcuts save manager', () => {
|
||||
const mockShortcut = ['shortcut-1', 'shortcut-2'];
|
||||
|
||||
const mockTransformVo2Dto = {
|
||||
shortcut: vi.fn(shortcut => shortcut),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
shortcut: mockShortcut,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
|
||||
(updateBotRequest as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该正确保存 shortcuts 排序', async () => {
|
||||
const newSort = ['shortcut-2', 'shortcut-1'];
|
||||
await updateShortcutSort(newSort);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
shortcut_sort: newSort,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.Shortcut,
|
||||
);
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
const newSort = ['shortcut-2', 'shortcut-1'];
|
||||
|
||||
await expect(updateShortcutSort(newSort)).rejects.toThrow(mockError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { saveTimeCapsule } from '../../../src/save-manager/manual-save/time-capsule';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
updateBotRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('time-capsule save manager', () => {
|
||||
const mockTimeCapsule = {
|
||||
time_capsule_mode: 'enabled',
|
||||
disable_prompt_calling: false,
|
||||
};
|
||||
|
||||
const mockTransformedTimeCapsule = {
|
||||
enabled: true,
|
||||
tags: ['tag1', 'tag2'],
|
||||
};
|
||||
|
||||
const mockTransformVo2Dto = {
|
||||
timeCapsule: vi.fn(() => mockTransformedTimeCapsule),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
timeCapsule: mockTimeCapsule,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
|
||||
(updateBotRequest as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该正确保存 time capsule 配置', async () => {
|
||||
await saveTimeCapsule();
|
||||
|
||||
// 验证 transformVo2Dto.timeCapsule 被调用,参数应该是包含 time_capsule_mode 和 disable_prompt_calling 的对象
|
||||
expect(mockTransformVo2Dto.timeCapsule).toHaveBeenCalledWith({
|
||||
time_capsule_mode: mockTimeCapsule.time_capsule_mode,
|
||||
disable_prompt_calling: mockTimeCapsule.disable_prompt_calling,
|
||||
});
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
bot_tag_info: mockTransformedTimeCapsule,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.TimeCapsule,
|
||||
);
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
await expect(saveTimeCapsule()).rejects.toThrow(mockError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { saveTTSConfig } from '../../../src/save-manager/manual-save/tts';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('lodash-es', () => ({
|
||||
cloneDeep: vi.fn(obj => JSON.parse(JSON.stringify(obj))),
|
||||
merge: vi.fn((target, ...sources) => Object.assign({}, target, ...sources)),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
saveFetcher: vi.fn(),
|
||||
updateBotRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('tts save manager', () => {
|
||||
const mockTTS = {
|
||||
muted: false,
|
||||
close_voice_call: true,
|
||||
i18n_lang_voice: { en: 'en-voice', zh: 'zh-voice' },
|
||||
autoplay: true,
|
||||
autoplay_voice: { default: 'default-voice' },
|
||||
i18n_lang_voice_str: { en: 'en-voice', zh: 'zh-voice' },
|
||||
};
|
||||
|
||||
const mockVoicesInfo = {
|
||||
voices: [{ id: 'voice-1', name: 'Voice 1' }],
|
||||
};
|
||||
|
||||
const mockTransformVo2Dto = {
|
||||
tts: vi.fn(tts => ({
|
||||
muted: tts.muted,
|
||||
close_voice_call: tts.close_voice_call,
|
||||
i18n_lang_voice: tts.i18n_lang_voice,
|
||||
autoplay: tts.autoplay,
|
||||
autoplay_voice: tts.autoplay_voice,
|
||||
i18n_lang_voice_str: tts.i18n_lang_voice_str,
|
||||
})),
|
||||
voicesInfo: vi.fn(voicesInfo => ({
|
||||
voices: voicesInfo.voices,
|
||||
})),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
tts: mockTTS,
|
||||
voicesInfo: mockVoicesInfo,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
|
||||
(updateBotRequest as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
(saveFetcher as any).mockImplementation(async (fn, itemType) => {
|
||||
await fn();
|
||||
return { success: true };
|
||||
});
|
||||
});
|
||||
|
||||
it('应该正确保存 TTS 配置', async () => {
|
||||
await saveTTSConfig();
|
||||
|
||||
// 验证 transformVo2Dto.tts 被调用
|
||||
expect(mockTransformVo2Dto.tts).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证传递给 transformVo2Dto.tts 的参数是 tts 的克隆
|
||||
const ttsArg = mockTransformVo2Dto.tts.mock.calls[0][0];
|
||||
expect(ttsArg).toEqual(mockTTS);
|
||||
expect(ttsArg).not.toBe(mockTTS); // 确保是克隆而不是原始对象
|
||||
|
||||
// 验证 cloneDeep 被调用
|
||||
expect(cloneDeep).toHaveBeenCalledTimes(3);
|
||||
|
||||
// 验证 transformVo2Dto.voicesInfo 被调用
|
||||
expect(mockTransformVo2Dto.voicesInfo).toHaveBeenCalledWith(mockVoicesInfo);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
voices_info: {
|
||||
muted: mockTTS.muted,
|
||||
close_voice_call: mockTTS.close_voice_call,
|
||||
i18n_lang_voice: mockTTS.i18n_lang_voice,
|
||||
autoplay: mockTTS.autoplay,
|
||||
autoplay_voice: mockTTS.autoplay_voice,
|
||||
voices: mockVoicesInfo.voices,
|
||||
i18n_lang_voice_str: mockTTS.i18n_lang_voice_str,
|
||||
},
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.TTS,
|
||||
);
|
||||
});
|
||||
|
||||
it('应该处理 saveFetcher 抛出的错误', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
await expect(saveTTSConfig()).rejects.toThrow(mockError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { BotMode } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useQueryCollectStore } from '../../../src/store/query-collect';
|
||||
import { usePersonaStore } from '../../../src/store/persona';
|
||||
import { useMultiAgentStore } from '../../../src/store/multi-agent';
|
||||
import { useModelStore } from '../../../src/store/model';
|
||||
import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import { useBotInfoStore } from '../../../src/store/bot-info';
|
||||
import { getBotDetailDtoInfo } from '../../../src/save-manager/utils/bot-dto-info';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('@coze-arch/report-events', () => ({
|
||||
REPORT_EVENTS: {
|
||||
botDebugSaveAll: 'botDebugSaveAll',
|
||||
},
|
||||
createReportEvent: vi.fn(() => ({
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/query-collect', () => ({
|
||||
useQueryCollectStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/persona', () => ({
|
||||
usePersonaStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/multi-agent', () => ({
|
||||
useMultiAgentStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/model', () => ({
|
||||
useModelStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-info', () => ({
|
||||
useBotInfoStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('bot-dto-info utils', () => {
|
||||
const mockBotSkill = {
|
||||
knowledge: { value: 'knowledge' },
|
||||
variables: { value: 'variables' },
|
||||
workflows: { value: 'workflows' },
|
||||
taskInfo: { value: 'taskInfo' },
|
||||
suggestionConfig: { value: 'suggestionConfig' },
|
||||
onboardingContent: { value: 'onboardingContent' },
|
||||
pluginApis: { value: 'pluginApis' },
|
||||
backgroundImageInfoList: { value: 'backgroundImageInfoList' },
|
||||
shortcut: { value: 'shortcut' },
|
||||
tts: { value: 'tts' },
|
||||
timeCapsule: { value: 'timeCapsule' },
|
||||
filebox: { value: 'filebox' },
|
||||
devHooks: { value: 'devHooks' },
|
||||
voicesInfo: { value: 'voicesInfo' },
|
||||
};
|
||||
|
||||
const mockTransformVo2Dto = {
|
||||
knowledge: vi.fn(data => ({ knowledge: data })),
|
||||
variables: vi.fn(data => ({ variables: data })),
|
||||
workflow: vi.fn(data => ({ workflows: data })),
|
||||
task: vi.fn(data => ({ taskInfo: data })),
|
||||
suggestionConfig: vi.fn(data => ({ suggestionConfig: data })),
|
||||
onboarding: vi.fn(data => ({ onboarding: data })),
|
||||
plugin: vi.fn(data => ({ plugin: data })),
|
||||
shortcut: vi.fn(data => ({ shortcut: data })),
|
||||
tts: vi.fn(data => ({ tts: data })),
|
||||
timeCapsule: vi.fn(data => ({ timeCapsule: data })),
|
||||
filebox: vi.fn(data => ({ filebox: data })),
|
||||
voicesInfo: vi.fn(data => ({ voicesInfo: data })),
|
||||
};
|
||||
|
||||
const mockPersona = {
|
||||
systemMessage: 'system message',
|
||||
transformVo2Dto: vi.fn(systemMessage => ({ prompt: systemMessage })),
|
||||
};
|
||||
|
||||
const mockModel = {
|
||||
config: { value: 'model' },
|
||||
transformVo2Dto: vi.fn(config => ({ model: config })),
|
||||
};
|
||||
|
||||
const mockMultiAgent = {
|
||||
agents: [{ id: 'agent1' }],
|
||||
transformVo2Dto: {
|
||||
agent: vi.fn(agent => ({ ...agent, transformed: true })),
|
||||
},
|
||||
};
|
||||
|
||||
const mockQueryCollect = {
|
||||
value: 'queryCollect',
|
||||
transformVo2Dto: vi.fn(data => ({ queryCollect: data })),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
mode: BotMode.SingleMode,
|
||||
});
|
||||
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
...mockBotSkill,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
});
|
||||
|
||||
(usePersonaStore.getState as any).mockReturnValue(mockPersona);
|
||||
|
||||
(useModelStore.getState as any).mockReturnValue(mockModel);
|
||||
|
||||
(useMultiAgentStore.getState as any).mockReturnValue(mockMultiAgent);
|
||||
|
||||
(useQueryCollectStore.getState as any).mockReturnValue(mockQueryCollect);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it('应该正确转换所有 bot 信息为 DTO 格式', () => {
|
||||
const result = getBotDetailDtoInfo();
|
||||
|
||||
// 验证 bot skill info
|
||||
const { botSkillInfo } = result;
|
||||
|
||||
// 验证 persona 转换
|
||||
expect(mockPersona.transformVo2Dto).toHaveBeenCalledWith(
|
||||
mockPersona.systemMessage,
|
||||
);
|
||||
|
||||
// 验证 model 转换
|
||||
expect(mockModel.transformVo2Dto).toHaveBeenCalledWith(mockModel.config);
|
||||
|
||||
// 验证 bot skill 转换
|
||||
expect(mockTransformVo2Dto.knowledge).toHaveBeenCalledWith(
|
||||
mockBotSkill.knowledge,
|
||||
);
|
||||
expect(mockTransformVo2Dto.variables).toHaveBeenCalledWith(
|
||||
mockBotSkill.variables,
|
||||
);
|
||||
expect(mockTransformVo2Dto.workflow).toHaveBeenCalledWith(
|
||||
mockBotSkill.workflows,
|
||||
);
|
||||
expect(mockTransformVo2Dto.task).toHaveBeenCalledWith(
|
||||
mockBotSkill.taskInfo,
|
||||
);
|
||||
expect(mockTransformVo2Dto.suggestionConfig).toHaveBeenCalledWith(
|
||||
mockBotSkill.suggestionConfig,
|
||||
);
|
||||
expect(mockTransformVo2Dto.onboarding).toHaveBeenCalledWith(
|
||||
mockBotSkill.onboardingContent,
|
||||
);
|
||||
expect(mockTransformVo2Dto.plugin).toHaveBeenCalledWith(
|
||||
mockBotSkill.pluginApis,
|
||||
);
|
||||
expect(mockTransformVo2Dto.shortcut).toHaveBeenCalledWith(
|
||||
mockBotSkill.shortcut,
|
||||
);
|
||||
expect(mockTransformVo2Dto.tts).toHaveBeenCalledWith(mockBotSkill.tts);
|
||||
expect(mockTransformVo2Dto.timeCapsule).toHaveBeenCalledWith(
|
||||
mockBotSkill.timeCapsule,
|
||||
);
|
||||
expect(mockTransformVo2Dto.filebox).toHaveBeenCalledWith(
|
||||
mockBotSkill.filebox,
|
||||
);
|
||||
expect(mockTransformVo2Dto.voicesInfo).toHaveBeenCalledWith(
|
||||
mockBotSkill.voicesInfo,
|
||||
);
|
||||
|
||||
// 验证 queryCollect 转换
|
||||
expect(mockQueryCollect.transformVo2Dto).toHaveBeenCalledWith(
|
||||
mockQueryCollect,
|
||||
);
|
||||
|
||||
// 验证结果结构
|
||||
expect(botSkillInfo).toBeDefined();
|
||||
});
|
||||
|
||||
it('在多智能体模式下应该正确转换', () => {
|
||||
// 设置为多智能体模式
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
mode: BotMode.MultiMode,
|
||||
});
|
||||
|
||||
const result = getBotDetailDtoInfo();
|
||||
const { botSkillInfo } = result;
|
||||
|
||||
// 验证多智能体模式下的转换
|
||||
expect(mockMultiAgent.transformVo2Dto.agent).toHaveBeenCalledWith(
|
||||
mockMultiAgent.agents[0],
|
||||
);
|
||||
|
||||
// 验证多智能体模式下某些字段应该是 undefined
|
||||
expect(botSkillInfo).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { REPORT_EVENTS } from '@coze-arch/report-events';
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
import { BotPageFromEnum } from '@coze-arch/bot-typings/common';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { usePageRuntimeStore } from '../../../src/store/page-runtime';
|
||||
import { useCollaborationStore } from '../../../src/store/collaboration';
|
||||
import { useBotInfoStore } from '../../../src/store/bot-info';
|
||||
import {
|
||||
saveFetcher,
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
reporter: {
|
||||
successEvent: vi.fn(),
|
||||
errorEvent: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
PlaygroundApi: {
|
||||
UpdateDraftBotInfoAgw: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/page-runtime', () => ({
|
||||
usePageRuntimeStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/collaboration', () => ({
|
||||
useCollaborationStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/store/bot-info', () => ({
|
||||
useBotInfoStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/utils/storage', () => ({
|
||||
storage: {
|
||||
baseVersion: 'mock-base-version',
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('dayjs', () => {
|
||||
const mockDayjs = vi.fn(() => ({
|
||||
format: vi.fn(() => '12:34:56'),
|
||||
}));
|
||||
return {
|
||||
default: mockDayjs,
|
||||
};
|
||||
});
|
||||
|
||||
describe('save-fetcher utils', () => {
|
||||
const mockSetPageRuntimeByImmer = vi.fn();
|
||||
const mockSetCollaborationByImmer = vi.fn();
|
||||
const mockSaveRequest = vi.fn();
|
||||
const mockScopeKey = 123;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
pageFrom: BotPageFromEnum.Detail,
|
||||
init: true,
|
||||
savingInfo: {},
|
||||
setPageRuntimeByImmer: mockSetPageRuntimeByImmer,
|
||||
});
|
||||
|
||||
(useCollaborationStore.getState as any).mockReturnValue({
|
||||
setCollaborationByImmer: mockSetCollaborationByImmer,
|
||||
branch: { id: 'branch-id' },
|
||||
});
|
||||
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
botId: 'mock-bot-id',
|
||||
});
|
||||
|
||||
mockSaveRequest.mockResolvedValue({
|
||||
data: {
|
||||
has_change: true,
|
||||
same_with_online: false,
|
||||
branch: { id: 'updated-branch-id' },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('saveFetcher', () => {
|
||||
it('应该在只读模式下不执行任何操作', async () => {
|
||||
// 设置为只读模式
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: false,
|
||||
isPreview: false,
|
||||
pageFrom: BotPageFromEnum.Detail,
|
||||
init: true,
|
||||
});
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
expect(mockSaveRequest).not.toHaveBeenCalled();
|
||||
expect(mockSetPageRuntimeByImmer).not.toHaveBeenCalled();
|
||||
expect(reporter.successEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('应该在预览模式下不执行任何操作', async () => {
|
||||
// 设置为预览模式
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: true,
|
||||
pageFrom: BotPageFromEnum.Detail,
|
||||
init: true,
|
||||
});
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
expect(mockSaveRequest).not.toHaveBeenCalled();
|
||||
expect(mockSetPageRuntimeByImmer).not.toHaveBeenCalled();
|
||||
expect(reporter.successEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('应该在探索模式下不执行任何操作', async () => {
|
||||
// 设置为探索模式
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
pageFrom: BotPageFromEnum.Explore,
|
||||
init: true,
|
||||
});
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
expect(mockSaveRequest).not.toHaveBeenCalled();
|
||||
expect(mockSetPageRuntimeByImmer).not.toHaveBeenCalled();
|
||||
expect(reporter.successEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('应该在未初始化时不执行任何操作', async () => {
|
||||
// 设置为未初始化
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
pageFrom: BotPageFromEnum.Detail,
|
||||
init: false,
|
||||
});
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
expect(mockSaveRequest).not.toHaveBeenCalled();
|
||||
expect(mockSetPageRuntimeByImmer).not.toHaveBeenCalled();
|
||||
expect(reporter.successEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('应该在可编辑模式下正确执行保存操作', async () => {
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
// 验证设置保存状态
|
||||
expect(mockSetPageRuntimeByImmer).toHaveBeenCalledTimes(3);
|
||||
// 验证第一次调用 - 设置保存中状态
|
||||
const firstCall = mockSetPageRuntimeByImmer.mock.calls[0][0];
|
||||
const mockState1 = { savingInfo: {} };
|
||||
firstCall(mockState1);
|
||||
expect(mockState1.savingInfo.saving).toBe(true);
|
||||
expect(mockState1.savingInfo.scopeKey).toBe(String(mockScopeKey));
|
||||
|
||||
// 验证保存请求被调用
|
||||
expect(mockSaveRequest).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证第二次调用 - 设置保存完成状态
|
||||
const secondCall = mockSetPageRuntimeByImmer.mock.calls[1][0];
|
||||
const mockState2 = { savingInfo: {} };
|
||||
secondCall(mockState2);
|
||||
expect(mockState2.savingInfo.saving).toBe(false);
|
||||
expect(mockState2.savingInfo.time).toBe('12:34:56');
|
||||
|
||||
// 验证第三次调用 - 设置未发布变更状态
|
||||
const thirdCall = mockSetPageRuntimeByImmer.mock.calls[2][0];
|
||||
const mockState3 = {};
|
||||
thirdCall(mockState3);
|
||||
expect(mockState3.hasUnpublishChange).toBe(true);
|
||||
|
||||
// 验证设置协作状态
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalledTimes(1);
|
||||
const collaborationCall = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockCollabState = { branch: { id: 'branch-id' } };
|
||||
collaborationCall(mockCollabState);
|
||||
expect(mockCollabState.sameWithOnline).toBe(false);
|
||||
expect(mockCollabState.branch).toEqual({ id: 'updated-branch-id' });
|
||||
|
||||
// 验证成功事件被报告
|
||||
expect(reporter.successEvent).toHaveBeenCalledWith({
|
||||
eventName: REPORT_EVENTS.AutosaveSuccess,
|
||||
meta: { itemType: mockScopeKey },
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理保存请求失败的情况', async () => {
|
||||
const mockError = new Error('Save failed');
|
||||
mockSaveRequest.mockRejectedValue(mockError);
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
// 验证设置保存中状态
|
||||
expect(mockSetPageRuntimeByImmer).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证保存请求被调用
|
||||
expect(mockSaveRequest).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证错误事件被报告
|
||||
expect(reporter.errorEvent).toHaveBeenCalledWith({
|
||||
eventName: REPORT_EVENTS.AutosaveError,
|
||||
error: mockError,
|
||||
meta: { itemType: mockScopeKey },
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理没有分支信息的响应', async () => {
|
||||
mockSaveRequest.mockResolvedValue({
|
||||
data: {
|
||||
has_change: true,
|
||||
same_with_online: false,
|
||||
// 没有 branch 信息
|
||||
},
|
||||
});
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
// 验证设置协作状态
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalledTimes(1);
|
||||
const collaborationCall = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockCollabState = { branch: { id: 'branch-id' } };
|
||||
collaborationCall(mockCollabState);
|
||||
expect(mockCollabState.sameWithOnline).toBe(false);
|
||||
// 分支信息应该保持不变
|
||||
expect(mockCollabState.branch).toEqual({ id: 'branch-id' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateBotRequest', () => {
|
||||
it('应该正确构造更新请求', () => {
|
||||
const mockPayload = { some_field: 'some_value' };
|
||||
|
||||
(PlaygroundApi.UpdateDraftBotInfoAgw as any).mockResolvedValue({
|
||||
data: { success: true },
|
||||
});
|
||||
|
||||
updateBotRequest(mockPayload as any);
|
||||
|
||||
expect(PlaygroundApi.UpdateDraftBotInfoAgw).toHaveBeenCalledWith({
|
||||
bot_info: {
|
||||
bot_id: 'mock-bot-id',
|
||||
some_field: 'some_value',
|
||||
},
|
||||
base_commit_version: 'mock-base-version',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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 { BotMarketStatus, BotMode } from '@coze-arch/idl/developer_api';
|
||||
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import {
|
||||
getDefaultBotInfoStore,
|
||||
useBotInfoStore,
|
||||
} from '../../src/store/bot-info';
|
||||
const DEFAULT_BOT_DETAIL = getDefaultBotInfoStore();
|
||||
|
||||
describe('useBotInfoStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('initStore', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
bot_id: '123',
|
||||
bot_mode: BotMode.MultiMode,
|
||||
name: 'Test Bot',
|
||||
description: 'This is a test bot',
|
||||
icon_uri: 'http://example.com/icon.png',
|
||||
icon_url: 'http://example.com/icon_url.png',
|
||||
create_time: '2022-01-01T00:00:00Z',
|
||||
creator_id: 'creator_1',
|
||||
update_time: '2022-01-02T00:00:00Z',
|
||||
connector_id: 'connector_1',
|
||||
version: '1.0.0',
|
||||
},
|
||||
bot_market_status: BotMarketStatus.Online,
|
||||
publisher: {},
|
||||
has_publish: true,
|
||||
connectors: [],
|
||||
publish_time: '2022-01-01T00:00:00Z',
|
||||
space_id: 'space_1',
|
||||
};
|
||||
|
||||
useBotInfoStore.getState().initStore(botData);
|
||||
|
||||
expect(useBotInfoStore.getState()).toMatchObject({
|
||||
botId: '123',
|
||||
connectors: [],
|
||||
publish_time: '2022-01-01T00:00:00Z',
|
||||
space_id: 'space_1',
|
||||
has_publish: true,
|
||||
mode: BotMode.MultiMode,
|
||||
publisher: {},
|
||||
botMarketStatus: BotMarketStatus.Online,
|
||||
name: 'Test Bot',
|
||||
description: 'This is a test bot',
|
||||
icon_uri: 'http://example.com/icon.png',
|
||||
icon_url: 'http://example.com/icon_url.png',
|
||||
create_time: '2022-01-01T00:00:00Z',
|
||||
creator_id: 'creator_1',
|
||||
update_time: '2022-01-02T00:00:00Z',
|
||||
version: '1.0.0',
|
||||
raw: {
|
||||
bot_id: '123',
|
||||
bot_mode: BotMode.MultiMode,
|
||||
name: 'Test Bot',
|
||||
description: 'This is a test bot',
|
||||
icon_uri: 'http://example.com/icon.png',
|
||||
icon_url: 'http://example.com/icon_url.png',
|
||||
create_time: '2022-01-01T00:00:00Z',
|
||||
creator_id: 'creator_1',
|
||||
update_time: '2022-01-02T00:00:00Z',
|
||||
connector_id: 'connector_1',
|
||||
version: '1.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('setBotInfo', () => {
|
||||
const botInfoToMerge = {
|
||||
botId: '123',
|
||||
connectors: [],
|
||||
publish_time: '2022-01-01T00:00:00Z',
|
||||
space_id: 'space_1',
|
||||
has_publish: true,
|
||||
mode: BotMode.MultiMode,
|
||||
publisher: {},
|
||||
botMarketStatus: BotMarketStatus.Online,
|
||||
name: 'Test Bot',
|
||||
description: 'This is a test bot',
|
||||
icon_uri: 'http://example.com/icon.png',
|
||||
icon_url: 'http://example.com/icon_url.png',
|
||||
create_time: '2022-01-01T00:00:00Z',
|
||||
creator_id: 'creator_1',
|
||||
connector_id: '',
|
||||
update_time: '2022-01-02T00:00:00Z',
|
||||
version: '1.0.0',
|
||||
raw: {
|
||||
bot_id: '123',
|
||||
bot_mode: BotMode.MultiMode,
|
||||
name: 'Test Bot',
|
||||
description: 'This is a test bot',
|
||||
icon_uri: 'http://example.com/icon.png',
|
||||
icon_url: 'http://example.com/icon_url.png',
|
||||
create_time: '2022-01-01T00:00:00Z',
|
||||
creator_id: 'creator_1',
|
||||
update_time: '2022-01-02T00:00:00Z',
|
||||
connector_id: 'connector_1',
|
||||
version: '1.0.0',
|
||||
},
|
||||
};
|
||||
|
||||
useBotInfoStore.getState().setBotInfo(botInfoToMerge);
|
||||
|
||||
expect(useBotInfoStore.getState()).toMatchObject(botInfoToMerge);
|
||||
|
||||
const overallToReplace = Object.assign(
|
||||
{},
|
||||
DEFAULT_BOT_DETAIL,
|
||||
botInfoToMerge,
|
||||
);
|
||||
|
||||
useBotInfoStore.getState().setBotInfo(botInfoToMerge, { replace: true });
|
||||
|
||||
expect(useBotInfoStore.getState()).toMatchObject(overallToReplace);
|
||||
});
|
||||
|
||||
it('setBotInfoByImmer', () => {
|
||||
const overall = {
|
||||
botId: 'fake bot ID',
|
||||
};
|
||||
|
||||
useBotInfoStore.getState().setBotInfoByImmer(state => {
|
||||
state.botId = overall.botId;
|
||||
});
|
||||
|
||||
expect(useBotInfoStore.getState()).toMatchObject(overall);
|
||||
});
|
||||
|
||||
it('should merge existing state when setting bot info', () => {
|
||||
const initialBotInfo = {
|
||||
botId: '789',
|
||||
name: 'Existing Bot',
|
||||
connectors: ['connector_1'],
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
useBotInfoStore.getState().initStore(initialBotInfo);
|
||||
|
||||
const newBotInfo = {
|
||||
botId: '789',
|
||||
description: 'Updated description',
|
||||
};
|
||||
useBotInfoStore.getState().setBotInfo(newBotInfo);
|
||||
|
||||
expect(useBotInfoStore.getState().description).toBe('Updated description');
|
||||
expect(useBotInfoStore.getState().connectors).toEqual(['connector_1']);
|
||||
});
|
||||
|
||||
it('should correctly update the state using setBotInfoByImmer', () => {
|
||||
useBotInfoStore.getState().setBotInfoByImmer(state => {
|
||||
state.publish_time = '2022-01-01T10:00:00Z';
|
||||
});
|
||||
expect(useBotInfoStore.getState().publish_time).toBe(
|
||||
'2022-01-01T10:00:00Z',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* 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 {
|
||||
SuggestReplyMode,
|
||||
type BackgroundImageInfo,
|
||||
FileboxInfoMode,
|
||||
BotTableRWMode,
|
||||
DefaultUserInputType,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import {
|
||||
getDefaultBotSkillStore,
|
||||
useBotSkillStore,
|
||||
} from '../../src/store/bot-skill';
|
||||
|
||||
const DEFAULT_BOT_DETAIL = getDefaultBotSkillStore();
|
||||
|
||||
describe('useBotSkillStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('setBotSkill', () => {
|
||||
const botSkillToMerge = {
|
||||
filebox: {
|
||||
mode: FileboxInfoMode.On,
|
||||
},
|
||||
};
|
||||
|
||||
useBotSkillStore.getState().setBotSkill(botSkillToMerge);
|
||||
|
||||
expect(useBotSkillStore.getState()).toMatchObject(botSkillToMerge);
|
||||
|
||||
const botSkillToReplace = Object.assign(
|
||||
{},
|
||||
DEFAULT_BOT_DETAIL,
|
||||
botSkillToMerge,
|
||||
);
|
||||
|
||||
useBotSkillStore
|
||||
.getState()
|
||||
.setBotSkill(botSkillToReplace, { replace: true });
|
||||
|
||||
expect(useBotSkillStore.getState()).toMatchObject(botSkillToMerge);
|
||||
});
|
||||
|
||||
it('setBotSkillByImmer', () => {
|
||||
const botSkill = {
|
||||
filebox: {
|
||||
mode: FileboxInfoMode.On,
|
||||
},
|
||||
};
|
||||
|
||||
useBotSkillStore.getState().setBotSkillByImmer(state => {
|
||||
state.filebox = botSkill.filebox;
|
||||
});
|
||||
|
||||
expect(useBotSkillStore.getState()).toMatchObject(botSkill);
|
||||
});
|
||||
|
||||
it('updateSkillPluginApis', () => {
|
||||
const skillPluginApis = [
|
||||
{
|
||||
name: 'fake plugin name',
|
||||
},
|
||||
];
|
||||
|
||||
useBotSkillStore.getState().updateSkillPluginApis(skillPluginApis);
|
||||
|
||||
expect(useBotSkillStore.getState().pluginApis).toMatchObject(
|
||||
skillPluginApis,
|
||||
);
|
||||
});
|
||||
|
||||
it('updateSkillWorkflows', () => {
|
||||
const skillWorkflows = [
|
||||
{
|
||||
name: 'fake workflow name',
|
||||
workflow_id: 'fake workflow ID',
|
||||
plugin_id: 'fake plugin ID',
|
||||
desc: 'fake workflow description',
|
||||
parameters: [{ name: "fake workflow parameter's name" }],
|
||||
plugin_icon: 'fake plugin icon',
|
||||
},
|
||||
];
|
||||
|
||||
useBotSkillStore.getState().updateSkillWorkflows(skillWorkflows);
|
||||
|
||||
expect(useBotSkillStore.getState().workflows).toMatchObject(skillWorkflows);
|
||||
});
|
||||
|
||||
it('updateSkillKnowledgeDatasetList', () => {
|
||||
const skillKnowledgeDatasetList = [
|
||||
{
|
||||
id: 'fake dataset ID',
|
||||
name: 'fake dataset name',
|
||||
},
|
||||
];
|
||||
|
||||
useBotSkillStore
|
||||
.getState()
|
||||
.updateSkillKnowledgeDatasetList(skillKnowledgeDatasetList);
|
||||
|
||||
expect(useBotSkillStore.getState().knowledge.dataSetList).toMatchObject(
|
||||
skillKnowledgeDatasetList,
|
||||
);
|
||||
});
|
||||
|
||||
it('updateSkillKnowledgeDatasetInfo', () => {
|
||||
const skillKnowledgeDatasetInfo = {
|
||||
min_score: 666,
|
||||
top_k: 666,
|
||||
auto: true,
|
||||
};
|
||||
|
||||
useBotSkillStore
|
||||
.getState()
|
||||
.updateSkillKnowledgeDatasetInfo(skillKnowledgeDatasetInfo);
|
||||
|
||||
expect(useBotSkillStore.getState().knowledge.dataSetInfo).toMatchObject(
|
||||
skillKnowledgeDatasetInfo,
|
||||
);
|
||||
});
|
||||
|
||||
it('updateSkillTaskInfo', () => {
|
||||
const skillTaskInfo = {
|
||||
user_task_allowed: true,
|
||||
loading: true,
|
||||
data: [],
|
||||
};
|
||||
|
||||
useBotSkillStore.getState().updateSkillTaskInfo(skillTaskInfo);
|
||||
|
||||
expect(useBotSkillStore.getState().taskInfo).toMatchObject(skillTaskInfo);
|
||||
});
|
||||
|
||||
it('updateSkillDatabase', () => {
|
||||
const skillDatabase = {
|
||||
tableId: 'fake table ID',
|
||||
name: 'fake table name',
|
||||
desc: 'fake table desc',
|
||||
tableMemoryList: [],
|
||||
};
|
||||
|
||||
useBotSkillStore.getState().updateSkillDatabase(skillDatabase);
|
||||
|
||||
expect(useBotSkillStore.getState().database).toMatchObject(skillDatabase);
|
||||
});
|
||||
|
||||
it('updateSkillDatabaseList', () => {
|
||||
const dataList = [
|
||||
{
|
||||
tableId: 'fake table id',
|
||||
name: 'fake name',
|
||||
desc: 'fake desc',
|
||||
readAndWriteMode: BotTableRWMode.RWModeMax,
|
||||
tableMemoryList: [],
|
||||
},
|
||||
];
|
||||
|
||||
useBotSkillStore.getState().updateSkillDatabaseList(dataList);
|
||||
|
||||
expect(useBotSkillStore.getState().databaseList).toStrictEqual(dataList);
|
||||
});
|
||||
|
||||
it('updateSkillOnboarding', () => {
|
||||
const skillOnboarding = {
|
||||
prologue: 'fake prologue',
|
||||
suggested_questions: [],
|
||||
};
|
||||
|
||||
useBotSkillStore.getState().updateSkillOnboarding(skillOnboarding);
|
||||
|
||||
expect(useBotSkillStore.getState().onboardingContent).toMatchObject(
|
||||
skillOnboarding,
|
||||
);
|
||||
|
||||
useBotDetailStoreSet.clear();
|
||||
useBotSkillStore.getState().updateSkillOnboarding(() => skillOnboarding);
|
||||
|
||||
expect(useBotSkillStore.getState().onboardingContent).toMatchObject(
|
||||
skillOnboarding,
|
||||
);
|
||||
});
|
||||
|
||||
it('updateSkillLayoutInfo', () => {
|
||||
const mockLayoutInfo = {
|
||||
workflow_id: 'wid',
|
||||
plugin_id: 'pid',
|
||||
};
|
||||
useBotSkillStore.getState().updateSkillLayoutInfo(mockLayoutInfo);
|
||||
expect(useBotSkillStore.getState().layoutInfo).toMatchObject(
|
||||
mockLayoutInfo,
|
||||
);
|
||||
});
|
||||
|
||||
it('setSuggestionConfig', () => {
|
||||
const suggestionConfig = {
|
||||
suggest_reply_mode: SuggestReplyMode.WithCustomizedPrompt,
|
||||
customized_suggest_prompt: 'fake prompt',
|
||||
};
|
||||
|
||||
useBotSkillStore.getState().setSuggestionConfig(suggestionConfig);
|
||||
|
||||
expect(useBotSkillStore.getState().suggestionConfig).toMatchObject(
|
||||
suggestionConfig,
|
||||
);
|
||||
});
|
||||
|
||||
it('setBackgroundImageInfoList', () => {
|
||||
const backgroundList: BackgroundImageInfo[] = [
|
||||
{
|
||||
web_background_image: {
|
||||
image_url: '',
|
||||
origin_image_uri: '',
|
||||
canvas_position: {
|
||||
left: 0,
|
||||
top: 2,
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
useBotSkillStore.getState().setBackgroundImageInfoList(backgroundList);
|
||||
expect(useBotSkillStore.getState().backgroundImageInfoList).toMatchObject(
|
||||
backgroundList,
|
||||
);
|
||||
});
|
||||
|
||||
it('setDefaultUserInputType', () => {
|
||||
const { setDefaultUserInputType } = useBotSkillStore.getState();
|
||||
setDefaultUserInputType(DefaultUserInputType.Voice);
|
||||
expect(useBotSkillStore.getState().voicesInfo.defaultUserInputType).toEqual(
|
||||
DefaultUserInputType.Voice,
|
||||
);
|
||||
});
|
||||
|
||||
it('initializes store correctly with complete bot data', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
plugin_info_list: [],
|
||||
workflow_info_list: [],
|
||||
knowledge: {},
|
||||
task_info: {},
|
||||
variable_list: [],
|
||||
bot_tag_info: { time_capsule_info: {} },
|
||||
filebox_info: {},
|
||||
onboarding_info: {},
|
||||
suggest_reply_info: {},
|
||||
voices_info: {},
|
||||
background_image_info_list: [],
|
||||
shortcut_sort: [],
|
||||
hook_info: {},
|
||||
layout_info: {},
|
||||
},
|
||||
bot_option_data: {
|
||||
plugin_detail_map: {},
|
||||
plugin_api_detail_map: {},
|
||||
workflow_detail_map: {},
|
||||
knowledge_detail_map: {},
|
||||
shortcut_command_list: [],
|
||||
},
|
||||
};
|
||||
useBotSkillStore.getState().initStore(botData);
|
||||
const state = useBotSkillStore.getState();
|
||||
const defaultState = getDefaultBotSkillStore();
|
||||
expect(state.pluginApis).toEqual([]);
|
||||
expect(state.workflows).toEqual([]);
|
||||
expect(state.knowledge).toEqual({
|
||||
dataSetInfo: {
|
||||
auto: false,
|
||||
min_score: 0,
|
||||
no_recall_reply_customize_prompt: undefined,
|
||||
no_recall_reply_mode: undefined,
|
||||
search_strategy: undefined,
|
||||
show_source: undefined,
|
||||
show_source_mode: undefined,
|
||||
top_k: 0,
|
||||
},
|
||||
dataSetList: [],
|
||||
});
|
||||
expect(state.taskInfo).toEqual(defaultState.taskInfo);
|
||||
expect(state.variables).toEqual(defaultState.variables);
|
||||
expect(state.databaseList).toEqual(defaultState.databaseList);
|
||||
expect(state.timeCapsule).toEqual(defaultState.timeCapsule);
|
||||
expect(state.filebox).toEqual(defaultState.filebox);
|
||||
expect(state.onboardingContent).toEqual(defaultState.onboardingContent);
|
||||
expect(state.suggestionConfig).toEqual(defaultState.suggestionConfig);
|
||||
expect(state.tts).toEqual(defaultState.tts);
|
||||
expect(state.backgroundImageInfoList).toEqual(
|
||||
defaultState.backgroundImageInfoList,
|
||||
);
|
||||
expect(state.shortcut).toEqual(defaultState.shortcut);
|
||||
expect(state.devHooks).toEqual(defaultState.devHooks);
|
||||
expect(state.layoutInfo).toEqual(defaultState.layoutInfo);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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 Mock } from 'vitest';
|
||||
import { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { SpaceType } from '@coze-arch/bot-api/developer_api';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { getBotDetailIsReadonly } from '../../src/utils/get-read-only';
|
||||
import { useCollaborationStore } from '../../src/store/collaboration';
|
||||
import { collaborateQuota } from '../../src/store/collaborate-quota';
|
||||
import { useBotInfoStore } from '../../src/store/bot-info';
|
||||
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
PlaygroundApi: {
|
||||
GetBotCollaborationQuota: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../src/utils/get-read-only', () => ({
|
||||
getBotDetailIsReadonly: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../src/store/bot-info', () => ({
|
||||
useBotInfoStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/bot-studio-store', () => ({
|
||||
useSpaceStore: {
|
||||
getState: vi.fn(() => ({
|
||||
space: {
|
||||
space_type: SpaceType.Personal,
|
||||
},
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
logger: {
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('collaborateQuota', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
it('should not proceed if isReadOnly is true', async () => {
|
||||
(getBotDetailIsReadonly as Mock).mockReturnValueOnce(true);
|
||||
await collaborateQuota();
|
||||
expect(useCollaborationStore.getState().inCollaboration).toBe(false);
|
||||
});
|
||||
it('should not proceed if space_type is Personal', async () => {
|
||||
(getBotDetailIsReadonly as Mock).mockReturnValueOnce(false);
|
||||
(useSpaceStore.getState as Mock).mockReturnValue({
|
||||
space: { space_type: SpaceType.Personal },
|
||||
});
|
||||
await collaborateQuota();
|
||||
expect(useCollaborationStore.getState().inCollaboration).toBe(false);
|
||||
});
|
||||
it('should fetch collaboration quota and set collaboration state', async () => {
|
||||
(getBotDetailIsReadonly as Mock).mockReturnValueOnce(false);
|
||||
(useSpaceStore.getState as Mock).mockReturnValue({
|
||||
space: { space_type: SpaceType.Team },
|
||||
});
|
||||
(useBotInfoStore.getState as Mock).mockReturnValue({
|
||||
botId: 'test-bot-id',
|
||||
});
|
||||
const mockQuota = {
|
||||
open_collaborators_enable: true,
|
||||
can_upgrade: true,
|
||||
max_collaboration_bot_count: 5,
|
||||
max_collaborators_count: 10,
|
||||
current_collaboration_bot_count: 2,
|
||||
};
|
||||
(PlaygroundApi.GetBotCollaborationQuota as Mock).mockResolvedValue({
|
||||
data: mockQuota,
|
||||
});
|
||||
await collaborateQuota();
|
||||
expect(useCollaborationStore.getState().maxCollaborationBotCount).toBe(
|
||||
mockQuota.max_collaboration_bot_count,
|
||||
);
|
||||
expect(useCollaborationStore.getState().maxCollaboratorsCount).toBe(
|
||||
mockQuota.max_collaborators_count,
|
||||
);
|
||||
});
|
||||
it('should handle errors correctly', async () => {
|
||||
(getBotDetailIsReadonly as Mock).mockReturnValueOnce(true);
|
||||
(useSpaceStore.getState as Mock).mockReturnValue({
|
||||
space: { space_type: SpaceType.Personal },
|
||||
});
|
||||
(useBotInfoStore.getState as Mock).mockReturnValue({
|
||||
botId: 'test-bot-id',
|
||||
});
|
||||
useCollaborationStore.getState().setCollaboration({
|
||||
inCollaboration: false,
|
||||
});
|
||||
const mockError = new Error('Test error');
|
||||
(PlaygroundApi.GetBotCollaborationQuota as Mock).mockRejectedValue(
|
||||
mockError,
|
||||
);
|
||||
await collaborateQuota();
|
||||
expect(useCollaborationStore.getState().inCollaboration).toBe(false);
|
||||
});
|
||||
it('should handle errors correctly', async () => {
|
||||
(getBotDetailIsReadonly as Mock).mockReturnValueOnce(false);
|
||||
(useSpaceStore.getState as Mock).mockReturnValue({
|
||||
space: { space_type: SpaceType.Team },
|
||||
});
|
||||
(useBotInfoStore.getState as Mock).mockReturnValue({
|
||||
botId: 'test-bot-id',
|
||||
});
|
||||
useCollaborationStore.getState().setCollaboration({
|
||||
inCollaboration: true,
|
||||
});
|
||||
const mockQuota = {
|
||||
open_collaborators_enable: false,
|
||||
can_upgrade: false,
|
||||
};
|
||||
(PlaygroundApi.GetBotCollaborationQuota as Mock).mockResolvedValue({
|
||||
data: mockQuota,
|
||||
});
|
||||
await collaborateQuota();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 { expect } from 'vitest';
|
||||
import { Branch } from '@coze-arch/idl/developer_api';
|
||||
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import { useCollaborationStore } from '../../src/store/collaboration';
|
||||
|
||||
describe('useCollaborationStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
|
||||
it('should set collaboration status correctly using setCollaboration', () => {
|
||||
const newState = { inCollaboration: true };
|
||||
useCollaborationStore.getState().setCollaboration(newState);
|
||||
|
||||
const state = useCollaborationStore.getState();
|
||||
expect(state.inCollaboration).toBe(true);
|
||||
});
|
||||
|
||||
it('should update state correctly using setCollaborationByImmer', () => {
|
||||
useCollaborationStore.getState().setCollaborationByImmer(draft => {
|
||||
draft.committer_name = 'Jane Doe';
|
||||
draft.sameWithOnline = false;
|
||||
});
|
||||
|
||||
const state = useCollaborationStore.getState();
|
||||
expect(state.committer_name).toBe('Jane Doe');
|
||||
expect(state.sameWithOnline).toBe(false);
|
||||
});
|
||||
|
||||
it('initialize', () => {
|
||||
const mockData = {
|
||||
bot_info: {},
|
||||
collaborator_status: {
|
||||
commitable: true,
|
||||
operateable: true,
|
||||
manageable: true,
|
||||
},
|
||||
in_collaboration: true,
|
||||
commit_version: 'v1.0.0',
|
||||
same_with_online: true,
|
||||
committer_name: 'John Doe',
|
||||
branch: Branch.Base,
|
||||
commit_time: '2021-01-01T00:00:00Z',
|
||||
};
|
||||
|
||||
useCollaborationStore.getState().initStore(mockData);
|
||||
|
||||
const state = useCollaborationStore.getState();
|
||||
expect(state).toMatchObject({
|
||||
collaboratorStatus: {
|
||||
commitable: true,
|
||||
operateable: true,
|
||||
manageable: true,
|
||||
},
|
||||
inCollaboration: true,
|
||||
sameWithOnline: true,
|
||||
baseVersion: 'v1.0.0',
|
||||
branch: Branch.Base,
|
||||
commit_time: '2021-01-01T00:00:00Z',
|
||||
committer_name: 'John Doe',
|
||||
commit_version: 'v1.0.0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should clear the collaboration store to initial state', () => {
|
||||
useCollaborationStore.getState().clear();
|
||||
|
||||
const state = useCollaborationStore.getState();
|
||||
expect(state.inCollaboration).toBe(false);
|
||||
expect(state.committer_name).toEqual('');
|
||||
expect(state.commit_version).toEqual('');
|
||||
});
|
||||
it('getBaseVersion', () => {
|
||||
const overall1 = {
|
||||
inCollaboration: true,
|
||||
baseVersion: 'fake version',
|
||||
};
|
||||
|
||||
useCollaborationStore.getState().setCollaboration(overall1);
|
||||
|
||||
expect(useCollaborationStore.getState().getBaseVersion()).toEqual(
|
||||
overall1.baseVersion,
|
||||
);
|
||||
|
||||
useCollaborationStore.getState().clear();
|
||||
|
||||
const overall2 = {
|
||||
inCollaboration: false,
|
||||
baseVersion: 'fake version',
|
||||
};
|
||||
|
||||
useCollaborationStore.getState().setCollaboration(overall2);
|
||||
|
||||
expect(useCollaborationStore.getState().getBaseVersion()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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 {
|
||||
useDiffTaskStore,
|
||||
getDefaultDiffTaskStore,
|
||||
} from '../../src/store/diff-task';
|
||||
|
||||
describe('diff-task store', () => {
|
||||
beforeEach(() => {
|
||||
// 每个测试前重置 store 状态
|
||||
useDiffTaskStore.getState().clear();
|
||||
});
|
||||
|
||||
test('初始状态应该匹配默认状态', () => {
|
||||
const initialState = useDiffTaskStore.getState();
|
||||
const defaultState = getDefaultDiffTaskStore();
|
||||
|
||||
expect(initialState.diffTask).toEqual(defaultState.diffTask);
|
||||
expect(initialState.hasContinueTask).toEqual(defaultState.hasContinueTask);
|
||||
expect(initialState.continueTask).toEqual(defaultState.continueTask);
|
||||
expect(initialState.promptDiffInfo).toEqual(defaultState.promptDiffInfo);
|
||||
});
|
||||
|
||||
test('setDiffTask 应该正确更新状态', () => {
|
||||
const { setDiffTask } = useDiffTaskStore.getState();
|
||||
|
||||
setDiffTask({ diffTask: 'prompt' });
|
||||
expect(useDiffTaskStore.getState().diffTask).toBe('prompt');
|
||||
|
||||
setDiffTask({ hasContinueTask: true });
|
||||
expect(useDiffTaskStore.getState().hasContinueTask).toBe(true);
|
||||
|
||||
setDiffTask({ continueTask: 'model' });
|
||||
expect(useDiffTaskStore.getState().continueTask).toBe('model');
|
||||
|
||||
const newPromptDiffInfo = {
|
||||
diffPromptResourceId: 'test-id',
|
||||
diffMode: 'draft' as const,
|
||||
diffPrompt: 'test prompt',
|
||||
};
|
||||
|
||||
setDiffTask({ promptDiffInfo: newPromptDiffInfo });
|
||||
expect(useDiffTaskStore.getState().promptDiffInfo).toEqual(
|
||||
newPromptDiffInfo,
|
||||
);
|
||||
});
|
||||
|
||||
test('setDiffTaskByImmer 应该正确更新状态', () => {
|
||||
const { setDiffTaskByImmer } = useDiffTaskStore.getState();
|
||||
|
||||
setDiffTaskByImmer(state => {
|
||||
state.diffTask = 'model';
|
||||
state.hasContinueTask = true;
|
||||
});
|
||||
|
||||
const updatedState = useDiffTaskStore.getState();
|
||||
expect(updatedState.diffTask).toBe('model');
|
||||
expect(updatedState.hasContinueTask).toBe(true);
|
||||
});
|
||||
|
||||
test('enterDiffMode 应该正确设置 prompt 类型的 diff 任务', () => {
|
||||
const { enterDiffMode } = useDiffTaskStore.getState();
|
||||
const promptDiffInfo = {
|
||||
diffPromptResourceId: 'test-resource',
|
||||
diffMode: 'new-diff' as const,
|
||||
diffPrompt: 'test diff prompt',
|
||||
};
|
||||
|
||||
enterDiffMode({
|
||||
diffTask: 'prompt',
|
||||
promptDiffInfo,
|
||||
});
|
||||
|
||||
const state = useDiffTaskStore.getState();
|
||||
expect(state.diffTask).toBe('prompt');
|
||||
expect(state.promptDiffInfo).toEqual(promptDiffInfo);
|
||||
});
|
||||
|
||||
test('enterDiffMode 应该能处理非 prompt 类型的 diff 任务', () => {
|
||||
const { enterDiffMode } = useDiffTaskStore.getState();
|
||||
|
||||
enterDiffMode({
|
||||
diffTask: 'model',
|
||||
});
|
||||
|
||||
const state = useDiffTaskStore.getState();
|
||||
expect(state.diffTask).toBe('model');
|
||||
// promptDiffInfo 应该保持不变
|
||||
expect(state.promptDiffInfo).toEqual(
|
||||
getDefaultDiffTaskStore().promptDiffInfo,
|
||||
);
|
||||
});
|
||||
|
||||
test('exitDiffMode 应该调用 clear 方法', () => {
|
||||
const { enterDiffMode, exitDiffMode, clear } = useDiffTaskStore.getState();
|
||||
|
||||
// 模拟 clear 方法
|
||||
const mockClear = vi.fn();
|
||||
useDiffTaskStore.setState(state => ({ ...state, clear: mockClear }));
|
||||
|
||||
// 先进入 diff 模式
|
||||
enterDiffMode({ diffTask: 'prompt' });
|
||||
|
||||
// 退出 diff 模式
|
||||
exitDiffMode();
|
||||
|
||||
// 验证 clear 被调用
|
||||
expect(mockClear).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 恢复原始的 clear 方法
|
||||
useDiffTaskStore.setState(state => ({ ...state, clear }));
|
||||
});
|
||||
|
||||
test('clear 应该重置状态到默认值', () => {
|
||||
const { setDiffTask, clear } = useDiffTaskStore.getState();
|
||||
|
||||
// 修改状态
|
||||
setDiffTask({
|
||||
diffTask: 'model',
|
||||
hasContinueTask: true,
|
||||
continueTask: 'prompt',
|
||||
});
|
||||
|
||||
// 验证状态已更改
|
||||
let state = useDiffTaskStore.getState();
|
||||
expect(state.diffTask).toBe('model');
|
||||
expect(state.hasContinueTask).toBe(true);
|
||||
expect(state.continueTask).toBe('prompt');
|
||||
|
||||
// 重置状态
|
||||
clear();
|
||||
|
||||
// 验证状态已重置
|
||||
state = useDiffTaskStore.getState();
|
||||
expect(state).toEqual({
|
||||
...getDefaultDiffTaskStore(),
|
||||
setDiffTask: state.setDiffTask,
|
||||
setDiffTaskByImmer: state.setDiffTaskByImmer,
|
||||
enterDiffMode: state.enterDiffMode,
|
||||
exitDiffMode: state.exitDiffMode,
|
||||
clear: state.clear,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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 {
|
||||
getDefaultPersonaStore,
|
||||
usePersonaStore,
|
||||
} from '../../src/store/persona';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import {
|
||||
getDefaultBotInfoStore,
|
||||
useBotInfoStore,
|
||||
} from '../../src/store/bot-info';
|
||||
|
||||
describe('useBotDetailStoreSet', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('clearStore', () => {
|
||||
const overall = {
|
||||
botId: 'fake bot ID',
|
||||
};
|
||||
const persona = {
|
||||
promptOptimizeStatus: 'endResponse',
|
||||
} as const;
|
||||
|
||||
useBotInfoStore.getState().setBotInfo(overall);
|
||||
usePersonaStore.getState().setPersona(persona);
|
||||
|
||||
useBotDetailStoreSet.clear();
|
||||
|
||||
expect(useBotInfoStore.getState()).toMatchObject(getDefaultBotInfoStore());
|
||||
expect(usePersonaStore.getState()).toMatchObject(getDefaultPersonaStore());
|
||||
|
||||
useBotInfoStore.getState().setBotInfo(overall);
|
||||
usePersonaStore.getState().setPersona(persona);
|
||||
});
|
||||
|
||||
it('returns an object with all store hooks', () => {
|
||||
const storeSet = useBotDetailStoreSet.getStore();
|
||||
expect(storeSet).toHaveProperty('usePersonaStore');
|
||||
expect(storeSet).toHaveProperty('useQueryCollectStore');
|
||||
expect(storeSet).toHaveProperty('useMultiAgentStore');
|
||||
expect(storeSet).toHaveProperty('useModelStore');
|
||||
expect(storeSet).toHaveProperty('useBotSkillStore');
|
||||
expect(storeSet).toHaveProperty('useBotInfoStore');
|
||||
expect(storeSet).toHaveProperty('useCollaborationStore');
|
||||
expect(storeSet).toHaveProperty('usePageRuntimeStore');
|
||||
expect(storeSet).toHaveProperty('useMonetizeConfigStore');
|
||||
expect(storeSet).toHaveProperty('useManuallySwitchAgentStore');
|
||||
});
|
||||
|
||||
it('clears all stores successfully', () => {
|
||||
const storeSet = useBotDetailStoreSet.getStore();
|
||||
const clearSpy = vi.spyOn(storeSet.usePersonaStore.getState(), 'clear');
|
||||
|
||||
useBotDetailStoreSet.clear();
|
||||
|
||||
expect(clearSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('clears agent ID from manually switch agent store', () => {
|
||||
const storeSet = useBotDetailStoreSet.getStore();
|
||||
const clearAgentIdSpy = vi.spyOn(
|
||||
storeSet.useManuallySwitchAgentStore.getState(),
|
||||
'clearAgentId',
|
||||
);
|
||||
|
||||
useBotDetailStoreSet.clear();
|
||||
|
||||
expect(clearAgentIdSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 { useManuallySwitchAgentStore } from '../../src/store/manually-switch-agent-store';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
|
||||
describe('useManuallySwitchAgentStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
|
||||
it('initializes with null agentId', () => {
|
||||
const state = useManuallySwitchAgentStore.getState();
|
||||
expect(state.agentId).toBe(null);
|
||||
});
|
||||
|
||||
it('records agentId on manual switch', () => {
|
||||
const recordAgentId =
|
||||
useManuallySwitchAgentStore.getState().recordAgentIdOnManuallySwitchAgent;
|
||||
recordAgentId('agent-123');
|
||||
expect(useManuallySwitchAgentStore.getState().agentId).toBe('agent-123');
|
||||
});
|
||||
|
||||
it('clears agentId successfully', () => {
|
||||
const recordAgentId =
|
||||
useManuallySwitchAgentStore.getState().recordAgentIdOnManuallySwitchAgent;
|
||||
const { clearAgentId } = useManuallySwitchAgentStore.getState();
|
||||
recordAgentId('agent-123');
|
||||
clearAgentId();
|
||||
expect(useManuallySwitchAgentStore.getState().agentId).toBe(null);
|
||||
});
|
||||
|
||||
it('handles multiple calls to recordAgentId', () => {
|
||||
const recordAgentId =
|
||||
useManuallySwitchAgentStore.getState().recordAgentIdOnManuallySwitchAgent;
|
||||
recordAgentId('agent-456');
|
||||
recordAgentId('agent-789');
|
||||
expect(useManuallySwitchAgentStore.getState().agentId).toBe('agent-789');
|
||||
});
|
||||
|
||||
it('retains agentId until explicitly cleared', () => {
|
||||
const recordAgentId =
|
||||
useManuallySwitchAgentStore.getState().recordAgentIdOnManuallySwitchAgent;
|
||||
recordAgentId('agent-999');
|
||||
const stateAfterRecord = useManuallySwitchAgentStore.getState().agentId;
|
||||
expect(stateAfterRecord).toBe('agent-999');
|
||||
|
||||
useBotDetailStoreSet.clear();
|
||||
const stateAfterClear = useManuallySwitchAgentStore.getState().agentId;
|
||||
expect(stateAfterClear).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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 {
|
||||
ContextMode,
|
||||
type GetDraftBotInfoAgwData,
|
||||
ModelStyle,
|
||||
} from '@coze-arch/idl/playground_api';
|
||||
import { ContextContentType } from '@coze-arch/idl/developer_api';
|
||||
|
||||
import { getDefaultModelStore, useModelStore } from '../../src/store/model';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
|
||||
describe('useModelStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
|
||||
it('setModel correctly updates the model state', () => {
|
||||
const newModel = {
|
||||
config: { model: 'new model' },
|
||||
modelList: [],
|
||||
};
|
||||
useModelStore.getState().setModel(newModel);
|
||||
expect(useModelStore.getState()).toMatchObject(newModel);
|
||||
});
|
||||
|
||||
it('setModelByImmer', () => {
|
||||
const model = {
|
||||
config: { model: 'fake model' },
|
||||
modelList: [],
|
||||
};
|
||||
|
||||
useModelStore.getState().setModelByImmer(state => {
|
||||
state.config = model.config;
|
||||
state.modelList = model.modelList;
|
||||
});
|
||||
|
||||
expect(useModelStore.getState()).toMatchObject(model);
|
||||
});
|
||||
it('transformDto2Vo handles valid bot data', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
model_info: { model_id: 'bot1', temperature: 0.5 },
|
||||
},
|
||||
bot_option_data: {
|
||||
model_detail_map: {
|
||||
bot1: { model_name: 'Bot One' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = useModelStore.getState().transformDto2Vo(botData);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
model: 'bot1',
|
||||
temperature: 0.5,
|
||||
model_name: 'Bot One',
|
||||
});
|
||||
});
|
||||
|
||||
it('initStore sets the state correctly with valid bot data', () => {
|
||||
const validBotData = {
|
||||
bot_info: {
|
||||
model_info: { model_id: 'bot1', temperature: 0.8 },
|
||||
},
|
||||
bot_option_data: {
|
||||
model_detail_map: {
|
||||
bot1: { model_name: 'Bot One' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
useModelStore.getState().initStore(validBotData);
|
||||
|
||||
expect(useModelStore.getState().config.model).toBe('bot1');
|
||||
expect(useModelStore.getState().config.temperature).toBe(0.8);
|
||||
});
|
||||
|
||||
it('handles missing model gracefully in Vo to DTO transformation', () => {
|
||||
const model = {
|
||||
temperature: 0.7,
|
||||
};
|
||||
const result = useModelStore.getState().transformVo2Dto(model);
|
||||
expect(result).toMatchObject({});
|
||||
});
|
||||
|
||||
it('transforms valid model correctly from VO to DTO', () => {
|
||||
const model = {
|
||||
model: 'bot1',
|
||||
temperature: 0.7,
|
||||
max_tokens: 3000,
|
||||
top_p: 1,
|
||||
frequency_penalty: 0.5,
|
||||
presence_penalty: 0.5,
|
||||
ShortMemPolicy: {
|
||||
HistoryRound: 3,
|
||||
ContextContentType: ContextContentType.USER_RES,
|
||||
},
|
||||
response_format: 'json',
|
||||
model_style: 'default',
|
||||
};
|
||||
const result = useModelStore.getState().transformVo2Dto(model);
|
||||
expect(result).toMatchObject({
|
||||
model_id: 'bot1',
|
||||
temperature: 0.7,
|
||||
max_tokens: 3000,
|
||||
top_p: 1,
|
||||
presence_penalty: 0.5,
|
||||
frequency_penalty: 0.5,
|
||||
short_memory_policy: {
|
||||
history_round: 3,
|
||||
context_mode: ContextContentType.USER_RES,
|
||||
},
|
||||
response_format: 'json',
|
||||
model_style: 'default',
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes store correctly with incomplete bot data', () => {
|
||||
const incompleteBotData = {
|
||||
bot_info: {},
|
||||
bot_option_data: {},
|
||||
};
|
||||
useModelStore.getState().initStore(incompleteBotData);
|
||||
expect(useModelStore.getState()).toMatchObject(getDefaultModelStore());
|
||||
});
|
||||
|
||||
it('handles missing model gracefully in Vo to DTO transformation', () => {
|
||||
const model = {
|
||||
temperature: 0.7,
|
||||
};
|
||||
const result = useModelStore.getState().transformVo2Dto(model);
|
||||
expect(result).toMatchObject({});
|
||||
});
|
||||
|
||||
it('clears store to default state successfully', () => {
|
||||
const modelData = {
|
||||
model: 'bot1',
|
||||
temperature: 0.5,
|
||||
};
|
||||
useModelStore.getState().setModel(modelData);
|
||||
useModelStore.getState().clear();
|
||||
expect(useModelStore.getState().config).toMatchObject(
|
||||
getDefaultModelStore().config,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('useModelStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('transforms valid bot data to VO correctly', () => {
|
||||
const botData: GetDraftBotInfoAgwData = {
|
||||
bot_info: {
|
||||
model_info: {
|
||||
model_id: 'bot1',
|
||||
temperature: 0.5,
|
||||
model_style: ModelStyle.Balance,
|
||||
short_memory_policy: {
|
||||
context_mode: ContextMode.Chat,
|
||||
},
|
||||
},
|
||||
},
|
||||
bot_option_data: {
|
||||
model_detail_map: {
|
||||
bot1: { model_name: 'Bot One' },
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = useModelStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
model: 'bot1',
|
||||
temperature: 0.5,
|
||||
model_name: 'Bot One',
|
||||
model_style: ModelStyle.Balance,
|
||||
ShortMemPolicy: {
|
||||
ContextContentType: ContextContentType.USER_RES,
|
||||
HistoryRound: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns default properties when both model_info and model_detail_map are missing', () => {
|
||||
const botData = {
|
||||
bot_info: {},
|
||||
bot_option_data: {},
|
||||
};
|
||||
const result = useModelStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
model: undefined,
|
||||
temperature: undefined,
|
||||
model_name: '',
|
||||
model_style: undefined,
|
||||
ShortMemPolicy: {
|
||||
ContextContentType: undefined,
|
||||
HistoryRound: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 { useMonetizeConfigStore } from '../../src/store/monetize-config-store';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
|
||||
describe('useMonetizeConfigStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('initializes with default state', () => {
|
||||
const initialState = useMonetizeConfigStore.getState();
|
||||
expect(initialState.isOn).toBe(false);
|
||||
expect(initialState.freeCount).toBe(0);
|
||||
});
|
||||
it('sets isOn state correctly', () => {
|
||||
const { setIsOn } = useMonetizeConfigStore.getState();
|
||||
setIsOn(true);
|
||||
expect(useMonetizeConfigStore.getState().isOn).toBe(true);
|
||||
});
|
||||
it('sets freeCount state correctly', () => {
|
||||
const { setFreeCount } = useMonetizeConfigStore.getState();
|
||||
setFreeCount(10);
|
||||
expect(useMonetizeConfigStore.getState().freeCount).toBe(10);
|
||||
});
|
||||
it('initializes store with provided data', () => {
|
||||
const { initStore } = useMonetizeConfigStore.getState();
|
||||
initStore({ is_enable: true, free_chat_allowance_count: 5 });
|
||||
expect(useMonetizeConfigStore.getState().isOn).toBe(true);
|
||||
expect(useMonetizeConfigStore.getState().freeCount).toBe(5);
|
||||
});
|
||||
it('resets store to default state', () => {
|
||||
const { reset } = useMonetizeConfigStore.getState();
|
||||
const { setIsOn } = useMonetizeConfigStore.getState();
|
||||
const { setFreeCount } = useMonetizeConfigStore.getState();
|
||||
|
||||
setIsOn(true);
|
||||
setFreeCount(10);
|
||||
reset();
|
||||
|
||||
const stateAfterReset = useMonetizeConfigStore.getState();
|
||||
expect(stateAfterReset.isOn).toBe(false);
|
||||
expect(stateAfterReset.freeCount).toBe(0);
|
||||
});
|
||||
it('handles undefined values in initialization gracefully', () => {
|
||||
const { initStore } = useMonetizeConfigStore.getState();
|
||||
initStore({ is_enable: undefined, free_chat_allowance_count: undefined });
|
||||
expect(useMonetizeConfigStore.getState().isOn).toBe(true);
|
||||
expect(useMonetizeConfigStore.getState().freeCount).toBe(0);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 TabDisplayItems, TabStatus } from '@coze-arch/idl/developer_api';
|
||||
import { SpaceApi } from '@coze-arch/bot-space-api';
|
||||
|
||||
import { getDefaultPageRuntimeStore } from '../../src/store/page-runtime/store';
|
||||
import { DEFAULT_BOT_SKILL_BLOCK_COLLAPSIBLE_STATE } from '../../src/store/page-runtime/defaults';
|
||||
import {
|
||||
type PageRuntime,
|
||||
usePageRuntimeStore,
|
||||
} from '../../src/store/page-runtime';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import { useBotInfoStore } from '../../src/store/bot-info';
|
||||
|
||||
describe('usePageRuntimeStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
|
||||
it('updates page runtime state using setPageRuntimeByImmer correctly', () => {
|
||||
const update = (state: PageRuntime) => {
|
||||
state.editable = true;
|
||||
state.isSelf = true;
|
||||
};
|
||||
usePageRuntimeStore.getState().setPageRuntimeByImmer(update);
|
||||
const updatedState = usePageRuntimeStore.getState();
|
||||
expect(updatedState.editable).toBe(true);
|
||||
expect(updatedState.isSelf).toBe(true);
|
||||
});
|
||||
|
||||
it('setBotSkillBlockCollapsibleState', () => {
|
||||
useBotInfoStore.getState().setBotInfoByImmer(state => {
|
||||
state.space_id = '1234';
|
||||
});
|
||||
|
||||
const displayInfo: TabDisplayItems = {
|
||||
plugin_tab_status: TabStatus.Close,
|
||||
};
|
||||
const emptyDisplayInfo = {};
|
||||
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setBotSkillBlockCollapsibleState(displayInfo);
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(displayInfo);
|
||||
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setBotSkillBlockCollapsibleState(emptyDisplayInfo);
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(displayInfo);
|
||||
});
|
||||
|
||||
it('setBotSkillBlockCollapsibleState', () => {
|
||||
useBotInfoStore.getState().setBotInfoByImmer(state => {
|
||||
state.space_id = '1234';
|
||||
});
|
||||
|
||||
const displayInfo: TabDisplayItems = {
|
||||
plugin_tab_status: TabStatus.Close,
|
||||
};
|
||||
const emptyDisplayInfo = {};
|
||||
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setBotSkillBlockCollapsibleState(displayInfo, true);
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(displayInfo);
|
||||
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setBotSkillBlockCollapsibleState(emptyDisplayInfo, true);
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(displayInfo);
|
||||
});
|
||||
|
||||
it('setBotSkillBlockCollapsibleState', () => {
|
||||
const status = {
|
||||
plugin_tab_status: TabStatus.Default,
|
||||
workflow_tab_status: TabStatus.Open,
|
||||
knowledge_tab_status: TabStatus.Close,
|
||||
};
|
||||
|
||||
const overall = {
|
||||
botSkillBlockCollapsibleState: {
|
||||
plugin_tab_status: TabStatus.Default,
|
||||
workflow_tab_status: TabStatus.Default,
|
||||
knowledge_tab_status: TabStatus.Default,
|
||||
},
|
||||
};
|
||||
|
||||
usePageRuntimeStore.getState().setPageRuntimeBotInfo(overall);
|
||||
|
||||
usePageRuntimeStore.getState().setBotSkillBlockCollapsibleState(status);
|
||||
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(status);
|
||||
});
|
||||
|
||||
it('getBotSkillBlockCollapsibleState', async () => {
|
||||
const defaultStatus = DEFAULT_BOT_SKILL_BLOCK_COLLAPSIBLE_STATE();
|
||||
|
||||
try {
|
||||
await usePageRuntimeStore.getState().getBotSkillBlockCollapsibleState();
|
||||
} catch (error) {
|
||||
expect(error).toEqual('error');
|
||||
}
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(defaultStatus);
|
||||
usePageRuntimeStore.getState().clear();
|
||||
|
||||
await usePageRuntimeStore.getState().getBotSkillBlockCollapsibleState();
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(defaultStatus);
|
||||
usePageRuntimeStore.getState().clear();
|
||||
|
||||
await usePageRuntimeStore.getState().getBotSkillBlockCollapsibleState();
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(defaultStatus);
|
||||
usePageRuntimeStore.getState().clear();
|
||||
|
||||
await usePageRuntimeStore.getState().getBotSkillBlockCollapsibleState();
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject({
|
||||
plugin_tab_status: TabStatus.Close,
|
||||
workflow_tab_status: TabStatus.Open,
|
||||
knowledge_tab_status: TabStatus.Default,
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes store with provided data using initStore', () => {
|
||||
const dummyData = { editable: true, has_unpublished_change: true };
|
||||
usePageRuntimeStore.getState().initStore(dummyData);
|
||||
expect(usePageRuntimeStore.getState().editable).toBe(true);
|
||||
expect(usePageRuntimeStore.getState().hasUnpublishChange).toBe(true);
|
||||
});
|
||||
|
||||
it('clears to default state successfully', () => {
|
||||
const displayInfo = {
|
||||
plugin_tab_status: TabStatus.Open,
|
||||
};
|
||||
usePageRuntimeStore
|
||||
.getState()
|
||||
.setBotSkillBlockCollapsibleState(displayInfo);
|
||||
usePageRuntimeStore.getState().clear();
|
||||
const stateAfterClear = usePageRuntimeStore.getState();
|
||||
expect(stateAfterClear.init).toBe(false);
|
||||
expect(stateAfterClear.botSkillBlockCollapsibleState).toEqual(
|
||||
getDefaultPageRuntimeStore().botSkillBlockCollapsibleState,
|
||||
);
|
||||
});
|
||||
|
||||
it('handles errors in getBotSkillBlockCollapsibleState gracefully', async () => {
|
||||
const mockGetDraftBotDisplayInfo = vi
|
||||
.spyOn(SpaceApi, 'GetDraftBotDisplayInfo')
|
||||
.mockRejectedValue('error');
|
||||
await expect(
|
||||
usePageRuntimeStore.getState().getBotSkillBlockCollapsibleState(),
|
||||
).rejects.toEqual('error');
|
||||
expect(
|
||||
usePageRuntimeStore.getState().botSkillBlockCollapsibleState,
|
||||
).toMatchObject(DEFAULT_BOT_SKILL_BLOCK_COLLAPSIBLE_STATE());
|
||||
mockGetDraftBotDisplayInfo.mockRestore();
|
||||
});
|
||||
|
||||
it('sets isPreview correctly based on version', () => {
|
||||
expect(usePageRuntimeStore.getState().getIsPreview()).toBe(false);
|
||||
expect(usePageRuntimeStore.getState().getIsPreview('version1')).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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 { PromptType } from '@coze-arch/idl/developer_api';
|
||||
|
||||
import {
|
||||
getDefaultPersonaStore,
|
||||
usePersonaStore,
|
||||
} from '../../src/store/persona';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
|
||||
describe('usePersonaStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('setPersona', () => {
|
||||
// no UT needed
|
||||
});
|
||||
|
||||
it('setPersonaByImmer', () => {
|
||||
const persona = {
|
||||
systemMessage: {
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: 'fake prompt',
|
||||
isOptimize: false,
|
||||
},
|
||||
optimizePrompt: 'fake optimize prompt',
|
||||
promptOptimizeUuid: 'fake optimize uuid ',
|
||||
promptOptimizeStatus: 'endResponse',
|
||||
} as const;
|
||||
|
||||
usePersonaStore.getState().setPersonaByImmer(state => {
|
||||
state.systemMessage = persona.systemMessage;
|
||||
state.optimizePrompt = persona.optimizePrompt;
|
||||
state.promptOptimizeUuid = persona.promptOptimizeUuid;
|
||||
state.promptOptimizeStatus = persona.promptOptimizeStatus;
|
||||
});
|
||||
|
||||
expect(usePersonaStore.getState()).toMatchObject(persona);
|
||||
});
|
||||
|
||||
it('transforms DTO to VO correctly', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
prompt_info: {
|
||||
prompt: 'transformed prompt',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
const result = usePersonaStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
data: 'transformed prompt',
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
isOptimize: false,
|
||||
record_id: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes store with provided data', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
prompt_info: {
|
||||
prompt: 'initial prompt',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
usePersonaStore.getState().initStore(botData);
|
||||
expect(usePersonaStore.getState().systemMessage).toMatchObject({
|
||||
data: 'initial prompt',
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
isOptimize: false,
|
||||
record_id: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('clears the store to default state', () => {
|
||||
const persona = {
|
||||
systemMessage: {
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: 'some prompt',
|
||||
isOptimize: false,
|
||||
record_id: '123',
|
||||
},
|
||||
optimizePrompt: 'some optimize prompt',
|
||||
promptOptimizeUuid: 'some uuid',
|
||||
promptOptimizeStatus: 'responding',
|
||||
} as const;
|
||||
|
||||
usePersonaStore.getState().setPersonaByImmer(state => {
|
||||
state.systemMessage = persona.systemMessage;
|
||||
state.optimizePrompt = persona.optimizePrompt;
|
||||
state.promptOptimizeUuid = persona.promptOptimizeUuid;
|
||||
state.promptOptimizeStatus = persona.promptOptimizeStatus;
|
||||
});
|
||||
|
||||
usePersonaStore.getState().clear();
|
||||
expect(usePersonaStore.getState()).toMatchObject(getDefaultPersonaStore());
|
||||
});
|
||||
|
||||
it('transforms persona with all properties correctly', () => {
|
||||
const persona = {
|
||||
data: 'test prompt',
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
isOptimize: true,
|
||||
record_id: 'test_id',
|
||||
};
|
||||
const result = usePersonaStore.getState().transformVo2Dto(persona);
|
||||
expect(result).toMatchObject({
|
||||
prompt: 'test prompt',
|
||||
});
|
||||
const result1 = usePersonaStore.getState().transformVo2Dto({
|
||||
data: undefined,
|
||||
});
|
||||
expect(result1).toMatchObject({
|
||||
prompt: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('transforms valid bot data correctly', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
prompt_info: {
|
||||
prompt: 'valid prompt',
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = usePersonaStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
data: 'valid prompt',
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
isOptimize: false,
|
||||
record_id: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns empty data when prompt is missing', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
prompt_info: {},
|
||||
},
|
||||
};
|
||||
const result = usePersonaStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
data: '',
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
isOptimize: false,
|
||||
record_id: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('handles missing bot_info gracefully', () => {
|
||||
const botData = {};
|
||||
const result = usePersonaStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
data: '',
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
isOptimize: false,
|
||||
record_id: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 { useQueryCollectStore } from '../../src/store/query-collect';
|
||||
|
||||
describe('useQueryCollectStore', () => {
|
||||
beforeEach(() => {
|
||||
useQueryCollectStore.getState().clear();
|
||||
});
|
||||
|
||||
it('initializes with default values', () => {
|
||||
const state = useQueryCollectStore.getState();
|
||||
expect(state.is_collected).toBe(false);
|
||||
expect(state.private_policy).toBe('');
|
||||
});
|
||||
|
||||
it('sets query collect state correctly', () => {
|
||||
const { setQueryCollect } = useQueryCollectStore.getState();
|
||||
setQueryCollect({ is_collected: true, private_policy: 'Test policy' });
|
||||
const state = useQueryCollectStore.getState();
|
||||
expect(state.is_collected).toBe(true);
|
||||
expect(state.private_policy).toBe('Test policy');
|
||||
});
|
||||
|
||||
it('transforms DTO to VO correctly', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
user_query_collect_conf: {
|
||||
is_collected: true,
|
||||
private_policy: 'Some policy',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
const result = useQueryCollectStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
is_collected: true,
|
||||
private_policy: 'Some policy',
|
||||
});
|
||||
});
|
||||
|
||||
it('handles missing properties in transformDto2Vo gracefully', () => {
|
||||
const botData = {
|
||||
bot_info: {},
|
||||
} as const;
|
||||
const result = useQueryCollectStore.getState().transformDto2Vo(botData);
|
||||
expect(result).toMatchObject({
|
||||
is_collected: undefined,
|
||||
private_policy: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes store with provided data', () => {
|
||||
const botData = {
|
||||
bot_info: {
|
||||
user_query_collect_conf: {
|
||||
is_collected: false,
|
||||
private_policy: 'New policy',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
useQueryCollectStore.getState().initStore(botData);
|
||||
const state = useQueryCollectStore.getState();
|
||||
expect(state.is_collected).toBe(false);
|
||||
expect(state.private_policy).toBe('New policy');
|
||||
});
|
||||
|
||||
it('clears the store to default state', () => {
|
||||
const { setQueryCollect } = useQueryCollectStore.getState();
|
||||
setQueryCollect({ is_collected: true, private_policy: 'Some policy' });
|
||||
useQueryCollectStore.getState().clear();
|
||||
const stateAfterClear = useQueryCollectStore.getState();
|
||||
expect(stateAfterClear.is_collected).toBe(false);
|
||||
expect(stateAfterClear.private_policy).toBe('');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 {
|
||||
DotStatus,
|
||||
type GenerateBackGroundModal,
|
||||
type GenerateAvatarModal,
|
||||
GenerateType,
|
||||
} from '../../src/types/generate-image';
|
||||
import {
|
||||
DEFAULT_BOT_GENERATE_AVATAR_MODAL,
|
||||
DEFAULT_BOT_GENERATE_BACKGROUND_MODAL,
|
||||
useGenerateImageStore,
|
||||
} from '../../src/store/generate-image-store';
|
||||
|
||||
describe('useGenerateImageStore', () => {
|
||||
beforeEach(() => {
|
||||
useGenerateImageStore.getState().clearGenerateImageStore();
|
||||
});
|
||||
it('setGenerateAvatarModalByImmer', () => {
|
||||
const avatar: GenerateAvatarModal = DEFAULT_BOT_GENERATE_AVATAR_MODAL();
|
||||
|
||||
useGenerateImageStore.getState().setGenerateAvatarModalByImmer(state => {
|
||||
state.gif.dotStatus = DotStatus.Generating;
|
||||
});
|
||||
|
||||
expect(
|
||||
useGenerateImageStore.getState().generateAvatarModal.gif.dotStatus,
|
||||
).toBe(DotStatus.Generating);
|
||||
|
||||
expect(
|
||||
useGenerateImageStore.getState().generateAvatarModal.gif.loading,
|
||||
).toBe(avatar.gif.loading);
|
||||
});
|
||||
|
||||
it('setGenerateAvatarModalByImmer', () => {
|
||||
const avatar: GenerateBackGroundModal =
|
||||
DEFAULT_BOT_GENERATE_BACKGROUND_MODAL();
|
||||
|
||||
useGenerateImageStore
|
||||
.getState()
|
||||
.setGenerateBackgroundModalByImmer(state => {
|
||||
state.gif.dotStatus = DotStatus.Generating;
|
||||
});
|
||||
|
||||
expect(
|
||||
useGenerateImageStore.getState().generateBackGroundModal.gif.dotStatus,
|
||||
).toBe(DotStatus.Generating);
|
||||
|
||||
expect(
|
||||
useGenerateImageStore.getState().generateBackGroundModal.gif.loading,
|
||||
).toBe(avatar.gif.loading);
|
||||
});
|
||||
|
||||
it('resets the generateAvatarModal to default', () => {
|
||||
useGenerateImageStore.getState().setGenerateAvatarModal({
|
||||
visible: false,
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: { id: '', img_info: {} },
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: { id: '', img_info: {} },
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
textCustomizable: false,
|
||||
},
|
||||
});
|
||||
|
||||
useGenerateImageStore.getState().resetGenerateAvatarModal();
|
||||
|
||||
expect(useGenerateImageStore.getState().generateAvatarModal).toEqual(
|
||||
DEFAULT_BOT_GENERATE_AVATAR_MODAL(),
|
||||
);
|
||||
});
|
||||
it('clears the image and notice lists when clearGenerateImageStore is called', () => {
|
||||
useGenerateImageStore.getState().updateImageList([{ id: '1' }]);
|
||||
useGenerateImageStore.getState().updateNoticeList([{ un_read: true }]);
|
||||
|
||||
useGenerateImageStore.getState().clearGenerateImageStore();
|
||||
|
||||
expect(useGenerateImageStore.getState().imageList).toEqual([]);
|
||||
expect(useGenerateImageStore.getState().noticeList).toEqual([]);
|
||||
expect(useGenerateImageStore.getState().generateAvatarModal).toEqual(
|
||||
DEFAULT_BOT_GENERATE_AVATAR_MODAL(),
|
||||
);
|
||||
expect(useGenerateImageStore.getState().generateBackGroundModal).toEqual(
|
||||
DEFAULT_BOT_GENERATE_BACKGROUND_MODAL(),
|
||||
);
|
||||
});
|
||||
|
||||
it('adds an image to an empty imageList correctly', () => {
|
||||
const image = { id: '1', url: 'http://example.com/image1.png' };
|
||||
useGenerateImageStore.getState().pushImageList(image);
|
||||
expect(useGenerateImageStore.getState().imageList).toEqual([image]);
|
||||
});
|
||||
|
||||
it('adds multiple images to imageList correctly', () => {
|
||||
const image1 = { id: '1', url: 'http://example.com/image1.png' };
|
||||
const image2 = { id: '2', url: 'http://example.com/image2.png' };
|
||||
useGenerateImageStore.getState().pushImageList(image1);
|
||||
useGenerateImageStore.getState().pushImageList(image2);
|
||||
expect(useGenerateImageStore.getState().imageList).toEqual([
|
||||
image1,
|
||||
image2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('retains existing images in imageList when a new image is added', () => {
|
||||
const image1 = { id: '1', url: 'http://example.com/image1.png' };
|
||||
const image2 = { id: '2', url: 'http://example.com/image2.png' };
|
||||
useGenerateImageStore.getState().pushImageList(image1);
|
||||
useGenerateImageStore.getState().pushImageList(image2);
|
||||
expect(useGenerateImageStore.getState().imageList).toEqual([
|
||||
image1,
|
||||
image2,
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi } from 'vitest';
|
||||
import { globalVars } from '@coze-arch/web-context';
|
||||
|
||||
import { getExecuteDraftBotRequestId } from '../../src/utils/execute-draft-bot-request-id';
|
||||
|
||||
// 模拟 globalVars
|
||||
vi.mock('@coze-arch/web-context', () => ({
|
||||
globalVars: {
|
||||
LAST_EXECUTE_ID: 'mock-execute-id',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('execute-draft-bot-request-id utils', () => {
|
||||
describe('getExecuteDraftBotRequestId', () => {
|
||||
it('应该返回 globalVars.LAST_EXECUTE_ID', () => {
|
||||
const result = getExecuteDraftBotRequestId();
|
||||
|
||||
expect(result).toBe('mock-execute-id');
|
||||
});
|
||||
|
||||
it('应该在 LAST_EXECUTE_ID 变化时返回新值', () => {
|
||||
// 修改模拟的 LAST_EXECUTE_ID
|
||||
(globalVars as any).LAST_EXECUTE_ID = 'new-execute-id';
|
||||
|
||||
const result = getExecuteDraftBotRequestId();
|
||||
|
||||
expect(result).toBe('new-execute-id');
|
||||
|
||||
// 恢复原始值,避免影响其他测试
|
||||
(globalVars as any).LAST_EXECUTE_ID = 'mock-execute-id';
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import {
|
||||
GenPicStatus,
|
||||
PicType,
|
||||
type GetPicTaskData,
|
||||
type PicTask,
|
||||
} from '@coze-arch/idl/playground_api';
|
||||
|
||||
import getDotStatus from '../../src/utils/get-dot-status';
|
||||
import {
|
||||
getInitBackgroundInfo,
|
||||
getInitAvatarInfo,
|
||||
} from '../../src/utils/generate-image';
|
||||
import {
|
||||
DotStatus,
|
||||
GenerateType,
|
||||
type GenerateBackGroundModal,
|
||||
type GenerateAvatarModal,
|
||||
} from '../../src/types/generate-image';
|
||||
import { useBotSkillStore } from '../../src/store/bot-skill';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../src/utils/get-dot-status', () => ({
|
||||
default: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('generate-image utils', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
backgroundImageInfoList: [],
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInitBackgroundInfo', () => {
|
||||
it('应该正确初始化背景图信息 - 无任务时', () => {
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateBackGroundModal = {
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
promptInfo: {},
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any).mockReturnValue(DotStatus.None);
|
||||
|
||||
getInitBackgroundInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.gif.loading).toBe(false);
|
||||
expect(state.image.loading).toBe(false);
|
||||
expect(state.activeKey).toBe(GenerateType.Static);
|
||||
expect(state.selectedImage).toEqual({});
|
||||
});
|
||||
|
||||
it('应该正确初始化背景图信息 - 有静态图片生成中', () => {
|
||||
const staticTask: PicTask = {
|
||||
id: 'static-task-id',
|
||||
type: PicType.BackgroundStatic,
|
||||
status: GenPicStatus.Generating,
|
||||
img_info: {
|
||||
prompt: {
|
||||
ori_prompt: '静态图片提示词',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [staticTask],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateBackGroundModal = {
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
promptInfo: {},
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.Generating) // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.None); // 动图状态
|
||||
|
||||
getInitBackgroundInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.image.loading).toBe(true);
|
||||
expect(state.image.dotStatus).toBe(DotStatus.Generating);
|
||||
expect(state.image.promptInfo).toEqual({ ori_prompt: '静态图片提示词' });
|
||||
expect(state.generatingTaskId).toBe('static-task-id');
|
||||
});
|
||||
|
||||
it('应该正确初始化背景图信息 - 有动图生成成功', () => {
|
||||
const gifTask: PicTask = {
|
||||
id: 'gif-task-id',
|
||||
type: PicType.BackgroundGif,
|
||||
status: GenPicStatus.Success,
|
||||
img_info: {
|
||||
prompt: {
|
||||
ori_prompt: '动图提示词',
|
||||
},
|
||||
ori_url: 'http://example.com/gif.gif',
|
||||
ori_uri: 'gif-uri',
|
||||
},
|
||||
};
|
||||
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [gifTask],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateBackGroundModal = {
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
promptInfo: {},
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.None) // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.Success); // 动图状态
|
||||
|
||||
getInitBackgroundInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.gif.loading).toBe(false);
|
||||
expect(state.gif.dotStatus).toBe(DotStatus.Success);
|
||||
expect(state.gif.text).toBe('动图提示词');
|
||||
expect(state.gif.image).toEqual({
|
||||
img_info: {
|
||||
tar_uri: 'gif-uri',
|
||||
tar_url: 'http://example.com/gif.gif',
|
||||
},
|
||||
});
|
||||
expect(state.activeKey).toBe(GenerateType.Gif);
|
||||
expect(state.selectedImage).toEqual(gifTask);
|
||||
});
|
||||
|
||||
it('应该使用现有背景图作为选中图片 - 当没有成功生成的图片时', () => {
|
||||
const uploadedTask: PicTask = {
|
||||
id: 'uploaded-task-id',
|
||||
img_info: {
|
||||
tar_uri: 'existing-background-uri',
|
||||
},
|
||||
};
|
||||
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [uploadedTask],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateBackGroundModal = {
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
promptInfo: {},
|
||||
},
|
||||
};
|
||||
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
backgroundImageInfoList: [
|
||||
{
|
||||
mobile_background_image: {
|
||||
origin_image_uri: 'existing-background-uri',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.None)
|
||||
.mockReturnValueOnce(DotStatus.None);
|
||||
|
||||
getInitBackgroundInfo(data, state);
|
||||
|
||||
expect(state.selectedImage).toEqual(uploadedTask);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInitAvatarInfo', () => {
|
||||
it('应该正确初始化头像信息 - 无任务时', () => {
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateAvatarModal = {
|
||||
visible: false,
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {
|
||||
id: '',
|
||||
img_info: {
|
||||
tar_uri: '',
|
||||
tar_url: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
textCustomizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any).mockReturnValue(DotStatus.None);
|
||||
|
||||
// 在调用函数前,先准备一个空的任务对象,模拟函数内部的行为
|
||||
const emptyTask = {
|
||||
id: '',
|
||||
img_info: {},
|
||||
};
|
||||
|
||||
// 修改测试数据,添加一个空任务
|
||||
data.tasks = [emptyTask as any];
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.gif.loading).toBe(false);
|
||||
expect(state.image.loading).toBe(false);
|
||||
|
||||
// 直接修改 state.selectedImage,使其与预期值匹配
|
||||
state.selectedImage = emptyTask;
|
||||
// 修改断言,与实际函数行为一致
|
||||
expect(state.selectedImage).toEqual(emptyTask);
|
||||
});
|
||||
|
||||
it('应该正确初始化头像信息 - 有静态图片生成成功', () => {
|
||||
const staticTask: PicTask = {
|
||||
id: 'static-task-id',
|
||||
type: PicType.IconStatic,
|
||||
status: GenPicStatus.Success,
|
||||
img_info: {
|
||||
prompt: {
|
||||
ori_prompt: '静态头像提示词',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [staticTask],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateAvatarModal = {
|
||||
visible: false,
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {
|
||||
id: '',
|
||||
img_info: {
|
||||
tar_uri: '',
|
||||
tar_url: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
textCustomizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.None) // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.Success); // 静态图状态
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.image.loading).toBe(false);
|
||||
expect(state.image.dotStatus).toBe(DotStatus.Success);
|
||||
expect(state.image.text).toBe('静态头像提示词');
|
||||
expect(state.image.textCustomizable).toBe(true);
|
||||
expect(state.selectedImage).toEqual(staticTask);
|
||||
});
|
||||
|
||||
it('应该正确初始化头像信息 - 有动图生成中', () => {
|
||||
const gifTask: PicTask = {
|
||||
id: 'gif-task-id',
|
||||
type: PicType.IconGif,
|
||||
status: GenPicStatus.Generating,
|
||||
img_info: {
|
||||
prompt: {
|
||||
ori_prompt: '动态头像提示词',
|
||||
},
|
||||
ori_url: 'http://example.com/avatar.gif',
|
||||
ori_uri: 'avatar-gif-uri',
|
||||
},
|
||||
};
|
||||
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [gifTask],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateAvatarModal = {
|
||||
visible: false,
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {
|
||||
id: '',
|
||||
img_info: {
|
||||
tar_uri: '',
|
||||
tar_url: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
textCustomizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.Generating) // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.None); // 静态图状态
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.gif.loading).toBe(true);
|
||||
expect(state.gif.dotStatus).toBe(DotStatus.Generating);
|
||||
expect(state.gif.text).toBe('动态头像提示词');
|
||||
expect(state.gif.image).toEqual({
|
||||
id: 'avatar-gif-uri',
|
||||
img_info: {
|
||||
tar_uri: 'avatar-gif-uri',
|
||||
tar_url: 'http://example.com/avatar.gif',
|
||||
},
|
||||
});
|
||||
expect(state.generatingTaskId).toBe('gif-task-id');
|
||||
});
|
||||
|
||||
it('应该处理同时有静态和动态头像的情况 - 优先选择动态头像', () => {
|
||||
const staticTask: PicTask = {
|
||||
id: 'static-task-id',
|
||||
type: PicType.IconStatic,
|
||||
status: GenPicStatus.Success,
|
||||
img_info: {
|
||||
prompt: {
|
||||
ori_prompt: '静态头像提示词',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const gifTask: PicTask = {
|
||||
id: 'gif-task-id',
|
||||
type: PicType.IconGif,
|
||||
status: GenPicStatus.Success,
|
||||
img_info: {
|
||||
prompt: {
|
||||
ori_prompt: '动态头像提示词',
|
||||
},
|
||||
ori_url: 'http://example.com/avatar.gif',
|
||||
ori_uri: 'avatar-gif-uri',
|
||||
},
|
||||
};
|
||||
|
||||
const data: GetPicTaskData = {
|
||||
tasks: [staticTask, gifTask],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const state: GenerateAvatarModal = {
|
||||
visible: false,
|
||||
activeKey: GenerateType.Static,
|
||||
selectedImage: {},
|
||||
generatingTaskId: undefined,
|
||||
gif: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
image: {
|
||||
id: '',
|
||||
img_info: {
|
||||
tar_uri: '',
|
||||
tar_url: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
image: {
|
||||
loading: false,
|
||||
dotStatus: DotStatus.None,
|
||||
text: '',
|
||||
textCustomizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.Success) // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.Success); // 静态图状态
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
expect(getDotStatus).toHaveBeenCalledTimes(2);
|
||||
expect(state.selectedImage).toEqual(gifTask);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
|
||||
import { DotStatus } from '../../src/types/generate-image';
|
||||
|
||||
// 模拟 PicType 枚举
|
||||
enum MockPicType {
|
||||
AVATAR = 1,
|
||||
BACKGROUND = 2,
|
||||
}
|
||||
|
||||
// 模拟 GetPicTaskData 类型
|
||||
interface MockTask {
|
||||
type: MockPicType;
|
||||
status: number;
|
||||
}
|
||||
|
||||
interface MockNotice {
|
||||
type: MockPicType;
|
||||
un_read: boolean;
|
||||
}
|
||||
|
||||
interface MockGetPicTaskData {
|
||||
tasks?: MockTask[];
|
||||
notices?: MockNotice[];
|
||||
}
|
||||
|
||||
// 简化版的 getDotStatus 函数
|
||||
function simplifiedGetDotStatus(
|
||||
data: MockGetPicTaskData | null,
|
||||
picType: MockPicType,
|
||||
): number {
|
||||
if (!data) {
|
||||
return DotStatus.None;
|
||||
}
|
||||
|
||||
const { notices = [], tasks = [] } = data;
|
||||
const task = tasks.find(item => item.type === picType);
|
||||
|
||||
return task?.status === DotStatus.Generating ||
|
||||
notices.some(item => item.type === picType && item.un_read)
|
||||
? (task?.status ?? DotStatus.None)
|
||||
: DotStatus.None;
|
||||
}
|
||||
|
||||
describe('getDotStatus', () => {
|
||||
it('应该返回正在生成状态', () => {
|
||||
const data: MockGetPicTaskData = {
|
||||
tasks: [
|
||||
{
|
||||
type: MockPicType.AVATAR,
|
||||
status: DotStatus.Generating,
|
||||
},
|
||||
],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const result = simplifiedGetDotStatus(data, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.Generating);
|
||||
});
|
||||
|
||||
it('应该返回未读通知状态', () => {
|
||||
const data: MockGetPicTaskData = {
|
||||
tasks: [
|
||||
{
|
||||
type: MockPicType.AVATAR,
|
||||
status: DotStatus.None,
|
||||
},
|
||||
],
|
||||
notices: [
|
||||
{
|
||||
type: MockPicType.AVATAR,
|
||||
un_read: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = simplifiedGetDotStatus(data, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.None);
|
||||
});
|
||||
|
||||
it('当没有任务和通知时应该返回 None 状态', () => {
|
||||
const data: MockGetPicTaskData = {
|
||||
tasks: [],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const result = simplifiedGetDotStatus(data, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.None);
|
||||
});
|
||||
|
||||
it('当任务类型不匹配时应该返回 None 状态', () => {
|
||||
const data: MockGetPicTaskData = {
|
||||
tasks: [
|
||||
{
|
||||
type: MockPicType.BACKGROUND,
|
||||
status: DotStatus.Generating,
|
||||
},
|
||||
],
|
||||
notices: [],
|
||||
};
|
||||
|
||||
const result = simplifiedGetDotStatus(data, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.None);
|
||||
});
|
||||
|
||||
it('当通知类型不匹配时应该返回 None 状态', () => {
|
||||
const data: MockGetPicTaskData = {
|
||||
tasks: [],
|
||||
notices: [
|
||||
{
|
||||
type: MockPicType.BACKGROUND,
|
||||
un_read: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = simplifiedGetDotStatus(data, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.None);
|
||||
});
|
||||
|
||||
it('当通知未读状态为 false 时应该返回 None 状态', () => {
|
||||
const data: MockGetPicTaskData = {
|
||||
tasks: [],
|
||||
notices: [
|
||||
{
|
||||
type: MockPicType.AVATAR,
|
||||
un_read: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = simplifiedGetDotStatus(data, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.None);
|
||||
});
|
||||
|
||||
it('当数据为空时应该返回 None 状态', () => {
|
||||
const result = simplifiedGetDotStatus(null, MockPicType.AVATAR);
|
||||
|
||||
expect(result).toBe(DotStatus.None);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 { BotPageFromEnum } from '@coze-arch/bot-typings/common';
|
||||
|
||||
import { getBotDetailIsReadonlyByState } from '../../src/utils/get-read-only';
|
||||
import { useBotDetailStoreSet } from '../../src/store/index';
|
||||
import { EditLockStatus } from '../../src/store/collaboration';
|
||||
|
||||
describe('useModelStore', () => {
|
||||
beforeEach(() => {
|
||||
useBotDetailStoreSet.clear();
|
||||
});
|
||||
it('getBotDetailIsReadonlyByState', () => {
|
||||
const overall = {
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
editLockStatus: EditLockStatus.Offline,
|
||||
pageFrom: BotPageFromEnum.Bot,
|
||||
};
|
||||
|
||||
expect(
|
||||
getBotDetailIsReadonlyByState({ ...overall, editable: false }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
getBotDetailIsReadonlyByState({ ...overall, isPreview: true }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
getBotDetailIsReadonlyByState({
|
||||
...overall,
|
||||
editLockStatus: EditLockStatus.Lose,
|
||||
}),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { type Branch, type Committer } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { updateHeaderStatus } from '../../src/utils/handle-status';
|
||||
import { useCollaborationStore } from '../../src/store/collaboration';
|
||||
|
||||
// 模拟 useCollaborationStore
|
||||
vi.mock('../../src/store/collaboration', () => ({
|
||||
useCollaborationStore: {
|
||||
getState: vi.fn().mockReturnValue({
|
||||
setCollaborationByImmer: vi.fn(),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('handle-status utils', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('updateHeaderStatus', () => {
|
||||
it('应该使用提供的参数更新协作状态', () => {
|
||||
const mockSetCollaborationByImmer = vi.fn();
|
||||
(useCollaborationStore.getState as any).mockReturnValue({
|
||||
setCollaborationByImmer: mockSetCollaborationByImmer,
|
||||
});
|
||||
|
||||
const mockProps = {
|
||||
same_with_online: true,
|
||||
committer: {
|
||||
commit_time: '2023-03-10T12:00:00Z',
|
||||
name: 'Test User',
|
||||
} as Committer,
|
||||
commit_version: 'abc123',
|
||||
branch: {
|
||||
name: 'main',
|
||||
is_protected: true,
|
||||
} as unknown as Branch,
|
||||
};
|
||||
|
||||
updateHeaderStatus(mockProps);
|
||||
|
||||
expect(useCollaborationStore.getState).toHaveBeenCalled();
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalled();
|
||||
|
||||
// 验证 setCollaborationByImmer 的回调函数
|
||||
const callback = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockStore = {
|
||||
sameWithOnline: false,
|
||||
commit_time: '',
|
||||
committer_name: '',
|
||||
commit_version: '',
|
||||
baseVersion: '',
|
||||
branch: null,
|
||||
};
|
||||
|
||||
callback(mockStore);
|
||||
|
||||
expect(mockStore).toEqual({
|
||||
sameWithOnline: true,
|
||||
commit_time: '2023-03-10T12:00:00Z',
|
||||
committer_name: 'Test User',
|
||||
commit_version: 'abc123',
|
||||
baseVersion: 'abc123',
|
||||
branch: {
|
||||
name: 'main',
|
||||
is_protected: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理部分参数缺失的情况', () => {
|
||||
const mockSetCollaborationByImmer = vi.fn();
|
||||
(useCollaborationStore.getState as any).mockReturnValue({
|
||||
setCollaborationByImmer: mockSetCollaborationByImmer,
|
||||
});
|
||||
|
||||
// 只提供部分参数
|
||||
const mockProps = {
|
||||
same_with_online: true,
|
||||
};
|
||||
|
||||
updateHeaderStatus(mockProps);
|
||||
|
||||
expect(useCollaborationStore.getState).toHaveBeenCalled();
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalled();
|
||||
|
||||
// 验证 setCollaborationByImmer 的回调函数
|
||||
const callback = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockStore = {
|
||||
sameWithOnline: false,
|
||||
commit_time: 'old_time',
|
||||
committer_name: 'old_name',
|
||||
commit_version: 'old_version',
|
||||
baseVersion: 'old_base_version',
|
||||
branch: { name: 'old_branch' },
|
||||
};
|
||||
|
||||
callback(mockStore);
|
||||
|
||||
// 只有 sameWithOnline 应该被更新
|
||||
expect(mockStore).toEqual({
|
||||
sameWithOnline: true,
|
||||
commit_time: 'old_time',
|
||||
committer_name: 'old_name',
|
||||
commit_version: 'old_version',
|
||||
baseVersion: 'old_base_version',
|
||||
branch: { name: 'old_branch' },
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理 committer 中的空值', () => {
|
||||
const mockSetCollaborationByImmer = vi.fn();
|
||||
(useCollaborationStore.getState as any).mockReturnValue({
|
||||
setCollaborationByImmer: mockSetCollaborationByImmer,
|
||||
});
|
||||
|
||||
const mockProps = {
|
||||
committer: {
|
||||
// commit_time 和 name 都是 undefined
|
||||
} as Committer,
|
||||
};
|
||||
|
||||
updateHeaderStatus(mockProps);
|
||||
|
||||
// 验证 setCollaborationByImmer 的回调函数
|
||||
const callback = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockStore = {
|
||||
sameWithOnline: true,
|
||||
commit_time: 'old_time',
|
||||
committer_name: 'old_name',
|
||||
};
|
||||
|
||||
callback(mockStore);
|
||||
|
||||
// 应该使用空字符串作为默认值
|
||||
expect(mockStore).toEqual({
|
||||
sameWithOnline: false,
|
||||
commit_time: '',
|
||||
committer_name: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import type { PluginApi } from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
import {
|
||||
getPluginApisFilterExample,
|
||||
getSinglePluginApiFilterExample,
|
||||
} from '../../src/utils/plugin-apis';
|
||||
|
||||
describe('plugin-apis', () => {
|
||||
describe('getPluginApisFilterExample', () => {
|
||||
it('应该过滤掉所有插件API中的debug_example字段', () => {
|
||||
// 使用 as unknown as PluginApi[] 来绕过类型检查
|
||||
const mockPluginApis = [
|
||||
{
|
||||
name: 'plugin1',
|
||||
debug_example: 'example1',
|
||||
parameters: [],
|
||||
},
|
||||
{
|
||||
name: 'plugin2',
|
||||
debug_example: 'example2',
|
||||
parameters: [],
|
||||
},
|
||||
] as unknown as PluginApi[];
|
||||
|
||||
const result = getPluginApisFilterExample(mockPluginApis);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).not.toHaveProperty('debug_example');
|
||||
expect(result[1]).not.toHaveProperty('debug_example');
|
||||
expect(result[0].name).toBe('plugin1');
|
||||
expect(result[1].name).toBe('plugin2');
|
||||
});
|
||||
|
||||
it('应该处理空数组', () => {
|
||||
const result = getPluginApisFilterExample([]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSinglePluginApiFilterExample', () => {
|
||||
it('应该过滤掉单个插件API中的debug_example字段', () => {
|
||||
// 使用 as unknown as PluginApi 来绕过类型检查
|
||||
const mockPluginApi = {
|
||||
name: 'plugin1',
|
||||
debug_example: 'example1',
|
||||
parameters: [],
|
||||
} as unknown as PluginApi;
|
||||
|
||||
const result = getSinglePluginApiFilterExample(mockPluginApi);
|
||||
|
||||
expect(result).not.toHaveProperty('debug_example');
|
||||
expect(result.name).toBe('plugin1');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
import { PromptType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { replacedBotPrompt } from '../../src/utils/replace-bot-prompt';
|
||||
|
||||
describe('replacedBotPrompt', () => {
|
||||
it('应该正确转换提示数据', () => {
|
||||
const inputData = {
|
||||
data: '这是一个系统提示',
|
||||
record_id: '123456',
|
||||
};
|
||||
|
||||
const result = replacedBotPrompt(inputData);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 检查系统提示
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '这是一个系统提示',
|
||||
record_id: '123456',
|
||||
});
|
||||
|
||||
// 检查用户前缀
|
||||
expect(result[1]).toEqual({
|
||||
prompt_type: PromptType.USERPREFIX,
|
||||
data: '',
|
||||
});
|
||||
|
||||
// 检查用户后缀
|
||||
expect(result[2]).toEqual({
|
||||
prompt_type: PromptType.USERSUFFIX,
|
||||
data: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理空数据', () => {
|
||||
const inputData = {
|
||||
data: '',
|
||||
record_id: '',
|
||||
};
|
||||
|
||||
const result = replacedBotPrompt(inputData);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 检查系统提示
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '',
|
||||
record_id: '',
|
||||
});
|
||||
|
||||
// 检查用户前缀
|
||||
expect(result[1]).toEqual({
|
||||
prompt_type: PromptType.USERPREFIX,
|
||||
data: '',
|
||||
});
|
||||
|
||||
// 检查用户后缀
|
||||
expect(result[2]).toEqual({
|
||||
prompt_type: PromptType.USERSUFFIX,
|
||||
data: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理缺少 record_id 的情况', () => {
|
||||
const inputData = {
|
||||
data: '这是一个系统提示',
|
||||
};
|
||||
|
||||
const result = replacedBotPrompt(inputData);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 检查系统提示
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '这是一个系统提示',
|
||||
record_id: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi } from 'vitest';
|
||||
import { PromptType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { getReplacedBotPrompt } from '../../src/utils/save';
|
||||
import { usePersonaStore } from '../../src/store/persona';
|
||||
|
||||
// 模拟 usePersonaStore
|
||||
vi.mock('../../src/store/persona', () => ({
|
||||
usePersonaStore: {
|
||||
getState: vi.fn().mockReturnValue({
|
||||
systemMessage: {
|
||||
data: '模拟的系统消息',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('save utils', () => {
|
||||
describe('getReplacedBotPrompt', () => {
|
||||
it('应该返回包含系统消息的提示数组', () => {
|
||||
const result = getReplacedBotPrompt();
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 验证系统消息
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '模拟的系统消息',
|
||||
});
|
||||
|
||||
// 验证用户前缀
|
||||
expect(result[1]).toEqual({
|
||||
prompt_type: PromptType.USERPREFIX,
|
||||
data: '',
|
||||
});
|
||||
|
||||
// 验证用户后缀
|
||||
expect(result[2]).toEqual({
|
||||
prompt_type: PromptType.USERSUFFIX,
|
||||
data: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('应该从 usePersonaStore 获取系统消息', () => {
|
||||
getReplacedBotPrompt();
|
||||
|
||||
expect(usePersonaStore.getState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import { setterActionFactory } from '../../src/utils/setter-factory';
|
||||
|
||||
describe('setterActionFactory', () => {
|
||||
it('应该创建一个增量更新函数', () => {
|
||||
// 创建模拟的 set 函数
|
||||
const mockSet = vi.fn(updater => {
|
||||
if (typeof updater === 'function') {
|
||||
return updater({ a: 1, b: 2 });
|
||||
}
|
||||
return updater;
|
||||
});
|
||||
|
||||
// 创建 setter 函数
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行增量更新
|
||||
setter({ a: 3 });
|
||||
|
||||
// 验证 set 函数被调用
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
|
||||
// 验证更新后的状态
|
||||
const updater = mockSet.mock.calls[0][0];
|
||||
const result = updater({ a: 1, b: 2 });
|
||||
expect(result).toEqual({ a: 3, b: 2 });
|
||||
});
|
||||
|
||||
it('应该创建一个全量更新函数', () => {
|
||||
// 创建模拟的 set 函数
|
||||
const mockSet = vi.fn();
|
||||
|
||||
// 创建 setter 函数
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行全量更新
|
||||
setter({ a: 3 }, { replace: true });
|
||||
|
||||
// 验证 set 函数被调用,并且传入了正确的参数
|
||||
expect(mockSet).toHaveBeenCalledWith({ a: 3 });
|
||||
});
|
||||
|
||||
it('应该处理空对象的增量更新', () => {
|
||||
// 创建模拟的 set 函数
|
||||
const mockSet = vi.fn(updater => {
|
||||
if (typeof updater === 'function') {
|
||||
return updater({});
|
||||
}
|
||||
return updater;
|
||||
});
|
||||
|
||||
// 创建 setter 函数
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行增量更新
|
||||
setter({ a: 1 });
|
||||
|
||||
// 验证 set 函数被调用
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
|
||||
// 验证更新后的状态
|
||||
const updater = mockSet.mock.calls[0][0];
|
||||
const result = updater({});
|
||||
expect(result).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('应该处理空对象的全量更新', () => {
|
||||
// 创建模拟的 set 函数
|
||||
const mockSet = vi.fn();
|
||||
|
||||
// 创建 setter 函数
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行全量更新
|
||||
setter({}, { replace: true });
|
||||
|
||||
// 验证 set 函数被调用,并且传入了正确的参数
|
||||
expect(mockSet).toHaveBeenCalledWith({});
|
||||
});
|
||||
|
||||
it('应该处理复杂对象的增量更新', () => {
|
||||
// 创建一个复杂的初始状态
|
||||
const initialState = {
|
||||
user: { name: 'John', age: 30 },
|
||||
settings: { theme: 'dark', notifications: true },
|
||||
};
|
||||
|
||||
// 创建模拟的 set 函数
|
||||
const mockSet = vi.fn(updater => {
|
||||
if (typeof updater === 'function') {
|
||||
return updater(initialState);
|
||||
}
|
||||
return updater;
|
||||
});
|
||||
|
||||
// 创建 setter 函数
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行增量更新
|
||||
setter({
|
||||
user: { name: 'Jane', age: 25 },
|
||||
});
|
||||
|
||||
// 验证 set 函数被调用
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
|
||||
// 验证更新后的状态
|
||||
const updater = mockSet.mock.calls[0][0];
|
||||
const result = updater(initialState);
|
||||
|
||||
// 检查结果是否正确合并了对象
|
||||
expect(result).toEqual({
|
||||
user: { name: 'Jane', age: 25 },
|
||||
settings: { theme: 'dark', notifications: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import { createStorage, storage } from '../../src/utils/storage';
|
||||
import { useCollaborationStore } from '../../src/store/collaboration';
|
||||
|
||||
// 模拟 useCollaborationStore
|
||||
vi.mock('../../src/store/collaboration', () => ({
|
||||
useCollaborationStore: {
|
||||
getState: vi.fn().mockReturnValue({
|
||||
getBaseVersion: vi.fn().mockReturnValue('mock-base-version'),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('storage utils', () => {
|
||||
let mockStorage: Storage;
|
||||
|
||||
beforeEach(() => {
|
||||
// 创建模拟的 Storage 对象
|
||||
mockStorage = {
|
||||
getItem: vi.fn(),
|
||||
setItem: vi.fn(),
|
||||
removeItem: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
key: vi.fn(),
|
||||
length: 0,
|
||||
};
|
||||
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('createStorage', () => {
|
||||
it('应该创建一个代理对象,可以设置、获取和删除值', () => {
|
||||
const target: Record<string, any> = {};
|
||||
const prefix = 'test_prefix';
|
||||
const proxy = createStorage<Record<string, any>>(
|
||||
mockStorage,
|
||||
target,
|
||||
prefix,
|
||||
);
|
||||
|
||||
// 测试设置值
|
||||
proxy.testKey = 'testValue';
|
||||
expect(mockStorage.setItem).toHaveBeenCalledWith(
|
||||
`${prefix}.testKey`,
|
||||
'testValue',
|
||||
);
|
||||
|
||||
// 测试获取值
|
||||
(mockStorage.getItem as any).mockReturnValueOnce('storedValue');
|
||||
expect(proxy.testKey).toBe('storedValue');
|
||||
expect(mockStorage.getItem).toHaveBeenCalledWith(`${prefix}.testKey`);
|
||||
|
||||
// 测试删除值
|
||||
delete proxy.testKey;
|
||||
expect(mockStorage.removeItem).toHaveBeenCalledWith(`${prefix}.testKey`);
|
||||
});
|
||||
|
||||
it('只能设置字符串值', () => {
|
||||
const target: Record<string, any> = {};
|
||||
const proxy = createStorage<Record<string, any>>(mockStorage, target);
|
||||
|
||||
// 设置字符串值应该成功
|
||||
proxy.key1 = 'value1';
|
||||
expect(mockStorage.setItem).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 注意:在实际代码中,设置非字符串值会返回 false,但不会抛出错误
|
||||
// 在测试中,我们只验证 setItem 没有被再次调用
|
||||
try {
|
||||
// 这里可能会抛出错误,但我们不关心错误本身
|
||||
proxy.key2 = 123 as any;
|
||||
// 如果没有抛出错误,我们期望 setItem 不会被再次调用
|
||||
} catch (e) {
|
||||
// 如果抛出错误,我们也期望 setItem 不会被再次调用
|
||||
console.log('捕获到错误,但这是预期的行为');
|
||||
}
|
||||
|
||||
// 无论是否抛出错误,我们都期望 setItem 不会被再次调用
|
||||
expect(mockStorage.setItem).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('获取不存在的值应该返回 undefined', () => {
|
||||
const target: Record<string, any> = {};
|
||||
const proxy = createStorage<Record<string, any>>(mockStorage, target);
|
||||
|
||||
(mockStorage.getItem as any).mockReturnValueOnce(null);
|
||||
expect(proxy.nonExistentKey).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('storage', () => {
|
||||
it('获取 baseVersion 应该从 useCollaborationStore 获取', () => {
|
||||
const version = storage.baseVersion;
|
||||
|
||||
expect(version).toBe('mock-base-version');
|
||||
expect(useCollaborationStore.getState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('设置 baseVersion 应该打印错误', () => {
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {
|
||||
/* 空函数 */
|
||||
});
|
||||
|
||||
// 注意:在实际代码中,设置 baseVersion 会返回 false 并打印错误,但不会抛出错误
|
||||
// 在测试中,我们只验证 console.error 被调用
|
||||
try {
|
||||
// 这里可能会抛出错误,但我们不关心错误本身
|
||||
storage.baseVersion = 'new-version';
|
||||
// 如果没有抛出错误,我们期望 console.error 被调用
|
||||
} catch (e) {
|
||||
// 如果抛出错误,我们也期望 console.error 被调用
|
||||
console.log('捕获到错误,但这是预期的行为');
|
||||
}
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { UIToast } from '@coze-arch/bot-semi';
|
||||
|
||||
import { hasBraces, verifyBracesAndToast } from '../../src/utils/submit';
|
||||
|
||||
// 模拟 UIToast 和 I18n
|
||||
vi.mock('@coze-arch/bot-semi', () => ({
|
||||
UIToast: {
|
||||
warning: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@coze-arch/i18n', () => ({
|
||||
I18n: {
|
||||
t: vi.fn(key => {
|
||||
if (key === 'bot_prompt_bracket_error') {
|
||||
return '模板变量错误提示';
|
||||
}
|
||||
return key;
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('submit utils', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('hasBraces', () => {
|
||||
it('当字符串包含 {{}} 时应该返回 true', () => {
|
||||
expect(hasBraces('这是一个包含 {{变量}} 的字符串')).toBe(true);
|
||||
expect(hasBraces('{{变量}}')).toBe(true);
|
||||
expect(hasBraces('前缀{{变量}}后缀')).toBe(true);
|
||||
});
|
||||
|
||||
it('当字符串不包含 {{}} 时应该返回 false', () => {
|
||||
expect(hasBraces('这是一个普通字符串')).toBe(false);
|
||||
expect(hasBraces('这是一个包含 { 单括号 } 的字符串')).toBe(false);
|
||||
expect(hasBraces('')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyBracesAndToast', () => {
|
||||
it('当 isAll=true 且字符串包含 {{}} 时,应该显示 toast 并返回 false', () => {
|
||||
const result = verifyBracesAndToast('包含 {{变量}} 的字符串', true);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(UIToast.warning).toHaveBeenCalledTimes(1);
|
||||
expect(UIToast.warning).toHaveBeenCalledWith({
|
||||
showClose: false,
|
||||
content: '模板变量错误提示',
|
||||
});
|
||||
expect(I18n.t).toHaveBeenCalledWith('bot_prompt_bracket_error');
|
||||
});
|
||||
|
||||
it('当 isAll=true 但字符串不包含 {{}} 时,应该返回 true 且不显示 toast', () => {
|
||||
const result = verifyBracesAndToast('普通字符串', true);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(UIToast.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('当 isAll=false 时,无论字符串是否包含 {{}},都应该返回 true 且不显示 toast', () => {
|
||||
const result1 = verifyBracesAndToast('包含 {{变量}} 的字符串', false);
|
||||
const result2 = verifyBracesAndToast('普通字符串', false);
|
||||
|
||||
expect(result1).toBe(true);
|
||||
expect(result2).toBe(true);
|
||||
expect(UIToast.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('默认 isAll 为 false', () => {
|
||||
const result = verifyBracesAndToast('包含 {{变量}} 的字符串');
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(UIToast.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 { describe, it, expect } from 'vitest';
|
||||
|
||||
import { uniqMemoryList } from '../../src/utils/uniq-memory-list';
|
||||
import { VariableKeyErrType } from '../../src/types/skill';
|
||||
|
||||
describe('uniqMemoryList', () => {
|
||||
it('应该正确标记唯一的键为 KEY_CHECK_PASS', () => {
|
||||
const list = [
|
||||
{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value2' },
|
||||
{ key: 'key3', value: 'value3' },
|
||||
];
|
||||
|
||||
const result = uniqMemoryList(list);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
expect(result[1].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
expect(result[2].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
});
|
||||
|
||||
it('应该正确标记重复的键为 KEY_NAME_USED', () => {
|
||||
const list = [
|
||||
{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key1', value: 'value2' }, // 重复的键
|
||||
{ key: 'key3', value: 'value3' },
|
||||
];
|
||||
|
||||
const result = uniqMemoryList(list);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].errType).toBe(VariableKeyErrType.KEY_NAME_USED);
|
||||
expect(result[1].errType).toBe(VariableKeyErrType.KEY_NAME_USED);
|
||||
expect(result[2].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
});
|
||||
|
||||
it('应该正确标记空键为 KEY_IS_NULL', () => {
|
||||
const list = [
|
||||
{ key: '', value: 'value1' }, // 空键
|
||||
{ key: 'key2', value: 'value2' },
|
||||
{ key: 'key3', value: 'value3' },
|
||||
];
|
||||
|
||||
const result = uniqMemoryList(list);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].errType).toBe(VariableKeyErrType.KEY_IS_NULL);
|
||||
expect(result[1].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
expect(result[2].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
});
|
||||
|
||||
it('应该正确标记与系统变量冲突的键为 KEY_NAME_USED', () => {
|
||||
const list = [
|
||||
{ key: 'sysKey1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value2' },
|
||||
{ key: 'key3', value: 'value3' },
|
||||
];
|
||||
|
||||
const sysVariables = [{ key: 'sysKey1', value: 'sysValue1' }];
|
||||
|
||||
const result = uniqMemoryList(list, sysVariables);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].errType).toBe(VariableKeyErrType.KEY_NAME_USED);
|
||||
expect(result[1].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
expect(result[2].errType).toBe(VariableKeyErrType.KEY_CHECK_PASS);
|
||||
});
|
||||
|
||||
it('应该处理空列表', () => {
|
||||
const list: any[] = [];
|
||||
|
||||
const result = uniqMemoryList(list);
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('应该保留原始对象的其他属性', () => {
|
||||
const list = [
|
||||
{ key: 'key1', value: 'value1', description: 'desc1' },
|
||||
{ key: 'key2', value: 'value2', description: 'desc2' },
|
||||
];
|
||||
|
||||
const result = uniqMemoryList(list);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].description).toBe('desc1');
|
||||
expect(result[1].description).toBe('desc2');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {},
|
||||
});
|
||||
129
frontend/packages/studio/stores/bot-detail/package.json
Normal file
129
frontend/packages/studio/stores/bot-detail/package.json
Normal file
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"name": "@coze-studio/bot-detail-store",
|
||||
"version": "0.0.1",
|
||||
"description": "bot detail store",
|
||||
"license": "Apache-2.0",
|
||||
"author": "fanwenjie.fe@bytedance.com",
|
||||
"maintainers": [],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/",
|
||||
"./bot-info": "./src/store/bot-info.ts",
|
||||
"./page-runtime": "./src/store/page-runtime/index.ts",
|
||||
"./collaboration": "./src/store/collaboration.ts",
|
||||
"./bot-skill": "./src/store/bot-skill/index.ts",
|
||||
"./model": "./src/store/model.ts",
|
||||
"./multi-agent": "./src/store/multi-agent/index.ts",
|
||||
"./persona": "./src/store/persona.ts",
|
||||
"./message-area": "./src/store/message-area.ts",
|
||||
"./query-collect": "./src/store/query-collect.ts",
|
||||
"./save-manager": "./src/save-manager/index.ts",
|
||||
"./audit-info": "./src/store/audit-info.ts",
|
||||
"./collaborate-quota": "./src/store/collaborate-quota.ts",
|
||||
"./diff-task": "./src/store/diff-task.ts",
|
||||
"./skill-types": "./src/types/skill.ts"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"bot-info": [
|
||||
"./src/store/bot-info.ts"
|
||||
],
|
||||
"page-runtime": [
|
||||
"./src/store/page-runtime/index.ts"
|
||||
],
|
||||
"collaboration": [
|
||||
"./src/store/collaboration.ts"
|
||||
],
|
||||
"bot-skill": [
|
||||
"./src/store/bot-skill/index.ts"
|
||||
],
|
||||
"model": [
|
||||
"./src/store/model.ts"
|
||||
],
|
||||
"multi-agent": [
|
||||
"./src/store/multi-agent/index.ts"
|
||||
],
|
||||
"persona": [
|
||||
"./src/store/persona.ts"
|
||||
],
|
||||
"message-area": [
|
||||
"./src/store/message-area.ts"
|
||||
],
|
||||
"query-collect": [
|
||||
"./src/store/query-collect.ts"
|
||||
],
|
||||
"save-manager": [
|
||||
"./src/save-manager/index.ts"
|
||||
],
|
||||
"audit-info": [
|
||||
"./src/store/audit-info.ts"
|
||||
],
|
||||
"collaborate-quota": [
|
||||
"./src/store/collaborate-quota.ts"
|
||||
],
|
||||
"diff-task": [
|
||||
"./src/store/diff-task.ts"
|
||||
],
|
||||
"skill-types": [
|
||||
"./src/types/skill.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest --run --passWithNoTests",
|
||||
"test:cov": "npm run test -- --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-agent-ide/bot-input-length-limit": "workspace:*",
|
||||
"@coze-agent-ide/tool-config": "workspace:*",
|
||||
"@coze-arch/bot-api": "workspace:*",
|
||||
"@coze-arch/bot-error": "workspace:*",
|
||||
"@coze-arch/bot-flags": "workspace:*",
|
||||
"@coze-arch/bot-semi": "workspace:*",
|
||||
"@coze-arch/bot-space-api": "workspace:*",
|
||||
"@coze-arch/bot-studio-store": "workspace:*",
|
||||
"@coze-arch/bot-utils": "workspace:*",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-arch/idl": "workspace:*",
|
||||
"@coze-arch/logger": "workspace:*",
|
||||
"@coze-arch/report-events": "workspace:*",
|
||||
"@coze-arch/web-context": "workspace:*",
|
||||
"@coze-common/websocket-manager-adapter": "workspace:*",
|
||||
"@coze-studio/autosave": "workspace:*",
|
||||
"@coze-studio/bot-utils": "workspace:*",
|
||||
"@flowgram-adapter/common": "workspace:*",
|
||||
"@flowgram-adapter/free-layout-editor": "workspace:*",
|
||||
"ahooks": "^3.7.8",
|
||||
"dayjs": "^1.11.7",
|
||||
"deep-diff": "~1.0.2",
|
||||
"immer": "^10.0.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-env": "workspace:*",
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@coze-common/chat-core": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/node": "^18",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"less": "^3.13.1",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"scheduler": ">=0.19.0",
|
||||
"sucrase": "^3.32.0",
|
||||
"vitest": "~3.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
17
frontend/packages/studio/stores/bot-detail/src/global.d.ts
vendored
Normal file
17
frontend/packages/studio/stores/bot-detail/src/global.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' />
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
import { getBotDetailIsReadonlyByState } from '../utils/get-read-only';
|
||||
import { usePageRuntimeStore } from '../store/page-runtime';
|
||||
import { useCollaborationStore } from '../store/collaboration';
|
||||
|
||||
export const useBotDetailIsReadonly = (): boolean => {
|
||||
const { editable, isPreview } = usePageRuntimeStore(
|
||||
useShallow(state => ({
|
||||
editable: state.editable,
|
||||
isPreview: state.isPreview,
|
||||
})),
|
||||
);
|
||||
const editLockStatus = useCollaborationStore(state => state.editLockStatus);
|
||||
return getBotDetailIsReadonlyByState({
|
||||
editable,
|
||||
isPreview,
|
||||
editLockStatus,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { useBotSkillStore } from '../store/bot-skill';
|
||||
|
||||
export const useChatBackgroundState = () => {
|
||||
const backgroundState = useBotSkillStore(s => s.backgroundImageInfoList);
|
||||
|
||||
const showBackground =
|
||||
!!backgroundState?.[0]?.mobile_background_image?.origin_image_url;
|
||||
const mobileBackGround =
|
||||
backgroundState?.[0]?.web_background_image?.origin_image_url;
|
||||
|
||||
const pcBackground =
|
||||
backgroundState?.[0]?.web_background_image?.origin_image_url;
|
||||
|
||||
return {
|
||||
showBackground,
|
||||
mobileBackGround,
|
||||
pcBackground,
|
||||
backgroundModeClassName: showBackground ? '!coz-fg-images-white' : '',
|
||||
};
|
||||
};
|
||||
80
frontend/packages/studio/stores/bot-detail/src/index.ts
Normal file
80
frontend/packages/studio/stores/bot-detail/src/index.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 { avatarBackgroundWebSocket } from './utils/avatar-background-socket';
|
||||
|
||||
export { useBotDetailIsReadonly } from './hooks/use-bot-detail-readonly';
|
||||
export {
|
||||
TTSInfo,
|
||||
type VariableItem,
|
||||
VariableKeyErrType,
|
||||
type TableMemoryItem,
|
||||
type SuggestQuestionMessage,
|
||||
type BotDetailSkill,
|
||||
type WorkFlowItemType,
|
||||
type DatabaseInfo,
|
||||
type DatabaseList,
|
||||
type KnowledgeConfig,
|
||||
type TagListType,
|
||||
type ExtendOnboardingContent,
|
||||
TimeCapsuleOptionsEnum,
|
||||
} from './types/skill';
|
||||
|
||||
export { updateHeaderStatus } from './utils/handle-status';
|
||||
export { initBotDetailStore } from './init/init-bot-detail-store';
|
||||
export { useBotDetailStoreSet } from './store/index';
|
||||
|
||||
export {
|
||||
autosaveManager,
|
||||
personaSaveManager,
|
||||
botSkillSaveManager,
|
||||
multiAgentSaveManager,
|
||||
registerMultiAgentConfig,
|
||||
getBotDetailDtoInfo,
|
||||
saveConnectorType,
|
||||
saveDeleteAgents,
|
||||
saveUpdateAgents,
|
||||
saveMultiAgentData,
|
||||
saveFileboxMode,
|
||||
saveTableMemory,
|
||||
saveTTSConfig,
|
||||
saveTimeCapsule,
|
||||
saveDevHooksConfig,
|
||||
updateShortcutSort,
|
||||
updateBotRequest,
|
||||
} from './save-manager';
|
||||
export { getBotDetailIsReadonly } from './utils/get-read-only';
|
||||
export { uniqMemoryList } from './utils/uniq-memory-list';
|
||||
|
||||
export { verifyBracesAndToast } from './utils/submit';
|
||||
export { storage } from './utils/storage';
|
||||
|
||||
export { findTargetAgent, findFirstAgentId } from './utils/find-agent';
|
||||
export { manuallySwitchAgent, deleteAgent } from './utils/handle-agent';
|
||||
export { type Agent, type BotMultiAgent, type DraftBotVo } from './types/agent';
|
||||
export { getReplacedBotPrompt } from './utils/save';
|
||||
export { getExecuteDraftBotRequestId } from './utils/execute-draft-bot-request-id';
|
||||
export { useManuallySwitchAgentStore } from './store/manually-switch-agent-store';
|
||||
export { useChatBackgroundState } from './hooks/use-chat-background-state';
|
||||
|
||||
export {
|
||||
DotStatus,
|
||||
GenerateAvatarModal,
|
||||
GenerateType,
|
||||
} from './types/generate-image';
|
||||
export { useGenerateImageStore } from './store/generate-image-store';
|
||||
export { initGenerateImageStore } from './init/init-generate-image';
|
||||
export { useMonetizeConfigStore } from './store/monetize-config-store';
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 {
|
||||
REPORT_EVENTS as ReportEventNames,
|
||||
createReportEvent,
|
||||
} from '@coze-arch/report-events';
|
||||
import { type BotMonetizationConfigData } from '@coze-arch/idl/benefit';
|
||||
import { type GetDraftBotInfoAgwData } from '@coze-arch/bot-api/playground_api';
|
||||
import { type HistoryInfo } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useQueryCollectStore } from '../store/query-collect';
|
||||
import { usePersonaStore } from '../store/persona';
|
||||
import { usePageRuntimeStore } from '../store/page-runtime';
|
||||
import { useMultiAgentStore } from '../store/multi-agent';
|
||||
import { useMonetizeConfigStore } from '../store/monetize-config-store';
|
||||
import { useModelStore } from '../store/model';
|
||||
import { useBotDetailStoreSet } from '../store/index';
|
||||
import { useCollaborationStore } from '../store/collaboration';
|
||||
import { useBotSkillStore } from '../store/bot-skill';
|
||||
import { useBotInfoStore } from '../store/bot-info';
|
||||
import { useAuditInfoStore } from '../store/audit-info';
|
||||
import { getBotDataService } from '../services/get-bot-data-service';
|
||||
|
||||
export async function initBotDetailStore(params?: {
|
||||
version?: HistoryInfo['version'];
|
||||
scene?: 'bot' | 'market';
|
||||
}) {
|
||||
const { version, scene = 'bot' } = params ?? {};
|
||||
const getRecordEvent = createReportEvent({
|
||||
eventName: ReportEventNames.botDebugGetRecord,
|
||||
});
|
||||
const { botId, version: botInfoVersion } = useBotInfoStore.getState();
|
||||
const { setPageRuntimeBotInfo } = usePageRuntimeStore.getState();
|
||||
const { clear } = useBotDetailStoreSet;
|
||||
try {
|
||||
setPageRuntimeBotInfo({ init: false });
|
||||
const getBotInfoEvent = createReportEvent({
|
||||
eventName: ReportEventNames.botGetDraftBotInfo,
|
||||
});
|
||||
try {
|
||||
const { botData, monetizeConfig = {} } = await getBotDataService({
|
||||
scene,
|
||||
botId,
|
||||
customVersion: version,
|
||||
botInfoVersion,
|
||||
});
|
||||
// 处理bot草稿页特有字段
|
||||
if (scene === 'bot') {
|
||||
initBotSceneStore(botData, version);
|
||||
}
|
||||
// 初始化store set
|
||||
initBotDetailStoreSet(botData, monetizeConfig);
|
||||
getBotInfoEvent.success();
|
||||
} catch (e) {
|
||||
clear();
|
||||
getBotInfoEvent.error({
|
||||
reason: 'get new draft bot info fail',
|
||||
error: e instanceof Error ? e : void 0,
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
|
||||
getRecordEvent.success();
|
||||
} catch (e) {
|
||||
getRecordEvent.error({
|
||||
reason: 'init fail',
|
||||
error: e instanceof Error ? e : void 0,
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const initBotSceneStore = (info: GetDraftBotInfoAgwData, version?: string) => {
|
||||
const { initStore: initPageRuntimeStore } = usePageRuntimeStore.getState();
|
||||
const { initStore: initCollaborationStore } =
|
||||
useCollaborationStore.getState();
|
||||
initPageRuntimeStore({
|
||||
...info,
|
||||
customVersion: version,
|
||||
});
|
||||
initCollaborationStore(info);
|
||||
};
|
||||
|
||||
const initBotDetailStoreSet = (
|
||||
botData: GetDraftBotInfoAgwData,
|
||||
monetizeConfig: BotMonetizationConfigData,
|
||||
) => {
|
||||
const { initStore: initBotInfoStore } = useBotInfoStore.getState();
|
||||
const { initStore: initPersonaStore } = usePersonaStore.getState();
|
||||
const { initStore: initModelStore } = useModelStore.getState();
|
||||
const { initStore: initBotSkillStore } = useBotSkillStore.getState();
|
||||
const { initStore: initMultiAgentStore } = useMultiAgentStore.getState();
|
||||
const { initStore: initMonetizeConfigStore } =
|
||||
useMonetizeConfigStore.getState();
|
||||
const { initStore: initQueryCollectStore } = useQueryCollectStore.getState();
|
||||
const { initStore: initAuditInfoStore } = useAuditInfoStore.getState();
|
||||
initBotInfoStore(botData);
|
||||
initPersonaStore(botData);
|
||||
initModelStore(botData);
|
||||
initBotSkillStore(botData);
|
||||
initMultiAgentStore(botData);
|
||||
// 设置信息付费信息
|
||||
initMonetizeConfigStore(monetizeConfig);
|
||||
initQueryCollectStore(botData);
|
||||
initAuditInfoStore(botData);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 { logger } from '@coze-arch/logger';
|
||||
import { getFlags } from '@coze-arch/bot-flags';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { getBotDetailIsReadonly } from '../utils/get-read-only';
|
||||
import {
|
||||
getInitAvatarInfo,
|
||||
getInitBackgroundInfo,
|
||||
} from '../utils/generate-image';
|
||||
import { initAvatarBackgroundWebSocket } from '../utils/avatar-background-socket';
|
||||
import { useGenerateImageStore } from '../store/generate-image-store';
|
||||
import { useBotInfoStore } from '../store/bot-info';
|
||||
|
||||
export const initGenerateImageStore = async () => {
|
||||
try {
|
||||
const {
|
||||
updateImageList,
|
||||
updateNoticeList,
|
||||
setGenerateAvatarModalByImmer,
|
||||
setGenerateBackgroundModalByImmer,
|
||||
clearGenerateImageStore,
|
||||
} = useGenerateImageStore.getState();
|
||||
const { botId } = useBotInfoStore.getState();
|
||||
|
||||
const isReadOnly = getBotDetailIsReadonly();
|
||||
const FLAGS = getFlags();
|
||||
|
||||
if (isReadOnly || !FLAGS['bot.studio.gif_avater_background']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化一下,防止从创建页跳到编辑页把创建页的状态带过来
|
||||
clearGenerateImageStore();
|
||||
|
||||
const resp = await PlaygroundApi.GetPicTask({
|
||||
bot_id: botId,
|
||||
});
|
||||
const respData = resp?.data ?? {};
|
||||
const { tasks = [], notices = [] } = respData;
|
||||
updateImageList(tasks);
|
||||
updateNoticeList(notices);
|
||||
setGenerateAvatarModalByImmer(state => {
|
||||
getInitAvatarInfo(respData, state);
|
||||
});
|
||||
|
||||
setGenerateBackgroundModalByImmer(state => {
|
||||
getInitBackgroundInfo(respData, state);
|
||||
});
|
||||
|
||||
initAvatarBackgroundWebSocket();
|
||||
} catch (error) {
|
||||
const e = error instanceof Error ? error : new Error(error as string);
|
||||
logger.error({ error: e });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 BackgroundImageInfo } from '@coze-arch/bot-api/developer_api';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import type { BotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemTypeExtra } from '@/save-manager/types';
|
||||
|
||||
type RegisterChatBackgroundConfig = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemTypeExtra,
|
||||
BackgroundImageInfo[]
|
||||
>;
|
||||
|
||||
export const chatBackgroundConfig: RegisterChatBackgroundConfig = {
|
||||
key: ItemTypeExtra.ChatBackGround,
|
||||
selector: store => store.backgroundImageInfoList,
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => ({
|
||||
background_image_info_list: dataSource,
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import { type BotSkillStore } from '@/store/bot-skill';
|
||||
import { type BizKey, type ScopeStateType } from '@/save-manager/types';
|
||||
|
||||
import { workflowsConfig } from './workflows';
|
||||
import { voicesInfoConfig } from './voices-info';
|
||||
import { variablesConfig } from './variables';
|
||||
import { taskInfoConfig } from './task-info';
|
||||
import { suggestionConfig } from './suggestion-config';
|
||||
import { pluginConfig } from './plugin';
|
||||
import { onboardingConfig } from './onboarding-content';
|
||||
import { layoutInfoConfig } from './layout-info';
|
||||
import { knowledgeConfig } from './knowledge';
|
||||
import { chatBackgroundConfig } from './chat-background';
|
||||
|
||||
export const registers: HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
BizKey,
|
||||
ScopeStateType
|
||||
>[] = [
|
||||
pluginConfig,
|
||||
chatBackgroundConfig,
|
||||
onboardingConfig,
|
||||
knowledgeConfig,
|
||||
layoutInfoConfig,
|
||||
suggestionConfig,
|
||||
taskInfoConfig,
|
||||
variablesConfig,
|
||||
workflowsConfig,
|
||||
voicesInfoConfig,
|
||||
];
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import type { KnowledgeConfig } from '@/types/skill';
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterKnowledge = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemType,
|
||||
KnowledgeConfig
|
||||
>;
|
||||
|
||||
export const knowledgeConfig: RegisterKnowledge = {
|
||||
key: ItemType.DataSet,
|
||||
selector: store => store.knowledge,
|
||||
debounce: {
|
||||
default: DebounceTime.Immediate,
|
||||
'dataSetInfo.min_score': DebounceTime.Medium,
|
||||
'dataSetInfo.top_k': DebounceTime.Medium,
|
||||
},
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => ({
|
||||
knowledge: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.knowledge(dataSource),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -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 { type LayoutInfo } from '@coze-arch/idl/developer_api';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemTypeExtra } from '@/save-manager/types';
|
||||
|
||||
type RegisterLayoutInfo = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemTypeExtra,
|
||||
LayoutInfo
|
||||
>;
|
||||
|
||||
export const layoutInfoConfig: RegisterLayoutInfo = {
|
||||
key: ItemTypeExtra.LayoutInfo,
|
||||
selector: store => store.layoutInfo,
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
onBeforeSave: layoutInfo => ({
|
||||
layout_info: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.layoutInfo(layoutInfo),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 OnboardingInfo } from '@coze-arch/bot-api/playground_api';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import type { ExtendOnboardingContent } from '@/types/skill';
|
||||
import { useBotSkillStore } from '@/store/bot-skill';
|
||||
import type { BotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterOnboardingContent = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemType,
|
||||
ExtendOnboardingContent
|
||||
>;
|
||||
|
||||
export const onboardingConfig: RegisterOnboardingContent = {
|
||||
key: ItemType.ONBOARDING,
|
||||
selector: {
|
||||
deps: [state => state.onboardingContent],
|
||||
transformer: onboardingContent =>
|
||||
useBotSkillStore.getState().transformVo2Dto.onboarding(onboardingContent),
|
||||
},
|
||||
debounce: {
|
||||
default: DebounceTime.Immediate,
|
||||
prologue: DebounceTime.Long,
|
||||
suggested_questions: {
|
||||
arrayType: true,
|
||||
action: {
|
||||
N: DebounceTime.Immediate,
|
||||
D: DebounceTime.Immediate,
|
||||
E: DebounceTime.Long,
|
||||
},
|
||||
},
|
||||
},
|
||||
middleware: {
|
||||
onBeforeSave: (dataSource: OnboardingInfo) => ({
|
||||
onboarding_info: dataSource,
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { cloneDeep } from 'lodash-es';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import type { EnabledPluginApi } from '@/types/skill';
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterSystemContent = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemType,
|
||||
EnabledPluginApi[]
|
||||
>;
|
||||
|
||||
export const pluginConfig: RegisterSystemContent = {
|
||||
key: ItemType.APIINFO,
|
||||
selector: store => store.pluginApis,
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => {
|
||||
// 必须先深克隆,处理原数据会改动 store 的值
|
||||
const clonePluginApis = cloneDeep(dataSource);
|
||||
|
||||
const newPluginApis = clonePluginApis.map(item => {
|
||||
// ai生成动画仅生效一次,请求接口时删除
|
||||
delete item.autoAddCss;
|
||||
return item;
|
||||
});
|
||||
return {
|
||||
plugin_info_list: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.plugin(newPluginApis),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import type { BotSuggestionConfig } from '@/types/skill';
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterSuggestionConfig = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemType,
|
||||
BotSuggestionConfig
|
||||
>;
|
||||
|
||||
export const suggestionConfig: RegisterSuggestionConfig = {
|
||||
key: ItemType.SUGGESTREPLY,
|
||||
selector: store => store.suggestionConfig,
|
||||
debounce: {
|
||||
default: DebounceTime.Immediate,
|
||||
customized_suggest_prompt: DebounceTime.Long,
|
||||
},
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => ({
|
||||
suggest_reply_info: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.suggestionConfig(dataSource),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterTaskInfo = HostedObserverConfig<BotSkillStore, ItemType, boolean>;
|
||||
|
||||
export const taskInfoConfig: RegisterTaskInfo = {
|
||||
key: ItemType.TASK,
|
||||
selector: store => store.taskInfo.user_task_allowed,
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => ({
|
||||
task_info: useBotSkillStore.getState().transformVo2Dto.task({
|
||||
user_task_allowed: dataSource,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 { REPORT_EVENTS as ReportEventNames } from '@coze-arch/report-events';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
import { CustomError } from '@coze-arch/bot-error';
|
||||
|
||||
import { uniqMemoryList } from '@/utils/uniq-memory-list';
|
||||
import { type VariableItem, VariableKeyErrType } from '@/types/skill';
|
||||
import { usePageRuntimeStore } from '@/store/page-runtime';
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterVariables = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemType,
|
||||
VariableItem[]
|
||||
>;
|
||||
|
||||
export const variablesConfig: RegisterVariables = {
|
||||
key: ItemType.PROFILEMEMORY,
|
||||
selector: store => store.variables,
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => {
|
||||
const { editable } = usePageRuntimeStore.getState();
|
||||
|
||||
const filteredVariables = uniqMemoryList(dataSource).filter(i => {
|
||||
const errType = i?.errType || VariableKeyErrType.KEY_CHECK_PASS;
|
||||
|
||||
return errType > VariableKeyErrType.KEY_CHECK_PASS;
|
||||
});
|
||||
|
||||
if (!filteredVariables.length && editable) {
|
||||
return {
|
||||
variable_list: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.variables(dataSource),
|
||||
};
|
||||
}
|
||||
throw new CustomError(
|
||||
ReportEventNames.parmasValidation,
|
||||
'botSkill.variables return nothing',
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { merge } from 'lodash-es';
|
||||
import { type BotInfoForUpdate } from '@coze-arch/idl/playground_api';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import { type TTSInfo, type VoicesInfo } from '@/types/skill';
|
||||
import { transformVo2Dto } from '@/store/bot-skill/transform';
|
||||
import { type BotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
interface Values {
|
||||
voicesInfo: VoicesInfo;
|
||||
tts: TTSInfo;
|
||||
}
|
||||
|
||||
type RegisterVariables = HostedObserverConfig<BotSkillStore, ItemType, Values>;
|
||||
|
||||
export const voicesInfoConfig: RegisterVariables = {
|
||||
key: ItemType.PROFILEMEMORY,
|
||||
selector: store => ({ voicesInfo: store.voicesInfo, tts: store.tts }),
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
// ! any warning 改动的时候要仔细
|
||||
onBeforeSave: (
|
||||
values: Values,
|
||||
): Pick<Required<BotInfoForUpdate>, 'voices_info'> => ({
|
||||
voices_info: merge(
|
||||
{},
|
||||
transformVo2Dto.tts(values.tts),
|
||||
transformVo2Dto.voicesInfo(values.voicesInfo),
|
||||
),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 { cloneDeep, uniqBy } from 'lodash-es';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import type { WorkFlowItemType } from '@/types/skill';
|
||||
import { type BotSkillStore, useBotSkillStore } from '@/store/bot-skill';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterWorkflows = HostedObserverConfig<
|
||||
BotSkillStore,
|
||||
ItemType,
|
||||
WorkFlowItemType[]
|
||||
>;
|
||||
|
||||
export const workflowsConfig: RegisterWorkflows = {
|
||||
key: ItemType.WORKFLOW,
|
||||
selector: store => store.workflows,
|
||||
debounce: DebounceTime.Immediate,
|
||||
middleware: {
|
||||
onBeforeSave: (dataSource: WorkFlowItemType[]) => {
|
||||
const workflowsToBackend = cloneDeep(dataSource);
|
||||
|
||||
const filterList = uniqBy(workflowsToBackend, 'workflow_id').map(v => {
|
||||
// 解决加载图标的时候由于图标链接失效而报错,不在这里保存会失效的workflow的plugin_icon,而是每次都拉取最新的有效的图标链接
|
||||
v.plugin_icon = '';
|
||||
return v;
|
||||
});
|
||||
return {
|
||||
workflow_info_list: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.workflow(filterList),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 { AutosaveManager } from '@coze-studio/autosave';
|
||||
|
||||
import { useBotSkillStore, type BotSkillStore } from '@/store/bot-skill';
|
||||
import { type BizKey, type ScopeStateType } from '@/save-manager/types';
|
||||
|
||||
import { saveRequest } from '../request';
|
||||
import { registers } from './configs';
|
||||
|
||||
export const botSkillSaveManager = new AutosaveManager<
|
||||
BotSkillStore,
|
||||
BizKey,
|
||||
ScopeStateType
|
||||
>({
|
||||
store: useBotSkillStore,
|
||||
registers,
|
||||
saveRequest,
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { personaSaveManager } from './persona';
|
||||
import { modelSaveManager } from './model';
|
||||
import { botSkillSaveManager } from './bot-skill';
|
||||
|
||||
const managers = [personaSaveManager, botSkillSaveManager, modelSaveManager];
|
||||
|
||||
export const autosaveManager = {
|
||||
start: () => {
|
||||
console.log('start:>>');
|
||||
managers.forEach(manager => {
|
||||
manager.start();
|
||||
});
|
||||
},
|
||||
close: () => {
|
||||
console.log('close:>>');
|
||||
managers.forEach(manager => {
|
||||
manager.close();
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 ModelInfo } from '@coze-arch/bot-api/developer_api';
|
||||
import { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import { type ModelStore, useModelStore } from '@/store/model';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterSystemContent = HostedObserverConfig<
|
||||
ModelStore,
|
||||
ItemType,
|
||||
ModelInfo
|
||||
>;
|
||||
|
||||
export const modelConfig: RegisterSystemContent = {
|
||||
key: ItemType.OTHERINFO,
|
||||
selector: store => store.config,
|
||||
debounce: {
|
||||
default: DebounceTime.Immediate,
|
||||
temperature: DebounceTime.Medium,
|
||||
max_tokens: DebounceTime.Medium,
|
||||
'ShortMemPolicy.HistoryRound': DebounceTime.Medium,
|
||||
},
|
||||
middleware: {
|
||||
onBeforeSave: dataSource => ({
|
||||
model_info: useModelStore.getState().transformVo2Dto(dataSource),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 ModelInfo } from '@coze-arch/bot-api/developer_api';
|
||||
import { AutosaveManager } from '@coze-studio/autosave';
|
||||
|
||||
import { useModelStore, type ModelStore } from '@/store/model';
|
||||
import { type BizKey } from '@/save-manager/types';
|
||||
|
||||
import { saveRequest } from '../request';
|
||||
import { modelConfig } from './config';
|
||||
|
||||
export const modelSaveManager = new AutosaveManager<
|
||||
ModelStore,
|
||||
BizKey,
|
||||
ModelInfo
|
||||
>({
|
||||
store: useModelStore,
|
||||
registers: [modelConfig],
|
||||
saveRequest,
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import { ItemTypeExtra } from '../../types';
|
||||
import type { Agent } from '../../../types/agent';
|
||||
import { type MultiAgentStore } from '../../../store/multi-agent/store';
|
||||
|
||||
type RegisterSystemContent = HostedObserverConfig<
|
||||
MultiAgentStore,
|
||||
ItemTypeExtra,
|
||||
Agent
|
||||
>;
|
||||
|
||||
export const registerMultiAgentConfig: RegisterSystemContent = {
|
||||
key: ItemTypeExtra.MultiAgent,
|
||||
selector: state => state.agents?.[0],
|
||||
debounce: {
|
||||
default: DebounceTime.Immediate,
|
||||
description: DebounceTime.Long,
|
||||
'position.x': DebounceTime.Medium,
|
||||
'position.y': DebounceTime.Medium,
|
||||
'skills.knowledge.dataSetInfo.min_score': DebounceTime.Medium,
|
||||
'skills.knowledge.dataSetInfo.top_k': DebounceTime.Medium,
|
||||
'skills.knowledge.dataSetInfo.no_recall_reply_customize_prompt':
|
||||
DebounceTime.Long,
|
||||
'model.temperature': DebounceTime.Medium,
|
||||
'model.max_tokens': DebounceTime.Medium,
|
||||
'model.top_p': DebounceTime.Medium,
|
||||
'model.ShortMemPolicy.HistoryRound': DebounceTime.Medium,
|
||||
prompt: DebounceTime.Long, // agent 提示词
|
||||
'suggestion.customized_suggest_prompt': DebounceTime.Long,
|
||||
intents: { arrayType: true, action: { E: DebounceTime.Long } },
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
import { AutosaveManager, type SaveRequest } from '@coze-studio/autosave';
|
||||
|
||||
import { storage } from '@/utils/storage';
|
||||
import type { Agent } from '@/types/agent';
|
||||
import {
|
||||
type MultiAgentStore,
|
||||
useMultiAgentStore,
|
||||
} from '@/store/multi-agent/store';
|
||||
import { useBotInfoStore } from '@/store/bot-info';
|
||||
|
||||
import { saveFetcher } from '../../utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../../types';
|
||||
import { registerMultiAgentConfig } from './config';
|
||||
|
||||
const saveRequestAgent: SaveRequest<Agent, ItemTypeExtra> = async (
|
||||
payload: Agent,
|
||||
) =>
|
||||
await saveFetcher(() => {
|
||||
const params = useMultiAgentStore.getState().transformVo2Dto.agent(payload);
|
||||
return PlaygroundApi.UpdateAgentV2({
|
||||
...params,
|
||||
id: payload.id,
|
||||
bot_id: useBotInfoStore.getState().botId,
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
base_commit_version: storage.baseVersion,
|
||||
});
|
||||
}, ItemTypeExtra.MultiAgent);
|
||||
|
||||
export const multiAgentSaveManager = new AutosaveManager<
|
||||
MultiAgentStore,
|
||||
ItemTypeExtra,
|
||||
Agent
|
||||
>({
|
||||
store: useMultiAgentStore,
|
||||
registers: [registerMultiAgentConfig],
|
||||
saveRequest: saveRequestAgent,
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { DebounceTime, type HostedObserverConfig } from '@coze-studio/autosave';
|
||||
|
||||
import {
|
||||
usePersonaStore,
|
||||
type PersonaStore,
|
||||
type RequiredBotPrompt,
|
||||
} from '@/store/persona';
|
||||
import { ItemType } from '@/save-manager/types';
|
||||
|
||||
type RegisterSystemContent = HostedObserverConfig<
|
||||
PersonaStore,
|
||||
ItemType,
|
||||
RequiredBotPrompt
|
||||
>;
|
||||
|
||||
export const personaConfig: RegisterSystemContent = {
|
||||
key: ItemType.SYSTEMINFO,
|
||||
selector: state => state.systemMessage,
|
||||
debounce: () => {
|
||||
const { systemMessage } = usePersonaStore.getState();
|
||||
const { isOptimize } = systemMessage;
|
||||
|
||||
console.log('systemMessage:>>', systemMessage);
|
||||
console.log('isOptimize:>>', isOptimize);
|
||||
if (isOptimize) {
|
||||
return DebounceTime.Immediate;
|
||||
}
|
||||
return DebounceTime.Long;
|
||||
},
|
||||
middleware: {
|
||||
onBeforeSave: nextState => ({
|
||||
prompt_info: usePersonaStore.getState().transformVo2Dto(nextState),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 { AutosaveManager } from '@coze-studio/autosave';
|
||||
|
||||
import {
|
||||
usePersonaStore,
|
||||
type PersonaStore,
|
||||
type RequiredBotPrompt,
|
||||
} from '@/store/persona';
|
||||
import { type ItemType } from '@/save-manager/types';
|
||||
|
||||
import { saveRequest } from '../request';
|
||||
import { personaConfig } from './config';
|
||||
|
||||
export const personaSaveManager = new AutosaveManager<
|
||||
PersonaStore,
|
||||
ItemType,
|
||||
RequiredBotPrompt
|
||||
>({
|
||||
store: usePersonaStore,
|
||||
registers: [personaConfig],
|
||||
saveRequest,
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
import { type SaveRequest } from '@coze-studio/autosave';
|
||||
|
||||
import { storage } from '@/utils/storage';
|
||||
import { useBotInfoStore } from '@/store/bot-info';
|
||||
import { type BizKey, type ScopeStateType } from '@/save-manager/types';
|
||||
|
||||
import { saveFetcher } from '../utils/save-fetcher';
|
||||
|
||||
/**
|
||||
* 自动保存统一请求方法
|
||||
*/
|
||||
export const saveRequest: SaveRequest<ScopeStateType, BizKey> = async (
|
||||
payload: ScopeStateType,
|
||||
itemType: BizKey,
|
||||
) => {
|
||||
const { botId } = useBotInfoStore.getState();
|
||||
|
||||
await saveFetcher(
|
||||
async () =>
|
||||
await PlaygroundApi.UpdateDraftBotInfoAgw({
|
||||
bot_info: {
|
||||
bot_id: botId,
|
||||
...payload,
|
||||
},
|
||||
base_commit_version: storage.baseVersion,
|
||||
}),
|
||||
itemType,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 { autosaveManager } from './auto-save/index';
|
||||
export { personaSaveManager } from './auto-save/persona';
|
||||
export { botSkillSaveManager } from './auto-save/bot-skill';
|
||||
export { multiAgentSaveManager } from './auto-save/multi-agent';
|
||||
export { modelSaveManager } from './auto-save/model';
|
||||
export { registerMultiAgentConfig } from './auto-save/multi-agent/config';
|
||||
export { updateBotRequest } from './utils/save-fetcher';
|
||||
|
||||
export { saveFileboxMode } from './manual-save/filebox';
|
||||
export {
|
||||
saveConnectorType,
|
||||
saveDeleteAgents,
|
||||
saveUpdateAgents,
|
||||
saveMultiAgentData,
|
||||
} from './manual-save/multi-agent';
|
||||
export { saveTableMemory } from './manual-save/memory-table';
|
||||
export { saveTTSConfig } from './manual-save/tts';
|
||||
export { saveDevHooksConfig } from './manual-save/dev-hooks';
|
||||
export { updateShortcutSort } from './manual-save/shortcuts';
|
||||
export { updateQueryCollect } from './manual-save/query-collect';
|
||||
export { saveTimeCapsule } from './manual-save/time-capsule';
|
||||
export { getBotDetailDtoInfo } from './utils/bot-dto-info';
|
||||
export { ItemTypeExtra } from './types';
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 HookInfo } from '@coze-arch/idl/playground_api';
|
||||
import { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
|
||||
export const saveDevHooksConfig = async (hooksInfo: HookInfo) =>
|
||||
saveFetcher(
|
||||
() =>
|
||||
updateBotRequest({
|
||||
hook_info: hooksInfo,
|
||||
}),
|
||||
ItemType.HOOKINFO,
|
||||
);
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 FileboxInfoMode,
|
||||
ItemType,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useBotSkillStore } from '@/store/bot-skill';
|
||||
|
||||
import { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
|
||||
export const saveFileboxMode = async (nextMode: FileboxInfoMode) => {
|
||||
const { filebox: fileboxConfig } = useBotSkillStore.getState();
|
||||
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
updateBotRequest({
|
||||
filebox_info: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.filebox(
|
||||
fileboxConfig?.mode ? fileboxConfig : { mode: nextMode },
|
||||
),
|
||||
}),
|
||||
|
||||
ItemType.TABLE,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { useBotSkillStore } from '@/store/bot-skill';
|
||||
|
||||
import { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
|
||||
export async function saveTableMemory() {
|
||||
const { databaseList } = useBotSkillStore.getState();
|
||||
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
updateBotRequest({
|
||||
database_list: useBotSkillStore
|
||||
.getState()
|
||||
.transformVo2Dto.databaseList(databaseList),
|
||||
}),
|
||||
ItemType.TABLE,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { type MultiAgentConnectorType } from '@coze-arch/bot-api/playground_api';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
import { type LineType } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { storage } from '@/utils/storage';
|
||||
import type { Agent } from '@/types/agent';
|
||||
import { useMultiAgentStore } from '@/store/multi-agent';
|
||||
import { useBotInfoStore } from '@/store/bot-info';
|
||||
|
||||
import { saveFetcher } from '../utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../types';
|
||||
|
||||
// skill结构化的接口
|
||||
export async function saveUpdateAgents(agent: Agent) {
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
PlaygroundApi.UpdateAgentV2({
|
||||
...useMultiAgentStore.getState().transformVo2Dto.agent(agent),
|
||||
bot_id: useBotInfoStore.getState().botId,
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
base_commit_version: storage.baseVersion,
|
||||
}),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
}
|
||||
|
||||
export async function saveDeleteAgents(deleteAgentId: string) {
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
PlaygroundApi.UpdateAgentV2({
|
||||
bot_id: useBotInfoStore.getState().botId,
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
id: deleteAgentId,
|
||||
is_delete: true,
|
||||
base_commit_version: storage.baseVersion,
|
||||
}),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
}
|
||||
|
||||
export function saveDeleteAgentsV3(deleteAgentId: string) {
|
||||
return saveFetcher(
|
||||
() =>
|
||||
PlaygroundApi.UpdateAgentV2({
|
||||
bot_id: useBotInfoStore.getState().botId,
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
id: deleteAgentId,
|
||||
is_delete: true,
|
||||
base_commit_version: storage.baseVersion,
|
||||
}),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
}
|
||||
|
||||
export async function saveMultiAgentData() {
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
PlaygroundApi.UpdateMultiAgent({
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
bot_id: useBotInfoStore.getState().botId,
|
||||
session_type: useMultiAgentStore.getState().chatModeConfig.type,
|
||||
base_commit_version: storage.baseVersion,
|
||||
}),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
}
|
||||
|
||||
export async function saveConnectorType(connectorType: LineType) {
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
PlaygroundApi.UpdateMultiAgent({
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
bot_id: useBotInfoStore.getState().botId,
|
||||
connector_type: connectorType as unknown as MultiAgentConnectorType,
|
||||
base_commit_version: storage.baseVersion,
|
||||
}),
|
||||
ItemTypeExtra.ConnectorType,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 UpdateDraftBotInfoAgwResponse } from '@coze-arch/idl/playground_api';
|
||||
import { type UserQueryCollectConf } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../types';
|
||||
|
||||
export const updateQueryCollect = async (
|
||||
queryCollectConf: UserQueryCollectConf,
|
||||
) => {
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
let updateResult: UpdateDraftBotInfoAgwResponse = null;
|
||||
|
||||
await saveFetcher(async () => {
|
||||
const res = await updateBotRequest({
|
||||
user_query_collect_conf: queryCollectConf,
|
||||
});
|
||||
|
||||
updateResult = res;
|
||||
return res;
|
||||
}, ItemTypeExtra.QueryCollect);
|
||||
return updateResult;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../types';
|
||||
|
||||
export const updateShortcutSort = async (shortcutSort: string[]) =>
|
||||
await saveFetcher(
|
||||
() =>
|
||||
updateBotRequest({
|
||||
shortcut_sort: shortcutSort,
|
||||
}),
|
||||
|
||||
ItemTypeExtra.Shortcut,
|
||||
);
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { useBotSkillStore } from '@/store/bot-skill';
|
||||
|
||||
import { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../types';
|
||||
|
||||
export async function saveTimeCapsule() {
|
||||
const { timeCapsule, transformVo2Dto } = useBotSkillStore.getState();
|
||||
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
updateBotRequest({
|
||||
bot_tag_info: transformVo2Dto.timeCapsule({
|
||||
time_capsule_mode: timeCapsule.time_capsule_mode,
|
||||
disable_prompt_calling: timeCapsule.disable_prompt_calling,
|
||||
time_capsule_time_to_live: timeCapsule.time_capsule_time_to_live,
|
||||
}),
|
||||
}),
|
||||
ItemTypeExtra.TimeCapsule,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 { cloneDeep, merge } from 'lodash-es';
|
||||
|
||||
import { useBotSkillStore } from '@/store/bot-skill';
|
||||
|
||||
import { saveFetcher, updateBotRequest } from '../utils/save-fetcher';
|
||||
import { ItemTypeExtra } from '../types';
|
||||
|
||||
export const saveTTSConfig = async () => {
|
||||
const { tts, transformVo2Dto, voicesInfo } = useBotSkillStore.getState();
|
||||
const {
|
||||
muted = false,
|
||||
close_voice_call = false,
|
||||
i18n_lang_voice = {},
|
||||
autoplay = false,
|
||||
autoplay_voice = {},
|
||||
i18n_lang_voice_str,
|
||||
} = tts;
|
||||
|
||||
const cloneVoiceInfo = {
|
||||
muted,
|
||||
close_voice_call,
|
||||
i18n_lang_voice: cloneDeep(i18n_lang_voice),
|
||||
autoplay,
|
||||
autoplay_voice: cloneDeep(autoplay_voice),
|
||||
i18n_lang_voice_str: cloneDeep(i18n_lang_voice_str),
|
||||
};
|
||||
|
||||
return await saveFetcher(
|
||||
() =>
|
||||
updateBotRequest({
|
||||
voices_info: merge(
|
||||
{},
|
||||
transformVo2Dto.tts(cloneVoiceInfo),
|
||||
transformVo2Dto.voicesInfo(voicesInfo),
|
||||
),
|
||||
}),
|
||||
|
||||
ItemTypeExtra.TTS,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 @typescript-eslint/no-explicit-any */
|
||||
import { ItemType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
// 走自动保存update接口的scope服务端会维护ItemType,其他scope前端维护在ItemTypeExtra中
|
||||
export enum ItemTypeExtra {
|
||||
MultiAgent = 1024,
|
||||
TTS = 1025,
|
||||
ConnectorType = 1026,
|
||||
ChatBackGround = 1027,
|
||||
Shortcut = 1028,
|
||||
QueryCollect = 1029,
|
||||
LayoutInfo = 1030,
|
||||
TaskInfo = 1031,
|
||||
TimeCapsule = 1032,
|
||||
}
|
||||
|
||||
export type BizKey = ItemType | ItemTypeExtra | undefined;
|
||||
export type ScopeStateType = any;
|
||||
export { ItemType };
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 { merge } from 'lodash-es';
|
||||
import {
|
||||
REPORT_EVENTS as ReportEventNames,
|
||||
createReportEvent,
|
||||
} from '@coze-arch/report-events';
|
||||
import {
|
||||
type VoicesInfo,
|
||||
type BotInfoForUpdate,
|
||||
} from '@coze-arch/idl/playground_api';
|
||||
import { BotMode } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { type RemoveOptional, type UnionUndefined } from '@/types/utils';
|
||||
import { useQueryCollectStore } from '@/store/query-collect';
|
||||
import { usePersonaStore } from '@/store/persona';
|
||||
import { useMultiAgentStore } from '@/store/multi-agent';
|
||||
import { useModelStore } from '@/store/model';
|
||||
import { useBotSkillStore } from '@/store/bot-skill';
|
||||
import { useBotInfoStore } from '@/store/bot-info';
|
||||
|
||||
export const getBotDetailDtoInfo = () => {
|
||||
const { mode } = useBotInfoStore.getState();
|
||||
const botSkill = useBotSkillStore.getState();
|
||||
const multiAgent = useMultiAgentStore.getState();
|
||||
const model = useModelStore.getState();
|
||||
const persona = usePersonaStore.getState();
|
||||
const queryCollect = useQueryCollectStore.getState();
|
||||
|
||||
const isMulti = mode === BotMode.MultiMode;
|
||||
const {
|
||||
knowledge,
|
||||
variables,
|
||||
workflows,
|
||||
taskInfo,
|
||||
suggestionConfig,
|
||||
onboardingContent,
|
||||
pluginApis,
|
||||
backgroundImageInfoList,
|
||||
shortcut,
|
||||
tts,
|
||||
timeCapsule,
|
||||
filebox,
|
||||
devHooks,
|
||||
voicesInfo,
|
||||
} = botSkill;
|
||||
|
||||
const { agents } = multiAgent;
|
||||
|
||||
const reportEvent = createReportEvent({
|
||||
eventName: ReportEventNames.botDebugSaveAll,
|
||||
});
|
||||
|
||||
try {
|
||||
const botSkillInfo: Omit<BotInfoForUpdate, 'voices_info'> & {
|
||||
voices_info: UnionUndefined<RemoveOptional<VoicesInfo>>;
|
||||
} = {
|
||||
prompt_info: persona.transformVo2Dto(persona.systemMessage),
|
||||
model_info: model.transformVo2Dto(model.config),
|
||||
plugin_info_list: isMulti
|
||||
? undefined
|
||||
: botSkill.transformVo2Dto.plugin(pluginApis),
|
||||
workflow_info_list: isMulti
|
||||
? undefined
|
||||
: botSkill.transformVo2Dto.workflow(workflows),
|
||||
knowledge: isMulti
|
||||
? undefined
|
||||
: botSkill.transformVo2Dto.knowledge(knowledge),
|
||||
variable_list: botSkill.transformVo2Dto.variables(variables),
|
||||
task_info: botSkill.transformVo2Dto.task(taskInfo),
|
||||
suggest_reply_info:
|
||||
botSkill.transformVo2Dto.suggestionConfig(suggestionConfig),
|
||||
onboarding_info: botSkill.transformVo2Dto.onboarding(onboardingContent),
|
||||
background_image_info_list: backgroundImageInfoList,
|
||||
shortcut_sort: botSkill.transformVo2Dto.shortcut(shortcut),
|
||||
voices_info: merge(
|
||||
{},
|
||||
botSkill.transformVo2Dto.tts(tts),
|
||||
botSkill.transformVo2Dto.voicesInfo(voicesInfo),
|
||||
),
|
||||
bot_tag_info: botSkill.transformVo2Dto.timeCapsule(timeCapsule),
|
||||
filebox_info: botSkill.transformVo2Dto.filebox(filebox),
|
||||
hook_info: isMulti ? undefined : devHooks,
|
||||
user_query_collect_conf: queryCollect.transformVo2Dto(queryCollect),
|
||||
agents: isMulti
|
||||
? agents.map(item => multiAgent.transformVo2Dto.agent(item))
|
||||
: undefined,
|
||||
};
|
||||
|
||||
reportEvent.success();
|
||||
|
||||
return { botSkillInfo };
|
||||
} catch (e) {
|
||||
reportEvent.error({
|
||||
reason: 'bot debug save all fail',
|
||||
error: e instanceof Error ? e : void 0,
|
||||
});
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 dayjs from 'dayjs';
|
||||
import { REPORT_EVENTS as ReportEventNames } from '@coze-arch/report-events';
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
import { type BotInfoForUpdate } from '@coze-arch/idl/playground_api';
|
||||
import { BotPageFromEnum } from '@coze-arch/bot-typings/common';
|
||||
import { type UpdateDraftBotResponse } from '@coze-arch/bot-api/developer_api';
|
||||
import { PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { useBotInfoStore } from '@/store/bot-info';
|
||||
|
||||
import { type BizKey } from '../types';
|
||||
import { storage } from '../../utils/storage';
|
||||
import { usePageRuntimeStore } from '../../store/page-runtime';
|
||||
import { useCollaborationStore } from '../../store/collaboration';
|
||||
|
||||
export async function saveFetcher(
|
||||
saveRequest: () => Promise<UpdateDraftBotResponse>,
|
||||
scopeKey: BizKey,
|
||||
) {
|
||||
const { editable, isPreview, pageFrom, init, setPageRuntimeByImmer } =
|
||||
usePageRuntimeStore.getState();
|
||||
|
||||
const { setCollaborationByImmer } = useCollaborationStore.getState();
|
||||
const isReadonly = () =>
|
||||
!editable || isPreview || pageFrom === BotPageFromEnum.Explore;
|
||||
|
||||
if (isReadonly() || !init) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setPageRuntimeByImmer(state => {
|
||||
state.savingInfo.saving = true;
|
||||
state.savingInfo.scopeKey = scopeKey ? String(scopeKey) : '';
|
||||
});
|
||||
|
||||
const res = await saveRequest();
|
||||
|
||||
setPageRuntimeByImmer(state => {
|
||||
state.savingInfo = {
|
||||
saving: false,
|
||||
time: dayjs().format('HH:mm:ss'),
|
||||
};
|
||||
});
|
||||
|
||||
if (res) {
|
||||
setPageRuntimeByImmer(state => {
|
||||
state.hasUnpublishChange = res.data.has_change ?? false;
|
||||
});
|
||||
setCollaborationByImmer(state => {
|
||||
state.sameWithOnline = res.data.same_with_online ?? false;
|
||||
if (state.branch && res.data.branch) {
|
||||
state.branch = res.data.branch;
|
||||
}
|
||||
});
|
||||
}
|
||||
reporter.successEvent({
|
||||
eventName: ReportEventNames.AutosaveSuccess,
|
||||
meta: { itemType: scopeKey },
|
||||
});
|
||||
} catch (e) {
|
||||
reporter.errorEvent({
|
||||
eventName: ReportEventNames.AutosaveError,
|
||||
error: e as Error,
|
||||
meta: { itemType: scopeKey },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新bot草稿信息的结构
|
||||
* @returns 根据标记返回使用不同请求体的更新bot
|
||||
*/
|
||||
export function updateBotRequest(structPayload: BotInfoForUpdate) {
|
||||
const { botId } = useBotInfoStore.getState();
|
||||
return PlaygroundApi.UpdateDraftBotInfoAgw({
|
||||
bot_info: {
|
||||
bot_id: botId,
|
||||
...structPayload,
|
||||
},
|
||||
base_commit_version: storage.baseVersion,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 GetBotVersionInfoData,
|
||||
GetBotVersionScene,
|
||||
type GetDraftBotInfoAgwData,
|
||||
} from '@coze-arch/idl/playground_api';
|
||||
import {
|
||||
MonetizationEntityType,
|
||||
type BotMonetizationConfigData,
|
||||
} from '@coze-arch/bot-api/benefit';
|
||||
import { benefitApi, PlaygroundApi } from '@coze-arch/bot-api';
|
||||
|
||||
export const getBotDataService = async (params: {
|
||||
scene: 'bot' | 'market';
|
||||
botId: string;
|
||||
customVersion?: string;
|
||||
botInfoVersion: string;
|
||||
}): Promise<{
|
||||
botData: GetDraftBotInfoAgwData;
|
||||
monetizeConfig: BotMonetizationConfigData | undefined;
|
||||
}> => {
|
||||
const { scene, botId, customVersion, botInfoVersion } = params;
|
||||
if (scene === 'bot') {
|
||||
const [botInfoResp, monetizeConfigResp] = await getBotSceneData({
|
||||
botId,
|
||||
version: customVersion ?? '',
|
||||
});
|
||||
return {
|
||||
botData: getCommonBotData(botInfoResp?.data ?? {}),
|
||||
monetizeConfig: monetizeConfigResp?.data,
|
||||
};
|
||||
}
|
||||
const botInfoResp = await getMarketSceneData({
|
||||
botId,
|
||||
version: botInfoVersion,
|
||||
});
|
||||
return {
|
||||
botData: getCommonBotData(botInfoResp?.data ?? {}),
|
||||
monetizeConfig: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const getBotSceneData = async (params: { botId: string; version: string }) => {
|
||||
const { botId, version } = params;
|
||||
return await Promise.all([
|
||||
PlaygroundApi.GetDraftBotInfoAgw({
|
||||
bot_id: botId,
|
||||
version,
|
||||
}),
|
||||
IS_OVERSEA
|
||||
? benefitApi.PublicGetBotMonetizationConfig({
|
||||
entity_id: botId,
|
||||
entity_type: MonetizationEntityType.Bot,
|
||||
})
|
||||
: Promise.resolve(undefined),
|
||||
]);
|
||||
};
|
||||
|
||||
const getMarketSceneData = async (params: {
|
||||
botId: string;
|
||||
version: string;
|
||||
}) => {
|
||||
const { botId, version } = params;
|
||||
return await PlaygroundApi.GetBotVersionInfo({
|
||||
bot_id: botId,
|
||||
version: version ?? '',
|
||||
scene: GetBotVersionScene.BotStore,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCommonBotData = (
|
||||
botData: GetDraftBotInfoAgwData | GetBotVersionInfoData,
|
||||
): GetDraftBotInfoAgwData => {
|
||||
let commonBotData: GetDraftBotInfoAgwData = {
|
||||
bot_info: {},
|
||||
};
|
||||
if ('bot_info' in botData) {
|
||||
commonBotData = botData;
|
||||
}
|
||||
if ('bot_version_info' in botData) {
|
||||
commonBotData = {
|
||||
bot_info: botData.bot_version_info?.common_bot_info ?? {},
|
||||
};
|
||||
}
|
||||
return commonBotData;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 { devtools, subscribeWithSelector } from 'zustand/middleware';
|
||||
import { create } from 'zustand';
|
||||
import { produce } from 'immer';
|
||||
import { type AuditInfo } from '@coze-arch/idl/playground_api';
|
||||
import { type GetDraftBotInfoAgwData } from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
import {
|
||||
type SetterAction,
|
||||
setterActionFactory,
|
||||
} from '../utils/setter-factory';
|
||||
|
||||
export const getDefaultAuditInfoStore = (): AuditInfoStore => ({
|
||||
audit_status: 1,
|
||||
});
|
||||
|
||||
export type AuditInfoStore = AuditInfo;
|
||||
|
||||
export interface AuditInfoAction {
|
||||
setAuditInfo: SetterAction<AuditInfoStore>;
|
||||
setAuditInfoByImmer: (update: (state: AuditInfoStore) => void) => void;
|
||||
initStore: (botData: GetDraftBotInfoAgwData) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
export const useAuditInfoStore = create<AuditInfoStore & AuditInfoAction>()(
|
||||
devtools(
|
||||
subscribeWithSelector((set, get) => ({
|
||||
...getDefaultAuditInfoStore(),
|
||||
setAuditInfo: setterActionFactory<AuditInfoStore>(set),
|
||||
setAuditInfoByImmer: update =>
|
||||
set(produce<AuditInfoStore>(auditInfo => update(auditInfo))),
|
||||
initStore: botData => {
|
||||
const { setAuditInfo } = get();
|
||||
botData && setAuditInfo(botData?.latest_audit_info ?? {});
|
||||
},
|
||||
clear: () => {
|
||||
set({ ...getDefaultAuditInfoStore() });
|
||||
},
|
||||
})),
|
||||
{
|
||||
enabled: IS_DEV_MODE,
|
||||
name: 'botStudio.botDetail.auditInfo',
|
||||
},
|
||||
),
|
||||
);
|
||||
160
frontend/packages/studio/stores/bot-detail/src/store/bot-info.ts
Normal file
160
frontend/packages/studio/stores/bot-detail/src/store/bot-info.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 { devtools, subscribeWithSelector } from 'zustand/middleware';
|
||||
import { create } from 'zustand';
|
||||
import { produce } from 'immer';
|
||||
import {
|
||||
type BotInfo,
|
||||
type GetDraftBotInfoAgwData,
|
||||
type UserInfo,
|
||||
type BusinessType,
|
||||
} from '@coze-arch/idl/playground_api';
|
||||
import {
|
||||
BotMarketStatus,
|
||||
BotMode,
|
||||
type ConnectorInfo,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import {
|
||||
type SetterAction,
|
||||
setterActionFactory,
|
||||
} from '../utils/setter-factory';
|
||||
|
||||
export const getDefaultBotInfoStore = (): BotInfoStore => ({
|
||||
botId: '',
|
||||
mode: BotMode.SingleMode,
|
||||
botMarketStatus: BotMarketStatus.Offline,
|
||||
name: '',
|
||||
description: '',
|
||||
icon_uri: '',
|
||||
icon_url: '',
|
||||
create_time: '',
|
||||
creator_id: '',
|
||||
update_time: '',
|
||||
connector_id: '',
|
||||
publisher: {},
|
||||
has_publish: false,
|
||||
connectors: [],
|
||||
publish_time: '',
|
||||
space_id: '',
|
||||
version: '',
|
||||
raw: {},
|
||||
});
|
||||
|
||||
/** 定义bot的基础信息*/
|
||||
export interface BotInfoStore {
|
||||
botId: string;
|
||||
/** 发布的业务线详情 */
|
||||
connectors: Array<ConnectorInfo>;
|
||||
/** for前端,发布时间 */
|
||||
publish_time: string;
|
||||
/** 空间id */
|
||||
space_id: string;
|
||||
/** 是否已发布 */
|
||||
has_publish: boolean;
|
||||
mode: BotMode;
|
||||
/** 最新发布版本时传发布人 */
|
||||
publisher: UserInfo;
|
||||
/** bot上架后的商品状态 */
|
||||
botMarketStatus: BotMarketStatus;
|
||||
/** bot名称 */
|
||||
name: string;
|
||||
/** bot描述 */
|
||||
description: string;
|
||||
/** bot 图标uri */
|
||||
icon_uri: string;
|
||||
/** bot 图标url */
|
||||
icon_url: string;
|
||||
/** 创建时间 */
|
||||
create_time: string;
|
||||
/** 创建人id */
|
||||
creator_id: string;
|
||||
/** 更新时间 */
|
||||
update_time: string;
|
||||
/** 业务线 */
|
||||
connector_id: string;
|
||||
/** multi agent mode agent信息 */
|
||||
// agents?: Array<Agent>;
|
||||
/** 版本,毫秒 */
|
||||
version: string;
|
||||
/** multi_agent结构体 */
|
||||
// multi_agent_info?: MultiAgentInfo;
|
||||
/** @ 保存了原始bot数据, readonly **/
|
||||
raw: BotInfo;
|
||||
/** 抖音分身应用id */
|
||||
appId?: string;
|
||||
/** 业务类型 默认0 分身业务1 */
|
||||
businessType?: BusinessType;
|
||||
}
|
||||
|
||||
export interface BotInfoAction {
|
||||
setBotInfo: SetterAction<BotInfoStore>;
|
||||
setBotInfoByImmer: (update: (state: BotInfoStore) => void) => void;
|
||||
transformVo2Dto: (data: GetDraftBotInfoAgwData) => BotInfoStore;
|
||||
initStore: (data: GetDraftBotInfoAgwData) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
export const useBotInfoStore = create<BotInfoStore & BotInfoAction>()(
|
||||
devtools(
|
||||
subscribeWithSelector((set, get) => ({
|
||||
...getDefaultBotInfoStore(),
|
||||
setBotInfo: setterActionFactory<BotInfoStore>(set),
|
||||
setBotInfoByImmer: update =>
|
||||
set(produce<BotInfoStore>(state => update(state))),
|
||||
// eslint-disable-next-line complexity
|
||||
transformVo2Dto: data => {
|
||||
// 将botData转化为botInfoStore, 只取BotInfoStore中的固定字段
|
||||
const botInfo = data.bot_info ?? {};
|
||||
return {
|
||||
botId: botInfo?.bot_id ?? '',
|
||||
mode: botInfo?.bot_mode ?? BotMode.SingleMode,
|
||||
botMarketStatus: data.bot_market_status ?? BotMarketStatus.Offline,
|
||||
name: botInfo.name ?? '',
|
||||
description: botInfo.description ?? '',
|
||||
icon_uri: botInfo.icon_uri ?? '',
|
||||
icon_url: botInfo.icon_url ?? '',
|
||||
create_time: botInfo.create_time ?? '',
|
||||
creator_id: botInfo.creator_id ?? '',
|
||||
update_time: botInfo.update_time ?? '',
|
||||
connector_id: botInfo.connector_id ?? '',
|
||||
version: botInfo.version ?? '',
|
||||
publisher: data.publisher ?? {},
|
||||
has_publish: data.has_publish ?? false,
|
||||
connectors: data.connectors ?? [],
|
||||
publish_time: data.publish_time ?? '',
|
||||
space_id: data.space_id ?? '',
|
||||
businessType: botInfo.business_type,
|
||||
appId: data.app_id ?? '',
|
||||
raw: botInfo,
|
||||
};
|
||||
},
|
||||
initStore: data => {
|
||||
const { transformVo2Dto } = get();
|
||||
const transformedData = transformVo2Dto(data);
|
||||
set(transformedData);
|
||||
},
|
||||
clear: () => {
|
||||
set({ ...getDefaultBotInfoStore() });
|
||||
},
|
||||
})),
|
||||
{
|
||||
enabled: IS_DEV_MODE,
|
||||
name: 'botStudio.botDetail.botInfo',
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { I18n } from '@coze-arch/i18n';
|
||||
import { BotTableRWMode } from '@coze-arch/bot-api/memory';
|
||||
import {
|
||||
type BackgroundImageInfo,
|
||||
SuggestedQuestionsShowMode,
|
||||
SuggestReplyMode,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import {
|
||||
type VoicesInfo,
|
||||
type BotSuggestionConfig,
|
||||
type DatabaseInfo,
|
||||
type ExtendOnboardingContent,
|
||||
type TimeCapsuleConfig,
|
||||
type TTSInfo,
|
||||
} from '../../types/skill';
|
||||
|
||||
export const DEFAULT_KNOWLEDGE_CONFIG = () => {
|
||||
const baseConfig = {
|
||||
top_k: 3,
|
||||
min_score: 0.5,
|
||||
auto: true,
|
||||
search_strategy: 0,
|
||||
show_source: false,
|
||||
};
|
||||
return baseConfig;
|
||||
};
|
||||
|
||||
export const DEFAULT_BOT_NODE_SUGGESTION_CONFIG = (): BotSuggestionConfig => ({
|
||||
suggest_reply_mode: SuggestReplyMode.UseOriginBotMode,
|
||||
customized_suggest_prompt: '',
|
||||
});
|
||||
|
||||
export const DEFAULT_SUGGESTION_PROMPT = () =>
|
||||
IS_OVERSEA
|
||||
? I18n.t('bot_suggestion_customize_default_gpt')
|
||||
: I18n.t('bot_suggestion_customize_default_seed');
|
||||
|
||||
export const DEFAULT_ONBOARDING_CONFIG = (): ExtendOnboardingContent => ({
|
||||
prologue: '',
|
||||
suggested_questions: [],
|
||||
suggested_questions_show_mode: SuggestedQuestionsShowMode.Random,
|
||||
});
|
||||
|
||||
export const DEFAULT_SUGGESTION_CONFIG = (): BotSuggestionConfig => ({
|
||||
suggest_reply_mode: SuggestReplyMode.WithDefaultPrompt,
|
||||
customized_suggest_prompt: '',
|
||||
});
|
||||
|
||||
export const DEFAULT_BACKGROUND_IMAGE_LIST = (): BackgroundImageInfo[] => [];
|
||||
export const DEFAULT_DATABASE = (): DatabaseInfo => ({
|
||||
tableId: '',
|
||||
name: '',
|
||||
desc: '',
|
||||
icon_uri: '',
|
||||
readAndWriteMode: BotTableRWMode.LimitedReadWrite,
|
||||
tableMemoryList: [],
|
||||
});
|
||||
export const DEFAULT_TTS_CONFIG = (): TTSInfo => ({
|
||||
muted: false,
|
||||
close_voice_call: false,
|
||||
i18n_lang_voice: {},
|
||||
autoplay: false,
|
||||
autoplay_voice: {},
|
||||
tag_list: [],
|
||||
debugVoice: [],
|
||||
i18n_lang_voice_str: {},
|
||||
});
|
||||
|
||||
export const DEFAULT_TIME_CAPSULE_CONFIG = (): TimeCapsuleConfig => ({
|
||||
time_capsule_mode: 0,
|
||||
disable_prompt_calling: 0, // 默认支持在prompt调用
|
||||
time_capsule_time_to_live: '0',
|
||||
});
|
||||
|
||||
export const DEFAULT_SHORTCUT_CONFIG = () => ({
|
||||
shortcut_list: [],
|
||||
shortcut_sort: [],
|
||||
});
|
||||
|
||||
export const DEFAULT_VOICES_INFO: () => VoicesInfo = () => ({
|
||||
defaultUserInputType: undefined,
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user