@websolutespa/payload-plugin-bowl-llm
Version:
LLM plugin for Bowl PayloadCms plugin
878 lines (877 loc) • 41.5 kB
JavaScript
import { hasRole, isRole, isTenant, roles } from '@websolutespa/payload-plugin-bowl';
import { KbFileNoToolOption } from '../api/consts';
import { Card } from '../blocks/Card';
import { Cta } from '../blocks/Cta';
import { ExternalEndpointAuthentication, Tool } from '../blocks/Tool';
import { options } from '../options';
import { LLM_DEFAULT_MAX_FILE_SIZE, LLM_DEFAULT_MIME_TYPES } from '../types';
export var LlmAppMode;
(function(LlmAppMode) {
LlmAppMode["Site"] = "site";
LlmAppMode["Chat"] = "chat";
LlmAppMode["Url2Text"] = "url2text";
LlmAppMode["Translation"] = "translation";
})(LlmAppMode || (LlmAppMode = {}));
export var RecognitionMode;
(function(RecognitionMode) {
RecognitionMode["None"] = "none";
RecognitionMode["Default"] = "default";
})(RecognitionMode || (RecognitionMode = {}));
export var StorageMode;
(function(StorageMode) {
StorageMode["Local"] = "local";
StorageMode["Session"] = "session";
})(StorageMode || (StorageMode = {}));
export var SynthesisMode;
(function(SynthesisMode) {
SynthesisMode["None"] = "none";
SynthesisMode["Default"] = "default";
SynthesisMode["Elevenlabs"] = "elevenlabs";
})(SynthesisMode || (SynthesisMode = {}));
const serverURL = process.env.PAYLOAD_PUBLIC_SERVER_URL || '';
const basePath = process.env.PAYLOAD_PUBLIC_BASE_PATH || '';
export const LlmApp = {
type: 'withCollection',
slug: options.slug.llmApp,
admin: {
group: options.group.llm,
useAsTitle: 'name',
defaultColumns: [
'name',
'appKey',
'isActive'
],
preview: (doc, { locale, token })=>{
const app = doc;
return `${serverURL}${basePath}/api/llm/${app.settings.credentials.appKey}/${locale}`;
},
hidden: (args)=>!hasRole(args.user, roles.Admin, options.roles.LlmContributor)
},
access: {
create: (args)=>isRole(roles.Admin)(args),
read: (args)=>isRole(roles.Admin)(args) || isTenant(options.slug.llmApp)(args),
update: (args)=>isRole(roles.Admin)(args) || isRole(options.roles.LlmContributor)(args) && isTenant(options.slug.llmApp)(args),
delete: (args)=>isRole(roles.Admin)(args)
},
fields: [
{
name: 'name',
type: 'text',
required: true
},
{
type: 'withIsActive'
},
{
name: 'mode',
type: 'select',
options: Object.entries(LlmAppMode).map(([k, v])=>({
value: v,
label: k
}))
},
{
type: 'tabs',
tabs: [
///////////////////////
// TAB App Settings
{
name: 'settings',
label: 'Settings',
fields: [
{
name: 'credentials',
type: 'group',
fields: [
{
type: 'row',
fields: [
{
name: 'appKey',
type: 'text',
required: true,
unique: true,
admin: {
width: '50%'
}
},
{
name: 'apiKey',
type: 'text',
required: true,
unique: true,
admin: {
width: '50%'
}
}
]
},
{
name: 'previewEnabled',
type: 'withCheckbox'
},
{
name: 'httpReferrers',
type: 'array',
fields: [
{
name: 'referrer',
type: 'text'
}
]
}
]
},
{
name: 'nightlyKbGeneration',
type: 'checkbox',
defaultValue: true
},
{
name: 'llmConfig',
type: 'group',
fields: [
{
name: 'provider',
type: 'text',
admin: {
components: {
Field: '@websolutespa/payload-plugin-bowl-llm/client#LlmProvider'
}
}
},
{
name: 'model',
type: 'text',
admin: {
components: {
Field: '@websolutespa/payload-plugin-bowl-llm/client#LlmModel'
}
}
},
{
name: 'temperature',
type: 'number',
defaultValue: 0,
min: 0,
max: 1,
admin: {
step: 0.1
}
},
{
name: 'secrets',
type: 'group',
fields: [
{
name: 'apiKey',
label: 'LLM Api Key',
type: 'text',
hooks: {
afterRead: [
({ value, data })=>{
return value || data?.settings?.llmConfig?.secrets?.openAIApiKey;
}
]
}
},
{
name: 'openAIApiKey',
label: 'OpenAI Api Key [DEPRECATED: use apiKey instead]',
type: 'text',
admin: {
hidden: true
}
},
{
name: 'langChainApiKey',
type: 'text'
},
{
name: 'nebulyApiKey',
type: 'text'
},
{
name: 'embeddingApiKey',
label: 'Embedding API Key',
type: 'text',
admin: {
description: 'Valorizzare con chiave OpenAIKey'
}
}
]
},
{
name: 'prompt',
label: 'Mental model',
type: 'group',
fields: [
{
name: 'systemMessage',
label: 'System Message (deprecated, use the prompt field below instead)',
type: 'textarea'
},
{
name: 'prompt',
label: 'Mental model',
type: 'relationship',
relationTo: options.slug.llmPrompt,
filterOptions: ({ id })=>{
return {
or: [
{
llmApp: {
equals: id
}
}
]
};
}
}
]
},
{
name: 'outputStructure',
type: 'group',
fields: [
{
name: 'outputType',
label: 'Output Type',
type: 'select',
options: [
{
value: 'text',
label: 'Text'
},
{
value: 'json',
label: 'JSON'
}
],
defaultValue: 'text'
},
{
name: 'outputFormat',
label: 'JSON Schema',
type: 'json',
admin: {
condition: (_, siblingData)=>siblingData?.outputType === 'json'
}
}
]
},
{
name: 'tools',
type: 'relationship',
relationTo: options.slug.llmTool,
hasMany: true
},
{
name: 'langChainTracing',
type: 'text'
},
{
name: 'langChainProject',
type: 'text'
}
]
},
{
name: 'appTools',
label: 'App Tools',
type: 'blocks',
blocks: [
Tool
],
admin: {
initCollapsed: true,
condition: ({ mode })=>mode !== 'url2text' && mode !== 'translation'
}
},
{
name: 'fineTuning',
type: 'group',
fields: [
{
name: 'modelNameSuffix',
type: 'text'
},
{
name: 'currentJobId',
type: 'text',
admin: {
readOnly: true
}
},
{
name: 'fineTunedModelName',
type: 'text',
admin: {
readOnly: true
}
},
{
name: 'fineTuningActions',
type: 'ui',
admin: {
components: {
Field: '@websolutespa/payload-plugin-bowl-llm/client#FineTune'
}
}
}
]
},
{
name: 'rules',
type: 'group',
fields: [
{
name: 'vectorDbFile',
type: 'relationship',
relationTo: options.slug.llmVectorDb,
// show only vector db files that are associated with the current app and have no tool
filterOptions: ({ id })=>{
return {
and: [
{
or: [
{
llmApp: {
equals: id
}
},
// avoid validation error with legacy vector db files that are not associated with any app
{
llmApp: {
equals: null
}
},
{
llmApp: {
equals: false
}
}
]
},
{
or: [
{
appTool: {
equals: KbFileNoToolOption
}
},
{
appTool: {
equals: null
}
},
{
appTool: {
exists: false
}
}
]
}
]
};
}
},
{
name: 'threshold',
type: 'number',
defaultValue: 0.7,
min: 0,
max: 1,
admin: {
step: 0.1
}
}
]
},
{
name: 'knowledgeBase',
type: 'group',
fields: [
{
name: 'externalEndpoints',
type: 'array',
fields: [
{
name: 'endpointUrl',
type: 'text'
},
{
name: 'authentication',
type: 'select',
options: Object.entries(ExternalEndpointAuthentication).map(([k, v])=>({
value: v,
label: k
}))
},
{
name: 'authSecret',
type: 'text',
admin: {
condition: (_, siblingData)=>siblingData?.authentication === ExternalEndpointAuthentication.Basic || siblingData?.authentication === ExternalEndpointAuthentication.Bearer || siblingData?.authentication === ExternalEndpointAuthentication.Custom
}
},
/*
* temporarily removed
{
name: 'postProcessor',
type: 'text',
defaultValue: 'default',
admin: {
components: {
Field: EndpointPostProcessor,
},
},
},
*/ {
name: 'fieldsMapping',
type: 'group',
fields: [
{
name: 'replacedFields',
type: 'array',
fields: [
{
name: 'srcName',
type: 'text'
},
{
name: 'destName',
type: 'text'
}
]
},
{
name: 'newFields',
type: 'array',
fields: [
{
name: 'name',
type: 'text'
},
{
name: 'value',
type: 'text'
}
]
},
{
name: 'deletedFields',
type: 'array',
fields: [
{
name: 'name',
type: 'text'
}
]
},
{
name: 'metaFields',
type: 'array',
fields: [
{
name: 'name',
type: 'text'
},
{
name: 'description',
type: 'text'
},
{
name: 'type',
type: 'select',
options: [
'int',
'float',
'string',
'bool',
'list',
'dict'
]
}
]
}
]
}
]
},
{
name: 'vectorDbFile',
type: 'relationship',
relationTo: options.slug.llmVectorDb,
// show only vector db files that are associated with the current app and have no tool
filterOptions: ({ id })=>{
return {
and: [
{
or: [
{
llmApp: {
equals: id
}
},
// avoid validation error with legacy vector db files that are not associated with any app
{
llmApp: {
equals: null
}
},
{
llmApp: {
equals: false
}
}
]
},
{
or: [
{
appTool: {
equals: KbFileNoToolOption
}
},
{
appTool: {
equals: null
}
},
{
appTool: {
exists: false
}
}
]
}
]
};
}
}
]
}
]
},
///////////////////////
// TAB contents
{
name: 'contents',
label: 'Contents',
fields: [
{
name: 'logo',
type: 'withMedia'
},
{
name: 'additionalLogos',
type: 'array',
fields: [
{
name: 'logo',
type: 'withMedia',
required: true
}
]
},
{
name: 'collapsedWelcomeText',
type: 'textarea',
localized: true
},
{
name: 'collapsedWelcomeTextCta',
type: 'textarea',
localized: true
},
{
name: 'collapsedWelcomeTextHover',
type: 'textarea',
localized: true
},
{
name: 'shortWelcomeText',
type: 'textarea',
localized: true
},
{
name: 'extendedWelcomeText',
type: 'textarea',
localized: true
},
{
name: 'disclaimer',
type: 'textarea',
localized: true
},
{
name: 'disclaimerShort',
type: 'textarea',
localized: true
},
{
name: 'disclaimerFooter',
type: 'withCheckbox'
},
{
name: 'promptPlaceholder',
type: 'text',
localized: true
},
{
name: 'sampleInputTexts',
type: 'array',
fields: [
{
name: 'sampleInputText',
type: 'text',
localized: true
}
]
},
{
name: 'layoutBuilder',
label: 'Blocks',
type: 'blocks',
blocks: [
Card
],
admin: {
initCollapsed: true
}
},
{
name: 'headerCtas',
label: 'Header Cta',
type: 'blocks',
blocks: [
Cta
],
admin: {
initCollapsed: true
}
},
{
name: 'customIntro',
type: 'text',
localized: true,
admin: {
description: 'An absolute http link that point to a custom intro to display in place of the default intro.'
}
},
{
name: 'customTheme',
type: 'json',
admin: {
// description: (props: any) => CustomThemeDescription(props),
components: {
Description: '@websolutespa/payload-plugin-bowl-llm/client#CustomThemeDescription'
}
}
},
{
name: 'openingSound',
type: 'withMedia',
displayPreview: false,
filterOptions: {
mimeType: {
contains: 'audio/mpeg'
}
}
},
{
name: 'recognitionMode',
type: 'select',
options: Object.entries(RecognitionMode).map(([k, v])=>({
value: v,
label: k
}))
},
{
name: 'synthesisMode',
type: 'select',
options: Object.entries(SynthesisMode).map(([k, v])=>({
value: v,
label: k
}))
},
{
name: 'textToSpeechApiKey',
type: 'text',
admin: {
condition: (_, siblingData)=>[
'elevenlabs'
].includes(siblingData?.synthesisMode)
}
},
{
name: 'textToSpeechVoiceId',
type: 'text',
admin: {
condition: (_, siblingData)=>[
'elevenlabs'
].includes(siblingData?.synthesisMode)
}
},
{
name: 'disableSpeechSynthesis',
type: 'withCheckbox'
},
{
name: 'disableSpeechRecognition',
type: 'withCheckbox'
},
{
name: 'enableUpload',
type: 'withCheckbox'
},
{
name: 'enableFeedback',
type: 'withCheckbox'
},
{
name: 'enableHistory',
type: 'withCheckbox'
},
{
name: 'enableMinimize',
type: 'withCheckbox'
},
{
name: 'disablePoweredBy',
type: 'withCheckbox'
},
{
name: 'promptTokenLimit',
type: 'number',
admin: {
description: 'leave blank if unsure. default prompt limit 8000 tokens.'
}
},
{
name: 'maxFileSize',
type: 'number',
admin: {
description: `leave blank if unsure. default max file size ${LLM_DEFAULT_MAX_FILE_SIZE} ${formatBytes(LLM_DEFAULT_MAX_FILE_SIZE)}.`
}
},
{
name: 'mimeTypes',
type: 'text',
admin: {
description: `leave blank if unsure. default allowed mime types (${LLM_DEFAULT_MIME_TYPES}).`
}
},
{
name: 'storageMode',
type: 'select',
options: Object.entries(StorageMode).map(([k, v])=>({
value: v,
label: k
})),
admin: {
description: 'leave blank if unsure. default to local storage mode.'
}
}
]
},
///////////////////////
// TAB hooks
{
label: 'Hooks',
fields: [
{
name: 'webhooks',
type: 'array',
fields: [
{
name: 'webhook',
type: 'text'
}
]
}
]
},
///////////////////////
// TAB links
{
label: 'Links',
fields: [
{
name: 'linksGroups',
type: 'array',
fields: [
{
name: 'title',
type: 'text'
},
{
name: 'links',
type: 'array',
fields: [
{
name: 'title',
type: 'text'
},
{
name: 'url',
type: 'text'
}
]
}
]
}
]
},
///////////////////////
// TAB preview
{
label: 'Preview',
fields: [
{
name: 'plugin',
type: 'group',
fields: [
{
name: 'name',
type: 'text'
},
{
name: 'version',
type: 'text'
},
{
name: 'namespace',
type: 'text'
}
],
admin: {
description: 'Customize the plugin used by the preview.'
}
}
]
}
]
}
]
};
function formatBytes(bytes, decimals = 2) {
if (!+bytes) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = [
'Bytes',
'KiB',
'MiB',
'GiB',
'TiB',
'PiB',
'EiB',
'ZiB',
'YiB'
];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}
//# sourceMappingURL=LlmApp.js.map