@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
248 lines (227 loc) • 7.54 kB
text/typescript
import { LobeChatPluginManifest, LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
import { describe, expect, it } from 'vitest';
import { initialState } from '../initialState';
import { ToolStoreState } from '../initialState';
import { toolSelectors } from './tool';
const mockState = {
...initialState,
installedPlugins: [
{
identifier: 'plugin-1',
manifest: {
identifier: 'plugin-1',
api: [{ name: 'api-1' }],
meta: { title: 'Plugin 1', description: 'Plugin 1 description' },
} as LobeChatPluginManifest,
type: 'plugin',
},
{
identifier: 'plugin-2',
manifest: {
identifier: 'plugin-2',
api: [{ name: 'api-2' }],
} as LobeChatPluginManifest,
type: 'plugin',
},
{
identifier: 'plugin-3',
manifest: {
identifier: 'plugin-3',
api: [
{
name: 'api-3',
url: 'bac',
description: '123123',
parameters: { type: 'object', properties: { a: { type: 'string' } } },
},
],
},
type: 'customPlugin',
},
],
builtinTools: [
{
type: 'builtin',
identifier: 'builtin-1',
manifest: {
identifier: 'builtin-1',
api: [{ name: 'builtin-api-1' }],
meta: { title: 'Builtin 1', description: 'Builtin 1 description' },
} as LobeChatPluginManifest,
},
],
pluginInstallLoading: {
'plugin-1': false,
'plugin-2': true,
},
} as ToolStoreState;
describe('toolSelectors', () => {
describe('enabledSchema', () => {
it('enabledSchema should return correct ChatCompletionFunctions array', () => {
const result = toolSelectors.enabledSchema(['plugin-1', 'plugin-2'])(mockState);
expect(result).toEqual([
{
type: 'function',
function: {
name: 'plugin-1____api-1',
},
},
{
type: 'function',
function: {
name: 'plugin-2____api-2',
},
},
]);
});
it('enabledSchema should return with standalone plugin', () => {
const result = toolSelectors.enabledSchema(['plugin-4'])({
...mockState,
installedPlugins: [
...mockState.installedPlugins,
{
identifier: 'plugin-4',
manifest: {
identifier: 'plugin-4',
api: [{ name: 'api-4' }],
type: 'standalone',
},
type: 'plugin',
},
],
} as ToolStoreState);
expect(result).toEqual([
{
type: 'function',
function: {
name: 'plugin-4____api-4____standalone',
},
},
]);
});
it('enabledSchema should return md5 hash apiName', () => {
const result = toolSelectors.enabledSchema(['long-long-plugin-with-id'])({
...mockState,
installedPlugins: [
...mockState.installedPlugins,
{
identifier: 'long-long-plugin-with-id',
manifest: {
identifier: 'long-long-plugin-with-id',
api: [{ name: 'long-long-manifest-long-long-apiName' }],
},
type: 'plugin',
},
],
} as ToolStoreState);
expect(result).toEqual([
{
type: 'function',
function: {
name: 'long-long-plugin-with-id____MD5HASH_396eae4c671da3fb642c49ad2b9e8790',
},
},
]);
});
it('enabledSchema should return empty', () => {
const result = toolSelectors.enabledSchema([])(mockState);
expect(result).toEqual([]);
});
// fix https://github.com/lobehub/lobe-chat/issues/2036
it('should not contain url', () => {
const result = toolSelectors.enabledSchema(['plugin-3'])(mockState);
expect(result[0].function).toEqual({
description: '123123',
name: 'plugin-3____api-3',
parameters: {
properties: {
a: {
type: 'string',
},
},
type: 'object',
},
});
expect(result[0].function).not.toHaveProperty('url');
});
});
describe('getToolManifestLoadingStatus', () => {
it('should return "loading" if the plugin manifest is being loaded', () => {
const result = toolSelectors.getManifestLoadingStatus('plugin-2')(mockState);
expect(result).toBe('loading');
});
it('should return "error" if the plugin manifest is not found', () => {
const result = toolSelectors.getManifestLoadingStatus('non-existing-plugin')(mockState);
expect(result).toBe('error');
});
it('should return "success" if the plugin manifest is loaded', () => {
const result = toolSelectors.getManifestLoadingStatus('plugin-1')(mockState);
expect(result).toBe('success');
});
});
describe('metaList and getMetaById', () => {
it('should return the correct list of tool metadata', () => {
const result = toolSelectors.metaList()(mockState);
expect(result).toEqual([
{
type: 'builtin',
author: 'LobeHub',
identifier: 'builtin-1',
meta: { title: 'Builtin 1', description: 'Builtin 1 description' },
},
{
identifier: 'plugin-1',
type: 'plugin',
meta: { title: 'Plugin 1', description: 'Plugin 1 description' },
},
{
type: 'plugin',
identifier: 'plugin-2',
meta: undefined,
},
{
identifier: 'plugin-3',
type: 'customPlugin',
},
]);
});
it('should return the correct metadata by identifier', () => {
const result = toolSelectors.getMetaById('plugin-1')(mockState);
expect(result).toEqual({ title: 'Plugin 1', description: 'Plugin 1 description' });
});
it('should return undefined for non-existent identifier', () => {
const result = toolSelectors.getMetaById('non-existent')(mockState);
expect(result).toBeUndefined();
});
});
describe('getManifestById and getManifestLoadingStatus', () => {
it('should return the correct manifest by identifier', () => {
const result = toolSelectors.getManifestById('plugin-1')(mockState);
expect(result).toEqual({
identifier: 'plugin-1',
api: [{ name: 'api-1' }],
meta: { title: 'Plugin 1', description: 'Plugin 1 description' },
});
});
it('should return undefined for non-existent identifier', () => {
const result = toolSelectors.getManifestById('non-existent')(mockState);
expect(result).toBeUndefined();
});
it('should return the correct loading status for a plugin', () => {
expect(toolSelectors.getManifestLoadingStatus('plugin-1')(mockState)).toBe('success');
expect(toolSelectors.getManifestLoadingStatus('plugin-2')(mockState)).toBe('loading');
expect(toolSelectors.getManifestLoadingStatus('non-existent')(mockState)).toBe('error');
});
});
describe('isToolHasUI', () => {
it('should return false if the tool has no UI', () => {
expect(toolSelectors.isToolHasUI('plugin-1')(mockState)).toBe(false);
});
it('should return true if the tool has UI', () => {
expect(toolSelectors.isToolHasUI('builtin-1')(mockState)).toBe(true);
});
it('should return false if the tool does not exist', () => {
expect(toolSelectors.isToolHasUI('non-existent')(mockState)).toBe(false);
});
});
});