UNPKG

@websolutespa/payload-plugin-bowl-llm

Version:

LLM plugin for Bowl PayloadCms plugin

878 lines (877 loc) 41.5 kB
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