@promptbook/cli 0.104.0-1 → 0.104.0-11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/agents-server/config.ts +1 -3
- package/apps/agents-server/next.config.ts +2 -2
- package/apps/agents-server/package.json +7 -3
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
- package/apps/agents-server/src/app/actions.ts +22 -5
- package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
- package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
- package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +263 -0
- package/apps/agents-server/src/app/admin/files/actions.ts +61 -0
- package/apps/agents-server/src/app/admin/files/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/image-generator-test/ImageGeneratorTestClient.tsx +169 -0
- package/apps/agents-server/src/app/admin/image-generator-test/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +256 -0
- package/apps/agents-server/src/app/admin/images/actions.ts +60 -0
- package/apps/agents-server/src/app/admin/images/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
- package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
- package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
- package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
- package/apps/agents-server/src/app/admin/search-engine-test/SearchEngineTestClient.tsx +109 -0
- package/apps/agents-server/src/app/admin/search-engine-test/actions.ts +17 -0
- package/apps/agents-server/src/app/admin/search-engine-test/page.tsx +13 -0
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
- package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
- package/apps/agents-server/src/app/api/agents/route.ts +28 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
- package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
- package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
- package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
- package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +128 -0
- package/apps/agents-server/src/app/api/messages/route.ts +102 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +128 -45
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
- package/apps/agents-server/src/app/docs/page.tsx +12 -12
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +54 -6
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
- package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
- package/apps/agents-server/src/app/security.txt/route.ts +1 -1
- package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentCapabilityChips.tsx +38 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +44 -116
- package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
- package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
- package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
- package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
- package/apps/agents-server/src/components/Header/Header.tsx +130 -40
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +150 -23
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
- package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
- package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
- package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/$getTableName.ts +1 -0
- package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
- package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
- package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
- package/apps/agents-server/src/database/migrate.ts +34 -1
- package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
- package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
- package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
- package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
- package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
- package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
- package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
- package/apps/agents-server/src/database/migrations/2025-12-0830-image-purpose.sql +5 -0
- package/apps/agents-server/src/database/migrations/2025-12-0890-file-agent-id.sql +5 -0
- package/apps/agents-server/src/database/schema.ts +244 -4
- package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
- package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
- package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
- package/apps/agents-server/src/message-providers/index.ts +13 -0
- package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
- package/apps/agents-server/src/middleware.ts +19 -23
- package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
- package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
- package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
- package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
- package/esm/index.es.js +1534 -1330
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -0
- package/esm/typings/src/_packages/core.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +16 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +29 -1
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
- package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +9 -13
- package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +3 -3
- package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +1 -1
- package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
- package/esm/typings/src/book-components/icons/AboutIcon.d.ts +1 -1
- package/esm/typings/src/book-components/icons/AttachmentIcon.d.ts +1 -1
- package/esm/typings/src/book-components/icons/CameraIcon.d.ts +1 -1
- package/esm/typings/src/book-components/icons/DownloadIcon.d.ts +1 -1
- package/esm/typings/src/book-components/icons/MenuIcon.d.ts +1 -1
- package/esm/typings/src/book-components/icons/SaveIcon.d.ts +1 -1
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -12
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +27 -15
- package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/llm-providers/_common/utils/count-total-usage/countUsage.d.ts +1 -1
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +6 -1
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
- package/esm/typings/src/remote-server/ui/ServerApp.d.ts +1 -1
- package/esm/typings/src/search-engines/SearchEngine.d.ts +9 -0
- package/esm/typings/src/search-engines/SearchResult.d.ts +18 -0
- package/esm/typings/src/search-engines/bing/BingSearchEngine.d.ts +15 -0
- package/esm/typings/src/search-engines/dummy/DummySearchEngine.d.ts +15 -0
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
- package/esm/typings/src/types/typeAliases.d.ts +23 -1
- package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
- package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
- package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
- package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomAgentPersona.d.ts +3 -2
- package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +1542 -1338
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/package-lock.json +0 -27
- package/apps/agents-server/public/fonts/download-font.js +0 -22
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
- package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
package/umd/index.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('waitasecond'), require('prompts'), require('path'), require('fs/promises'), require('dotenv'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto'), require('socket.io-client'), require('
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'waitasecond', 'prompts', 'path', 'fs/promises', 'dotenv', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto', 'socket.io-client', 'rxjs', 'child_process', 'jszip', 'papaparse', '
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global.spaceTrim$1, global.waitasecond, global.prompts, global.path, global.promises, global.dotenv, global.hexEncoder, global.sha256, global.crypto, global.socket_ioClient, global.rxjs, global.child_process, global.JSZip, global.papaparse, global.
|
|
5
|
-
})(this, (function (exports, colors, commander, spaceTrim$1, waitasecond, prompts, path, promises, dotenv, hexEncoder, sha256, crypto, socket_ioClient, rxjs, child_process, JSZip, papaparse,
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('waitasecond'), require('prompts'), require('path'), require('fs/promises'), require('dotenv'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto'), require('socket.io-client'), require('crypto-js'), require('rxjs'), require('child_process'), require('jszip'), require('papaparse'), require('mime-types'), require('glob-promise'), require('moment'), require('express'), require('express-openapi-validator'), require('http'), require('socket.io'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('@anthropic-ai/sdk'), require('bottleneck'), require('@azure/openai'), require('openai'), require('@mozilla/readability'), require('jsdom'), require('showdown')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'waitasecond', 'prompts', 'path', 'fs/promises', 'dotenv', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto', 'socket.io-client', 'crypto-js', 'rxjs', 'child_process', 'jszip', 'papaparse', 'mime-types', 'glob-promise', 'moment', 'express', 'express-openapi-validator', 'http', 'socket.io', 'swagger-ui-express', 'react', 'react-dom/server', '@anthropic-ai/sdk', 'bottleneck', '@azure/openai', 'openai', '@mozilla/readability', 'jsdom', 'showdown'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global.spaceTrim$1, global.waitasecond, global.prompts, global.path, global.promises, global.dotenv, global.hexEncoder, global.sha256, global.crypto, global.socket_ioClient, global.cryptoJs, global.rxjs, global.child_process, global.JSZip, global.papaparse, global.mimeTypes, global.glob, global.moment, global.express, global.OpenApiValidator, global.http, global.socket_io, global.swaggerUi, global.react, global.server, global.Anthropic, global.Bottleneck, global.openai, global.OpenAI, global.readability, global.jsdom, global.showdown));
|
|
5
|
+
})(this, (function (exports, colors, commander, spaceTrim$1, waitasecond, prompts, path, promises, dotenv, hexEncoder, sha256, crypto, socket_ioClient, cryptoJs, rxjs, child_process, JSZip, papaparse, mimeTypes, glob, moment, express, OpenApiValidator, http, socket_io, swaggerUi, react, server, Anthropic, Bottleneck, openai, OpenAI, readability, jsdom, showdown) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
8
|
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
* @generated
|
|
57
57
|
* @see https://github.com/webgptorg/promptbook
|
|
58
58
|
*/
|
|
59
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.104.0-
|
|
59
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.104.0-11';
|
|
60
60
|
/**
|
|
61
61
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
62
62
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -66,6 +66,8 @@
|
|
|
66
66
|
* Core Promptbook server configuration.
|
|
67
67
|
*
|
|
68
68
|
* This server is also used for auto-federation in the Agents Server.
|
|
69
|
+
*
|
|
70
|
+
* @public exported from `@promptbook/core`
|
|
69
71
|
*/
|
|
70
72
|
const CORE_SERVER = {
|
|
71
73
|
title: 'Promptbook Core',
|
|
@@ -1382,13 +1384,14 @@
|
|
|
1382
1384
|
*
|
|
1383
1385
|
* @public exported from `@promptbook/utils`
|
|
1384
1386
|
*/
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1387
|
+
function $isRunningInNode() {
|
|
1388
|
+
try {
|
|
1389
|
+
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
1390
|
+
}
|
|
1391
|
+
catch (e) {
|
|
1392
|
+
return false;
|
|
1393
|
+
}
|
|
1390
1394
|
}
|
|
1391
|
-
`);
|
|
1392
1395
|
/**
|
|
1393
1396
|
* TODO: [🎺]
|
|
1394
1397
|
*/
|
|
@@ -1400,13 +1403,14 @@
|
|
|
1400
1403
|
*
|
|
1401
1404
|
* @public exported from `@promptbook/utils`
|
|
1402
1405
|
*/
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1406
|
+
function $isRunningInBrowser() {
|
|
1407
|
+
try {
|
|
1408
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
1409
|
+
}
|
|
1410
|
+
catch (e) {
|
|
1411
|
+
return false;
|
|
1412
|
+
}
|
|
1408
1413
|
}
|
|
1409
|
-
`);
|
|
1410
1414
|
/**
|
|
1411
1415
|
* TODO: [🎺]
|
|
1412
1416
|
*/
|
|
@@ -1418,13 +1422,15 @@
|
|
|
1418
1422
|
*
|
|
1419
1423
|
* @public exported from `@promptbook/utils`
|
|
1420
1424
|
*/
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1425
|
+
function $isRunningInJest() {
|
|
1426
|
+
var _a;
|
|
1427
|
+
try {
|
|
1428
|
+
return typeof process !== 'undefined' && ((_a = process.env) === null || _a === void 0 ? void 0 : _a.JEST_WORKER_ID) !== undefined;
|
|
1429
|
+
}
|
|
1430
|
+
catch (e) {
|
|
1431
|
+
return false;
|
|
1432
|
+
}
|
|
1426
1433
|
}
|
|
1427
|
-
`);
|
|
1428
1434
|
/**
|
|
1429
1435
|
* TODO: [🎺]
|
|
1430
1436
|
*/
|
|
@@ -1436,17 +1442,17 @@
|
|
|
1436
1442
|
*
|
|
1437
1443
|
* @public exported from `@promptbook/utils`
|
|
1438
1444
|
*/
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1445
|
+
function $isRunningInWebWorker() {
|
|
1446
|
+
try {
|
|
1447
|
+
// Note: Check for importScripts which is specific to workers
|
|
1448
|
+
// and not available in the main browser thread
|
|
1449
|
+
return (typeof self !== 'undefined' &&
|
|
1450
|
+
typeof self.importScripts === 'function');
|
|
1451
|
+
}
|
|
1452
|
+
catch (e) {
|
|
1444
1453
|
return false;
|
|
1445
1454
|
}
|
|
1446
|
-
} catch (e) {
|
|
1447
|
-
return false;
|
|
1448
1455
|
}
|
|
1449
|
-
`);
|
|
1450
1456
|
/**
|
|
1451
1457
|
* TODO: [🎺]
|
|
1452
1458
|
*/
|
|
@@ -1964,7 +1970,7 @@
|
|
|
1964
1970
|
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
1965
1971
|
${morePieces.join('; ')}
|
|
1966
1972
|
`);
|
|
1967
|
-
if ($isRunningInNode) {
|
|
1973
|
+
if ($isRunningInNode()) {
|
|
1968
1974
|
if (isInstalled && isFullyConfigured) {
|
|
1969
1975
|
providerMessage = colors__default["default"].green(providerMessage);
|
|
1970
1976
|
}
|
|
@@ -3726,6 +3732,7 @@
|
|
|
3726
3732
|
}
|
|
3727
3733
|
}
|
|
3728
3734
|
/**
|
|
3735
|
+
* TODO: !!!! Deprecate pipeline server and all of its components
|
|
3729
3736
|
* TODO: Maybe use `$exportJson`
|
|
3730
3737
|
* TODO: [🧠][🛍] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`
|
|
3731
3738
|
* TODO: [🍓] Allow to list compatible models with each variant
|
|
@@ -3735,280 +3742,1166 @@
|
|
|
3735
3742
|
*/
|
|
3736
3743
|
|
|
3737
3744
|
/**
|
|
3738
|
-
*
|
|
3745
|
+
* Normalizes a given text to camelCase format.
|
|
3739
3746
|
*
|
|
3740
|
-
*
|
|
3741
|
-
* @returns `true` if the string is a valid JSON string, false otherwise
|
|
3747
|
+
* Note: [🔂] This function is idempotent.
|
|
3742
3748
|
*
|
|
3749
|
+
* @param text The text to be normalized.
|
|
3750
|
+
* @param _isFirstLetterCapital Whether the first letter should be capitalized.
|
|
3751
|
+
* @returns The camelCase formatted string.
|
|
3752
|
+
* @example 'helloWorld'
|
|
3753
|
+
* @example 'iLovePromptbook'
|
|
3743
3754
|
* @public exported from `@promptbook/utils`
|
|
3744
3755
|
*/
|
|
3745
|
-
function
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3756
|
+
function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
|
|
3757
|
+
let charType;
|
|
3758
|
+
let lastCharType = null;
|
|
3759
|
+
let normalizedName = '';
|
|
3760
|
+
for (const char of text) {
|
|
3761
|
+
let normalizedChar;
|
|
3762
|
+
if (/^[a-z]$/.test(char)) {
|
|
3763
|
+
charType = 'LOWERCASE';
|
|
3764
|
+
normalizedChar = char;
|
|
3754
3765
|
}
|
|
3755
|
-
|
|
3766
|
+
else if (/^[A-Z]$/.test(char)) {
|
|
3767
|
+
charType = 'UPPERCASE';
|
|
3768
|
+
normalizedChar = char.toLowerCase();
|
|
3769
|
+
}
|
|
3770
|
+
else if (/^[0-9]$/.test(char)) {
|
|
3771
|
+
charType = 'NUMBER';
|
|
3772
|
+
normalizedChar = char;
|
|
3773
|
+
}
|
|
3774
|
+
else {
|
|
3775
|
+
charType = 'OTHER';
|
|
3776
|
+
normalizedChar = '';
|
|
3777
|
+
}
|
|
3778
|
+
if (!lastCharType) {
|
|
3779
|
+
if (_isFirstLetterCapital) {
|
|
3780
|
+
normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
else if (charType !== lastCharType &&
|
|
3784
|
+
!(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
|
|
3785
|
+
!(lastCharType === 'NUMBER') &&
|
|
3786
|
+
!(charType === 'NUMBER')) {
|
|
3787
|
+
normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
|
|
3788
|
+
}
|
|
3789
|
+
normalizedName += normalizedChar;
|
|
3790
|
+
lastCharType = charType;
|
|
3756
3791
|
}
|
|
3792
|
+
return normalizedName;
|
|
3757
3793
|
}
|
|
3758
|
-
|
|
3759
3794
|
/**
|
|
3760
|
-
*
|
|
3761
|
-
*
|
|
3762
|
-
* Note: [🔂] This function is idempotent.
|
|
3763
|
-
*
|
|
3764
|
-
* @public exported from `@promptbook/utils`
|
|
3795
|
+
* TODO: [🌺] Use some intermediate util splitWords
|
|
3765
3796
|
*/
|
|
3766
|
-
function capitalize(word) {
|
|
3767
|
-
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
3768
|
-
}
|
|
3769
3797
|
|
|
3770
3798
|
/**
|
|
3771
|
-
*
|
|
3799
|
+
* Creates a Mermaid graph based on the promptbook
|
|
3772
3800
|
*
|
|
3773
|
-
* Note:
|
|
3774
|
-
* - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
|
|
3775
|
-
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
3776
|
-
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
3777
|
-
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
3801
|
+
* Note: The result is not wrapped in a Markdown code block
|
|
3778
3802
|
*
|
|
3779
|
-
* @
|
|
3780
|
-
* @returns code blocks with language and content
|
|
3781
|
-
* @throws {ParseError} if block is not closed properly
|
|
3782
|
-
* @public exported from `@promptbook/markdown-utils`
|
|
3803
|
+
* @public exported from `@promptbook/utils`
|
|
3783
3804
|
*/
|
|
3784
|
-
function
|
|
3785
|
-
const
|
|
3786
|
-
const
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
} /* not else */
|
|
3795
|
-
if (currentCodeBlock.blockNotation === '>') {
|
|
3796
|
-
if (currentCodeBlock.content !== '') {
|
|
3797
|
-
currentCodeBlock.content += '\n';
|
|
3798
|
-
}
|
|
3799
|
-
currentCodeBlock.content += line.slice(2);
|
|
3800
|
-
}
|
|
3805
|
+
function renderPromptbookMermaid(pipelineJson, options) {
|
|
3806
|
+
const { linkTask = () => null } = options || {};
|
|
3807
|
+
const MERMAID_PREFIX = 'pipeline_';
|
|
3808
|
+
const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
|
|
3809
|
+
const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
|
|
3810
|
+
const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
|
|
3811
|
+
const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
|
|
3812
|
+
const parameterNameToTaskName = (parameterName) => {
|
|
3813
|
+
if (parameterName === 'knowledge') {
|
|
3814
|
+
return MERMAID_KNOWLEDGE_NAME;
|
|
3801
3815
|
}
|
|
3802
|
-
else if (
|
|
3803
|
-
|
|
3804
|
-
currentCodeBlock = null;
|
|
3816
|
+
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
3817
|
+
return MERMAID_RESERVED_NAME;
|
|
3805
3818
|
}
|
|
3806
|
-
|
|
3807
|
-
if (
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
currentCodeBlock = { blockNotation: '```', language, content: '' };
|
|
3811
|
-
}
|
|
3812
|
-
else {
|
|
3813
|
-
if (language !== null) {
|
|
3814
|
-
throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
|
|
3815
|
-
}
|
|
3816
|
-
codeBlocks.push(currentCodeBlock);
|
|
3817
|
-
currentCodeBlock = null;
|
|
3818
|
-
}
|
|
3819
|
+
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
3820
|
+
if (!parameter) {
|
|
3821
|
+
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
3822
|
+
// <- TODO: This causes problems when {knowledge} and other reserved parameters are used
|
|
3819
3823
|
}
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
currentCodeBlock.content += '\n';
|
|
3823
|
-
}
|
|
3824
|
-
currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
|
|
3824
|
+
if (parameter.isInput) {
|
|
3825
|
+
return MERMAID_INPUT_NAME;
|
|
3825
3826
|
}
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3827
|
+
const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
|
|
3828
|
+
if (!task) {
|
|
3829
|
+
throw new Error(`Could not find task for {${parameterName}}`);
|
|
3830
|
+
}
|
|
3831
|
+
return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
|
|
3832
|
+
};
|
|
3833
|
+
const inputAndIntermediateParametersMermaid = pipelineJson.tasks
|
|
3834
|
+
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
3835
|
+
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
3836
|
+
...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
|
|
3837
|
+
])
|
|
3838
|
+
.join('\n');
|
|
3839
|
+
const outputParametersMermaid = pipelineJson.parameters
|
|
3840
|
+
.filter(({ isOutput }) => isOutput)
|
|
3841
|
+
.map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
|
|
3842
|
+
.join('\n');
|
|
3843
|
+
const linksMermaid = pipelineJson.tasks
|
|
3844
|
+
.map((task) => {
|
|
3845
|
+
const link = linkTask(task);
|
|
3846
|
+
if (link === null) {
|
|
3847
|
+
return '';
|
|
3848
|
+
}
|
|
3849
|
+
const { href, title } = link;
|
|
3850
|
+
const taskName = parameterNameToTaskName(task.resultingParameterName);
|
|
3851
|
+
return `click ${taskName} href "${href}" "${title}";`;
|
|
3852
|
+
})
|
|
3853
|
+
.filter((line) => line !== '')
|
|
3854
|
+
.join('\n');
|
|
3855
|
+
const interactionPointsMermaid = Object.entries({
|
|
3856
|
+
[MERMAID_INPUT_NAME]: 'Input',
|
|
3857
|
+
[MERMAID_OUTPUT_NAME]: 'Output',
|
|
3858
|
+
[MERMAID_RESERVED_NAME]: 'Other',
|
|
3859
|
+
[MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
|
|
3860
|
+
})
|
|
3861
|
+
.filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
|
|
3862
|
+
.map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
|
|
3863
|
+
.join('\n');
|
|
3864
|
+
const promptbookMermaid = spaceTrim$1.spaceTrim((block) => `
|
|
3865
|
+
|
|
3866
|
+
%% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
|
|
3867
|
+
|
|
3868
|
+
flowchart LR
|
|
3869
|
+
subgraph "${pipelineJson.title}"
|
|
3870
|
+
|
|
3871
|
+
%% Basic configuration
|
|
3872
|
+
direction TB
|
|
3873
|
+
|
|
3874
|
+
%% Interaction points from pipeline to outside
|
|
3875
|
+
${block(interactionPointsMermaid)}
|
|
3876
|
+
|
|
3877
|
+
%% Input and intermediate parameters
|
|
3878
|
+
${block(inputAndIntermediateParametersMermaid)}
|
|
3879
|
+
|
|
3880
|
+
|
|
3881
|
+
%% Output parameters
|
|
3882
|
+
${block(outputParametersMermaid)}
|
|
3883
|
+
|
|
3884
|
+
%% Links
|
|
3885
|
+
${block(linksMermaid)}
|
|
3886
|
+
|
|
3887
|
+
%% Styles
|
|
3888
|
+
classDef ${MERMAID_INPUT_NAME} color: grey;
|
|
3889
|
+
classDef ${MERMAID_OUTPUT_NAME} color: grey;
|
|
3890
|
+
classDef ${MERMAID_RESERVED_NAME} color: grey;
|
|
3891
|
+
classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
|
|
3892
|
+
|
|
3893
|
+
end;
|
|
3894
|
+
|
|
3895
|
+
`);
|
|
3896
|
+
return promptbookMermaid;
|
|
3831
3897
|
}
|
|
3832
3898
|
/**
|
|
3833
|
-
* TODO:
|
|
3899
|
+
* TODO: [🧠] FOREACH in mermaid graph
|
|
3900
|
+
* TODO: [🧠] Knowledge in mermaid graph
|
|
3901
|
+
* TODO: [🧠] Personas in mermaid graph
|
|
3902
|
+
* TODO: Maybe use some Mermaid package instead of string templating
|
|
3903
|
+
* TODO: [🕌] When more than 2 functionalities, split into separate functions
|
|
3834
3904
|
*/
|
|
3835
3905
|
|
|
3836
3906
|
/**
|
|
3837
|
-
*
|
|
3838
|
-
*
|
|
3839
|
-
* - When given string is a valid JSON as it is, it just returns it
|
|
3840
|
-
* - When there is no JSON code block the function throws a `ParseError`
|
|
3841
|
-
* - When there are multiple JSON code blocks the function throws a `ParseError`
|
|
3842
|
-
*
|
|
3843
|
-
* Note: It is not important if marked as ```json BUT if it is VALID JSON
|
|
3844
|
-
* Note: There are multiple similar function:
|
|
3845
|
-
* - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
|
|
3846
|
-
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
3847
|
-
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
3848
|
-
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
3907
|
+
* Serializes an error into a [🚉] JSON-serializable object
|
|
3849
3908
|
*
|
|
3850
|
-
* @public exported from `@promptbook/
|
|
3851
|
-
* @throws {ParseError} if there is no valid JSON block in the markdown
|
|
3909
|
+
* @public exported from `@promptbook/utils`
|
|
3852
3910
|
*/
|
|
3853
|
-
function
|
|
3854
|
-
|
|
3855
|
-
|
|
3911
|
+
function serializeError(error) {
|
|
3912
|
+
const { name, message, stack } = error;
|
|
3913
|
+
const { id } = error;
|
|
3914
|
+
if (!Object.keys(ALL_ERRORS).includes(name)) {
|
|
3915
|
+
console.error(spaceTrim__default["default"]((block) => `
|
|
3916
|
+
|
|
3917
|
+
Cannot serialize error with name "${name}"
|
|
3918
|
+
|
|
3919
|
+
Authors of Promptbook probably forgot to add this error into the list of errors:
|
|
3920
|
+
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
3921
|
+
|
|
3922
|
+
|
|
3923
|
+
${block(stack || message)}
|
|
3924
|
+
|
|
3925
|
+
`));
|
|
3926
|
+
}
|
|
3927
|
+
return {
|
|
3928
|
+
name: name,
|
|
3929
|
+
message,
|
|
3930
|
+
stack,
|
|
3931
|
+
id, // Include id in the serialized object
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
/**
|
|
3936
|
+
* Async version of Array.forEach
|
|
3937
|
+
*
|
|
3938
|
+
* @param array - Array to iterate over
|
|
3939
|
+
* @param options - Options for the function
|
|
3940
|
+
* @param callbackfunction - Function to call for each item
|
|
3941
|
+
* @public exported from `@promptbook/utils`
|
|
3942
|
+
* @deprecated [🪂] Use queues instead
|
|
3943
|
+
*/
|
|
3944
|
+
async function forEachAsync(array, options, callbackfunction) {
|
|
3945
|
+
const { maxParallelCount = Infinity } = options;
|
|
3946
|
+
let index = 0;
|
|
3947
|
+
let runningTasks = [];
|
|
3948
|
+
const tasks = [];
|
|
3949
|
+
for (const item of array) {
|
|
3950
|
+
const currentIndex = index++;
|
|
3951
|
+
const task = callbackfunction(item, currentIndex, array);
|
|
3952
|
+
tasks.push(task);
|
|
3953
|
+
runningTasks.push(task);
|
|
3954
|
+
/* not await */ Promise.resolve(task).then(() => {
|
|
3955
|
+
runningTasks = runningTasks.filter((t) => t !== task);
|
|
3956
|
+
});
|
|
3957
|
+
if (maxParallelCount < runningTasks.length) {
|
|
3958
|
+
await Promise.race(runningTasks);
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
await Promise.all(tasks);
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
/**
|
|
3965
|
+
* Function to check if a string is valid CSV
|
|
3966
|
+
*
|
|
3967
|
+
* @param value The string to check
|
|
3968
|
+
* @returns `true` if the string is a valid CSV string, false otherwise
|
|
3969
|
+
*
|
|
3970
|
+
* @public exported from `@promptbook/utils`
|
|
3971
|
+
*/
|
|
3972
|
+
function isValidCsvString(value) {
|
|
3973
|
+
try {
|
|
3974
|
+
// A simple check for CSV format: at least one comma and no invalid characters
|
|
3975
|
+
if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
|
|
3976
|
+
return true;
|
|
3977
|
+
}
|
|
3978
|
+
return false;
|
|
3979
|
+
}
|
|
3980
|
+
catch (error) {
|
|
3981
|
+
assertsError(error);
|
|
3982
|
+
return false;
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
/**
|
|
3987
|
+
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
3988
|
+
*
|
|
3989
|
+
* @param value The string to check
|
|
3990
|
+
* @returns `true` if the string is a valid JSON string, false otherwise
|
|
3991
|
+
*
|
|
3992
|
+
* @public exported from `@promptbook/utils`
|
|
3993
|
+
*/
|
|
3994
|
+
function isValidJsonString(value /* <- [👨⚖️] */) {
|
|
3995
|
+
try {
|
|
3996
|
+
JSON.parse(value);
|
|
3997
|
+
return true;
|
|
3998
|
+
}
|
|
3999
|
+
catch (error) {
|
|
4000
|
+
assertsError(error);
|
|
4001
|
+
if (error.message.includes('Unexpected token')) {
|
|
4002
|
+
return false;
|
|
4003
|
+
}
|
|
4004
|
+
return false;
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
|
|
4008
|
+
/**
|
|
4009
|
+
* Function to check if a string is valid XML
|
|
4010
|
+
*
|
|
4011
|
+
* @param value
|
|
4012
|
+
* @returns `true` if the string is a valid XML string, false otherwise
|
|
4013
|
+
*
|
|
4014
|
+
* @public exported from `@promptbook/utils`
|
|
4015
|
+
*/
|
|
4016
|
+
function isValidXmlString(value) {
|
|
4017
|
+
try {
|
|
4018
|
+
const parser = new DOMParser();
|
|
4019
|
+
const parsedDocument = parser.parseFromString(value, 'application/xml');
|
|
4020
|
+
const parserError = parsedDocument.getElementsByTagName('parsererror');
|
|
4021
|
+
if (parserError.length > 0) {
|
|
4022
|
+
return false;
|
|
4023
|
+
}
|
|
4024
|
+
return true;
|
|
4025
|
+
}
|
|
4026
|
+
catch (error) {
|
|
4027
|
+
assertsError(error);
|
|
4028
|
+
return false;
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
|
|
4032
|
+
/**
|
|
4033
|
+
* Format either small or big number
|
|
4034
|
+
*
|
|
4035
|
+
* @public exported from `@promptbook/utils`
|
|
4036
|
+
*/
|
|
4037
|
+
function numberToString(value) {
|
|
4038
|
+
if (value === 0) {
|
|
4039
|
+
return '0';
|
|
4040
|
+
}
|
|
4041
|
+
else if (Number.isNaN(value)) {
|
|
4042
|
+
return VALUE_STRINGS.nan;
|
|
4043
|
+
}
|
|
4044
|
+
else if (value === Infinity) {
|
|
4045
|
+
return VALUE_STRINGS.infinity;
|
|
4046
|
+
}
|
|
4047
|
+
else if (value === -Infinity) {
|
|
4048
|
+
return VALUE_STRINGS.negativeInfinity;
|
|
4049
|
+
}
|
|
4050
|
+
for (let exponent = 0; exponent < 15; exponent++) {
|
|
4051
|
+
const factor = 10 ** exponent;
|
|
4052
|
+
const valueRounded = Math.round(value * factor) / factor;
|
|
4053
|
+
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
4054
|
+
return valueRounded.toFixed(exponent);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
return value.toString();
|
|
4058
|
+
}
|
|
4059
|
+
|
|
4060
|
+
/**
|
|
4061
|
+
* Function `valueToString` will convert the given value to string
|
|
4062
|
+
* This is useful and used in the `templateParameters` function
|
|
4063
|
+
*
|
|
4064
|
+
* Note: This function is not just calling `toString` method
|
|
4065
|
+
* It's more complex and can handle this conversion specifically for LLM models
|
|
4066
|
+
* See `VALUE_STRINGS`
|
|
4067
|
+
*
|
|
4068
|
+
* Note: There are 2 similar functions
|
|
4069
|
+
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
4070
|
+
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
4071
|
+
*
|
|
4072
|
+
* @public exported from `@promptbook/utils`
|
|
4073
|
+
*/
|
|
4074
|
+
function valueToString(value) {
|
|
4075
|
+
try {
|
|
4076
|
+
if (value === '') {
|
|
4077
|
+
return VALUE_STRINGS.empty;
|
|
4078
|
+
}
|
|
4079
|
+
else if (value === null) {
|
|
4080
|
+
return VALUE_STRINGS.null;
|
|
4081
|
+
}
|
|
4082
|
+
else if (value === undefined) {
|
|
4083
|
+
return VALUE_STRINGS.undefined;
|
|
4084
|
+
}
|
|
4085
|
+
else if (typeof value === 'string') {
|
|
4086
|
+
return value;
|
|
4087
|
+
}
|
|
4088
|
+
else if (typeof value === 'number') {
|
|
4089
|
+
return numberToString(value);
|
|
4090
|
+
}
|
|
4091
|
+
else if (value instanceof Date) {
|
|
4092
|
+
return value.toISOString();
|
|
4093
|
+
}
|
|
4094
|
+
else {
|
|
4095
|
+
try {
|
|
4096
|
+
return JSON.stringify(value);
|
|
4097
|
+
}
|
|
4098
|
+
catch (error) {
|
|
4099
|
+
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
4100
|
+
return VALUE_STRINGS.circular;
|
|
4101
|
+
}
|
|
4102
|
+
throw error;
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
catch (error) {
|
|
4107
|
+
assertsError(error);
|
|
4108
|
+
console.error(error);
|
|
4109
|
+
return VALUE_STRINGS.unserializable;
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
|
|
4113
|
+
/**
|
|
4114
|
+
* Replaces parameters in template with values from parameters object
|
|
4115
|
+
*
|
|
4116
|
+
* Note: This function is not places strings into string,
|
|
4117
|
+
* It's more complex and can handle this operation specifically for LLM models
|
|
4118
|
+
*
|
|
4119
|
+
* @param template the template with parameters in {curly} braces
|
|
4120
|
+
* @param parameters the object with parameters
|
|
4121
|
+
* @returns the template with replaced parameters
|
|
4122
|
+
* @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
|
|
4123
|
+
* @public exported from `@promptbook/utils`
|
|
4124
|
+
*/
|
|
4125
|
+
function templateParameters(template, parameters) {
|
|
4126
|
+
for (const [parameterName, parameterValue] of Object.entries(parameters)) {
|
|
4127
|
+
if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
|
|
4128
|
+
throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
|
|
4129
|
+
}
|
|
4130
|
+
else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
|
|
4131
|
+
// TODO: [🍵]
|
|
4132
|
+
throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
let replacedTemplates = template;
|
|
4136
|
+
let match;
|
|
4137
|
+
let loopLimit = LOOP_LIMIT;
|
|
4138
|
+
while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
|
|
4139
|
+
.exec(replacedTemplates))) {
|
|
4140
|
+
if (loopLimit-- < 0) {
|
|
4141
|
+
throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
|
|
4142
|
+
}
|
|
4143
|
+
const precol = match.groups.precol;
|
|
4144
|
+
const parameterName = match.groups.parameterName;
|
|
4145
|
+
if (parameterName === '') {
|
|
4146
|
+
// Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
|
|
4147
|
+
continue;
|
|
4148
|
+
}
|
|
4149
|
+
if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
|
|
4150
|
+
throw new PipelineExecutionError('Parameter is already opened or not closed');
|
|
4151
|
+
}
|
|
4152
|
+
if (parameters[parameterName] === undefined) {
|
|
4153
|
+
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
4154
|
+
}
|
|
4155
|
+
let parameterValue = parameters[parameterName];
|
|
4156
|
+
if (parameterValue === undefined) {
|
|
4157
|
+
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
4158
|
+
}
|
|
4159
|
+
parameterValue = valueToString(parameterValue);
|
|
4160
|
+
// Escape curly braces in parameter values to prevent prompt-injection
|
|
4161
|
+
parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
|
|
4162
|
+
if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
|
|
4163
|
+
parameterValue = parameterValue
|
|
4164
|
+
.split('\n')
|
|
4165
|
+
.map((line, index) => (index === 0 ? line : `${precol}${line}`))
|
|
4166
|
+
.join('\n');
|
|
4167
|
+
}
|
|
4168
|
+
replacedTemplates =
|
|
4169
|
+
replacedTemplates.substring(0, match.index + precol.length) +
|
|
4170
|
+
parameterValue +
|
|
4171
|
+
replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
|
|
4172
|
+
}
|
|
4173
|
+
// [💫] Check if there are parameters that are not closed properly
|
|
4174
|
+
if (/{\w+$/.test(replacedTemplates)) {
|
|
4175
|
+
throw new PipelineExecutionError('Parameter is not closed');
|
|
4176
|
+
}
|
|
4177
|
+
// [💫] Check if there are parameters that are not opened properly
|
|
4178
|
+
if (/^\w+}/.test(replacedTemplates)) {
|
|
4179
|
+
throw new PipelineExecutionError('Parameter is not opened');
|
|
4180
|
+
}
|
|
4181
|
+
return replacedTemplates;
|
|
4182
|
+
}
|
|
4183
|
+
|
|
4184
|
+
/**
|
|
4185
|
+
* Number of characters per standard line with 11pt Arial font size.
|
|
4186
|
+
*
|
|
4187
|
+
* @public exported from `@promptbook/utils`
|
|
4188
|
+
*/
|
|
4189
|
+
const CHARACTERS_PER_STANDARD_LINE = 63;
|
|
4190
|
+
/**
|
|
4191
|
+
* Number of lines per standard A4 page with 11pt Arial font size and standard margins and spacing.
|
|
4192
|
+
*
|
|
4193
|
+
* @public exported from `@promptbook/utils`
|
|
4194
|
+
*/
|
|
4195
|
+
const LINES_PER_STANDARD_PAGE = 44;
|
|
4196
|
+
/**
|
|
4197
|
+
* TODO: [🧠] Should be this `constants.ts` or `config.ts`?
|
|
4198
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4199
|
+
*/
|
|
4200
|
+
|
|
4201
|
+
/**
|
|
4202
|
+
* Counts number of characters in the text
|
|
4203
|
+
*
|
|
4204
|
+
* @public exported from `@promptbook/utils`
|
|
4205
|
+
*/
|
|
4206
|
+
function countCharacters(text) {
|
|
4207
|
+
// Remove null characters
|
|
4208
|
+
text = text.replace(/\0/g, '');
|
|
4209
|
+
// Replace emojis (and also ZWJ sequence) with hyphens
|
|
4210
|
+
text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
|
|
4211
|
+
text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
|
|
4212
|
+
text = text.replace(/\p{Extended_Pictographic}(\u{200D}\p{Extended_Pictographic})*/gu, '-');
|
|
4213
|
+
return text.length;
|
|
4214
|
+
}
|
|
4215
|
+
/**
|
|
4216
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4217
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4218
|
+
*/
|
|
4219
|
+
|
|
4220
|
+
/**
|
|
4221
|
+
* Counts number of lines in the text
|
|
4222
|
+
*
|
|
4223
|
+
* Note: This does not check only for the presence of newlines, but also for the length of the standard line.
|
|
4224
|
+
*
|
|
4225
|
+
* @public exported from `@promptbook/utils`
|
|
4226
|
+
*/
|
|
4227
|
+
function countLines(text) {
|
|
4228
|
+
if (text === '') {
|
|
4229
|
+
return 0;
|
|
4230
|
+
}
|
|
4231
|
+
text = text.replace('\r\n', '\n');
|
|
4232
|
+
text = text.replace('\r', '\n');
|
|
4233
|
+
const lines = text.split('\n');
|
|
4234
|
+
return lines.reduce((count, line) => count + Math.max(Math.ceil(line.length / CHARACTERS_PER_STANDARD_LINE), 1), 0);
|
|
4235
|
+
}
|
|
4236
|
+
/**
|
|
4237
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4238
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4239
|
+
*/
|
|
4240
|
+
|
|
4241
|
+
/**
|
|
4242
|
+
* Counts number of pages in the text
|
|
4243
|
+
*
|
|
4244
|
+
* Note: This does not check only for the count of newlines, but also for the length of the standard line and length of the standard page.
|
|
4245
|
+
*
|
|
4246
|
+
* @public exported from `@promptbook/utils`
|
|
4247
|
+
*/
|
|
4248
|
+
function countPages(text) {
|
|
4249
|
+
return Math.ceil(countLines(text) / LINES_PER_STANDARD_PAGE);
|
|
4250
|
+
}
|
|
4251
|
+
/**
|
|
4252
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4253
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4254
|
+
*/
|
|
4255
|
+
|
|
4256
|
+
/**
|
|
4257
|
+
* Counts number of paragraphs in the text
|
|
4258
|
+
*
|
|
4259
|
+
* @public exported from `@promptbook/utils`
|
|
4260
|
+
*/
|
|
4261
|
+
function countParagraphs(text) {
|
|
4262
|
+
return text.split(/\n\s*\n/).filter((paragraph) => paragraph.trim() !== '').length;
|
|
4263
|
+
}
|
|
4264
|
+
/**
|
|
4265
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4266
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4267
|
+
*/
|
|
4268
|
+
|
|
4269
|
+
/**
|
|
4270
|
+
* Split text into sentences
|
|
4271
|
+
*
|
|
4272
|
+
* @public exported from `@promptbook/utils`
|
|
4273
|
+
*/
|
|
4274
|
+
function splitIntoSentences(text) {
|
|
4275
|
+
return text.split(/[.!?]+/).filter((sentence) => sentence.trim() !== '');
|
|
4276
|
+
}
|
|
4277
|
+
/**
|
|
4278
|
+
* Counts number of sentences in the text
|
|
4279
|
+
*
|
|
4280
|
+
* @public exported from `@promptbook/utils`
|
|
4281
|
+
*/
|
|
4282
|
+
function countSentences(text) {
|
|
4283
|
+
return splitIntoSentences(text).length;
|
|
4284
|
+
}
|
|
4285
|
+
/**
|
|
4286
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4287
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4288
|
+
*/
|
|
4289
|
+
|
|
4290
|
+
/**
|
|
4291
|
+
* Counts number of words in the text
|
|
4292
|
+
*
|
|
4293
|
+
* @public exported from `@promptbook/utils`
|
|
4294
|
+
*/
|
|
4295
|
+
function countWords(text) {
|
|
4296
|
+
text = text.replace(/[\p{Extended_Pictographic}]/gu, 'a');
|
|
4297
|
+
text = removeDiacritics(text);
|
|
4298
|
+
// Add spaces before uppercase letters preceded by lowercase letters (for camelCase)
|
|
4299
|
+
text = text.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
4300
|
+
return text.split(/[^a-zа-я0-9]+/i).filter((word) => word.length > 0).length;
|
|
4301
|
+
}
|
|
4302
|
+
/**
|
|
4303
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4304
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4305
|
+
* TODO: [✌️] `countWords` should be just `splitWords(...).length`, and all other counters should use this pattern as well
|
|
4306
|
+
*/
|
|
4307
|
+
|
|
4308
|
+
/**
|
|
4309
|
+
* Index of all counter functions
|
|
4310
|
+
*
|
|
4311
|
+
* @public exported from `@promptbook/utils`
|
|
4312
|
+
*/
|
|
4313
|
+
const CountUtils = {
|
|
4314
|
+
CHARACTERS: countCharacters,
|
|
4315
|
+
WORDS: countWords,
|
|
4316
|
+
SENTENCES: countSentences,
|
|
4317
|
+
PARAGRAPHS: countParagraphs,
|
|
4318
|
+
LINES: countLines,
|
|
4319
|
+
PAGES: countPages,
|
|
4320
|
+
};
|
|
4321
|
+
/**
|
|
4322
|
+
* TODO: [🧠][🤠] This should be probably as part of `TextFormatParser`
|
|
4323
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4324
|
+
*/
|
|
4325
|
+
|
|
4326
|
+
/**
|
|
4327
|
+
* Simple wrapper `new Date().toISOString()`
|
|
4328
|
+
*
|
|
4329
|
+
* Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
|
|
4330
|
+
*
|
|
4331
|
+
* @returns string_date branded type
|
|
4332
|
+
* @public exported from `@promptbook/utils`
|
|
4333
|
+
*/
|
|
4334
|
+
function $getCurrentDate() {
|
|
4335
|
+
return new Date().toISOString();
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
/**
|
|
4339
|
+
* Computes SHA-256 hash of the given object
|
|
4340
|
+
*
|
|
4341
|
+
* @public exported from `@promptbook/utils`
|
|
4342
|
+
*/
|
|
4343
|
+
function computeHash(value) {
|
|
4344
|
+
return cryptoJs.SHA256(hexEncoder__default["default"].parse(spaceTrim__default["default"](valueToString(value)))).toString( /* hex */);
|
|
4345
|
+
}
|
|
4346
|
+
/**
|
|
4347
|
+
* TODO: [🥬][🥬] Use this ACRY
|
|
4348
|
+
*/
|
|
4349
|
+
|
|
4350
|
+
/**
|
|
4351
|
+
* Function parseNumber will parse number from string
|
|
4352
|
+
*
|
|
4353
|
+
* Note: [🔂] This function is idempotent.
|
|
4354
|
+
* Unlike Number.parseInt, Number.parseFloat it will never ever result in NaN
|
|
4355
|
+
* Note: it also works only with decimal numbers
|
|
4356
|
+
*
|
|
4357
|
+
* @returns parsed number
|
|
4358
|
+
* @throws {ParseError} if the value is not a number
|
|
4359
|
+
*
|
|
4360
|
+
* @public exported from `@promptbook/utils`
|
|
4361
|
+
*/
|
|
4362
|
+
function parseNumber(value) {
|
|
4363
|
+
const originalValue = value;
|
|
4364
|
+
if (typeof value === 'number') {
|
|
4365
|
+
value = value.toString(); // <- TODO: Maybe more efficient way to do this
|
|
3856
4366
|
}
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
4367
|
+
if (typeof value !== 'string') {
|
|
4368
|
+
return 0;
|
|
4369
|
+
}
|
|
4370
|
+
value = value.trim();
|
|
4371
|
+
if (value.startsWith('+')) {
|
|
4372
|
+
return parseNumber(value.substring(1));
|
|
4373
|
+
}
|
|
4374
|
+
if (value.startsWith('-')) {
|
|
4375
|
+
const number = parseNumber(value.substring(1));
|
|
4376
|
+
if (number === 0) {
|
|
4377
|
+
return 0; // <- Note: To prevent -0
|
|
4378
|
+
}
|
|
4379
|
+
return -number;
|
|
4380
|
+
}
|
|
4381
|
+
value = value.replace(/,/g, '.');
|
|
4382
|
+
value = value.toUpperCase();
|
|
4383
|
+
if (value === '') {
|
|
4384
|
+
return 0;
|
|
4385
|
+
}
|
|
4386
|
+
if (value === '♾' || value.startsWith('INF')) {
|
|
4387
|
+
return Infinity;
|
|
4388
|
+
}
|
|
4389
|
+
if (value.includes('/')) {
|
|
4390
|
+
const [numerator_, denominator_] = value.split('/');
|
|
4391
|
+
const numerator = parseNumber(numerator_);
|
|
4392
|
+
const denominator = parseNumber(denominator_);
|
|
4393
|
+
if (denominator === 0) {
|
|
4394
|
+
throw new ParseError(`Unable to parse number from "${originalValue}" because denominator is zero`);
|
|
4395
|
+
}
|
|
4396
|
+
return numerator / denominator;
|
|
4397
|
+
}
|
|
4398
|
+
if (/^(NAN|NULL|NONE|UNDEFINED|ZERO|NO.*)$/.test(value)) {
|
|
4399
|
+
return 0;
|
|
4400
|
+
}
|
|
4401
|
+
if (value.includes('E')) {
|
|
4402
|
+
const [significand, exponent] = value.split('E');
|
|
4403
|
+
return parseNumber(significand) * 10 ** parseNumber(exponent);
|
|
4404
|
+
}
|
|
4405
|
+
if (!/^[0-9.]+$/.test(value) || value.split('.').length > 2) {
|
|
4406
|
+
throw new ParseError(`Unable to parse number from "${originalValue}"`);
|
|
4407
|
+
}
|
|
4408
|
+
const num = parseFloat(value);
|
|
4409
|
+
if (isNaN(num)) {
|
|
4410
|
+
throw new ParseError(`Unexpected NaN when parsing number from "${originalValue}"`);
|
|
4411
|
+
}
|
|
4412
|
+
return num;
|
|
4413
|
+
}
|
|
4414
|
+
/**
|
|
4415
|
+
* TODO: Maybe use sth. like safe-eval in fraction/calculation case @see https://www.npmjs.com/package/safe-eval
|
|
4416
|
+
* TODO: [🧠][🌻] Maybe export through `@promptbook/markdown-utils` not `@promptbook/utils`
|
|
4417
|
+
*/
|
|
4418
|
+
|
|
4419
|
+
/**
|
|
4420
|
+
* Makes first letter of a string uppercase
|
|
4421
|
+
*
|
|
4422
|
+
* Note: [🔂] This function is idempotent.
|
|
4423
|
+
*
|
|
4424
|
+
* @public exported from `@promptbook/utils`
|
|
4425
|
+
*/
|
|
4426
|
+
function capitalize(word) {
|
|
4427
|
+
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
4428
|
+
}
|
|
4429
|
+
|
|
4430
|
+
/**
|
|
4431
|
+
* Makes first letter of a string lowercase
|
|
4432
|
+
*
|
|
4433
|
+
* Note: [🔂] This function is idempotent.
|
|
4434
|
+
*
|
|
4435
|
+
* @public exported from `@promptbook/utils`
|
|
4436
|
+
*/
|
|
4437
|
+
function decapitalize(word) {
|
|
4438
|
+
return word.substring(0, 1).toLowerCase() + word.substring(1);
|
|
4439
|
+
}
|
|
4440
|
+
|
|
4441
|
+
/**
|
|
4442
|
+
* Parses keywords from a string
|
|
4443
|
+
*
|
|
4444
|
+
* @param {string} input
|
|
4445
|
+
* @returns {Set} of keywords without diacritics in lowercase
|
|
4446
|
+
* @public exported from `@promptbook/utils`
|
|
4447
|
+
*/
|
|
4448
|
+
function parseKeywordsFromString(input) {
|
|
4449
|
+
const keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
|
|
4450
|
+
.toLowerCase()
|
|
4451
|
+
.split(/[^a-z0-9]+/gs)
|
|
4452
|
+
.filter((value) => value);
|
|
4453
|
+
return new Set(keywords);
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
/**
|
|
4457
|
+
* Converts a name string into a URI-compatible format.
|
|
4458
|
+
*
|
|
4459
|
+
* @param name The string to be converted to a URI-compatible format.
|
|
4460
|
+
* @returns A URI-compatible string derived from the input name.
|
|
4461
|
+
* @example 'Hello World' -> 'hello-world'
|
|
4462
|
+
* @public exported from `@promptbook/utils`
|
|
4463
|
+
*/
|
|
4464
|
+
function nameToUriPart(name) {
|
|
4465
|
+
let uriPart = name;
|
|
4466
|
+
uriPart = uriPart.toLowerCase();
|
|
4467
|
+
uriPart = removeDiacritics(uriPart);
|
|
4468
|
+
uriPart = uriPart.replace(/[^a-zA-Z0-9]+/g, '-');
|
|
4469
|
+
uriPart = uriPart.replace(/^-+/, '');
|
|
4470
|
+
uriPart = uriPart.replace(/-+$/, '');
|
|
4471
|
+
return uriPart;
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
/**
|
|
4475
|
+
* Converts a given name into URI-compatible parts.
|
|
4476
|
+
*
|
|
4477
|
+
* @param name The name to be converted into URI parts.
|
|
4478
|
+
* @returns An array of URI-compatible parts derived from the name.
|
|
4479
|
+
* @example 'Example Name' -> ['example', 'name']
|
|
4480
|
+
* @public exported from `@promptbook/utils`
|
|
4481
|
+
*/
|
|
4482
|
+
function nameToUriParts(name) {
|
|
4483
|
+
return nameToUriPart(name)
|
|
4484
|
+
.split('-')
|
|
4485
|
+
.filter((value) => value !== '');
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
/**
|
|
4489
|
+
* Normalizes a given text to PascalCase format.
|
|
4490
|
+
*
|
|
4491
|
+
* Note: [🔂] This function is idempotent.
|
|
4492
|
+
*
|
|
4493
|
+
* @param text @public exported from `@promptbook/utils`
|
|
4494
|
+
* @returns
|
|
4495
|
+
* @example 'HelloWorld'
|
|
4496
|
+
* @example 'ILovePromptbook'
|
|
4497
|
+
* @public exported from `@promptbook/utils`
|
|
4498
|
+
*/
|
|
4499
|
+
function normalizeTo_PascalCase(text) {
|
|
4500
|
+
return normalizeTo_camelCase(text, true);
|
|
4501
|
+
}
|
|
4502
|
+
|
|
4503
|
+
/**
|
|
4504
|
+
* Take every whitespace (space, new line, tab) and replace it with a single space
|
|
4505
|
+
*
|
|
4506
|
+
* Note: [🔂] This function is idempotent.
|
|
4507
|
+
*
|
|
4508
|
+
* @public exported from `@promptbook/utils`
|
|
4509
|
+
*/
|
|
4510
|
+
function normalizeWhitespaces(sentence) {
|
|
4511
|
+
return sentence.replace(/\s+/gs, ' ').trim();
|
|
4512
|
+
}
|
|
4513
|
+
|
|
4514
|
+
/**
|
|
4515
|
+
* Removes quotes from a string
|
|
4516
|
+
*
|
|
4517
|
+
* Note: [🔂] This function is idempotent.
|
|
4518
|
+
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
4519
|
+
* Note: This function removes only the same quotes from the beginning and the end of the string
|
|
4520
|
+
* Note: There are two similar functions:
|
|
4521
|
+
* - `removeQuotes` which removes only bounding quotes
|
|
4522
|
+
* - `unwrapResult` which removes whole introduce sentence
|
|
4523
|
+
*
|
|
4524
|
+
* @param text optionally quoted text
|
|
4525
|
+
* @returns text without quotes
|
|
4526
|
+
* @public exported from `@promptbook/utils`
|
|
4527
|
+
*/
|
|
4528
|
+
function removeQuotes(text) {
|
|
4529
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
4530
|
+
return text.slice(1, -1);
|
|
3861
4531
|
}
|
|
3862
|
-
if (
|
|
3863
|
-
|
|
4532
|
+
if (text.startsWith("'") && text.endsWith("'")) {
|
|
4533
|
+
return text.slice(1, -1);
|
|
3864
4534
|
}
|
|
3865
|
-
return
|
|
4535
|
+
return text;
|
|
3866
4536
|
}
|
|
4537
|
+
|
|
3867
4538
|
/**
|
|
3868
|
-
*
|
|
3869
|
-
*
|
|
4539
|
+
* Adds suffix to the URL
|
|
4540
|
+
*
|
|
4541
|
+
* @public exported from `@promptbook/utils`
|
|
3870
4542
|
*/
|
|
4543
|
+
function suffixUrl(value, suffix) {
|
|
4544
|
+
const baseUrl = value.href.endsWith('/') ? value.href.slice(0, -1) : value.href;
|
|
4545
|
+
const normalizedSuffix = suffix.replace(/\/+/g, '/');
|
|
4546
|
+
return (baseUrl + normalizedSuffix);
|
|
4547
|
+
}
|
|
3871
4548
|
|
|
3872
4549
|
/**
|
|
3873
|
-
*
|
|
4550
|
+
* Removes quotes and optional introduce text from a string
|
|
4551
|
+
*
|
|
4552
|
+
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
4553
|
+
* Note: This function trims the text and removes whole introduce sentence if it is present
|
|
4554
|
+
* Note: There are two similar functions:
|
|
4555
|
+
* - `removeQuotes` which removes only bounding quotes
|
|
4556
|
+
* - `unwrapResult` which removes whole introduce sentence
|
|
3874
4557
|
*
|
|
4558
|
+
* @param text optionally quoted text
|
|
4559
|
+
* @returns text without quotes
|
|
3875
4560
|
* @public exported from `@promptbook/utils`
|
|
3876
4561
|
*/
|
|
3877
|
-
function
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
//
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
4562
|
+
function unwrapResult(text, options) {
|
|
4563
|
+
const { isTrimmed = true, isIntroduceSentenceRemoved = true } = options || {};
|
|
4564
|
+
let trimmedText = text;
|
|
4565
|
+
// Remove leading and trailing spaces and newlines
|
|
4566
|
+
if (isTrimmed) {
|
|
4567
|
+
trimmedText = spaceTrim$1.spaceTrim(trimmedText);
|
|
4568
|
+
}
|
|
4569
|
+
let processedText = trimmedText;
|
|
4570
|
+
if (isIntroduceSentenceRemoved) {
|
|
4571
|
+
const introduceSentenceRegex = /^[a-zěščřžýáíéúů:\s]*:\s*/i;
|
|
4572
|
+
if (introduceSentenceRegex.test(text)) {
|
|
4573
|
+
// Remove the introduce sentence and quotes by replacing it with an empty string
|
|
4574
|
+
processedText = processedText.replace(introduceSentenceRegex, '');
|
|
4575
|
+
}
|
|
4576
|
+
processedText = spaceTrim$1.spaceTrim(processedText);
|
|
4577
|
+
}
|
|
4578
|
+
if (processedText.length < 3) {
|
|
4579
|
+
return trimmedText;
|
|
4580
|
+
}
|
|
4581
|
+
if (processedText.includes('\n')) {
|
|
4582
|
+
return trimmedText;
|
|
4583
|
+
}
|
|
4584
|
+
// Remove the quotes by extracting the substring without the first and last characters
|
|
4585
|
+
const unquotedText = processedText.slice(1, -1);
|
|
4586
|
+
// Check if the text starts and ends with quotes
|
|
4587
|
+
if ([
|
|
4588
|
+
['"', '"'],
|
|
4589
|
+
["'", "'"],
|
|
4590
|
+
['`', '`'],
|
|
4591
|
+
['*', '*'],
|
|
4592
|
+
['_', '_'],
|
|
4593
|
+
['„', '“'],
|
|
4594
|
+
['«', '»'] /* <- QUOTES to config */,
|
|
4595
|
+
].some(([startQuote, endQuote]) => {
|
|
4596
|
+
if (!processedText.startsWith(startQuote)) {
|
|
4597
|
+
return false;
|
|
4598
|
+
}
|
|
4599
|
+
if (!processedText.endsWith(endQuote)) {
|
|
4600
|
+
return false;
|
|
4601
|
+
}
|
|
4602
|
+
if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
|
|
4603
|
+
return false;
|
|
4604
|
+
}
|
|
4605
|
+
if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
|
|
4606
|
+
return false;
|
|
4607
|
+
}
|
|
4608
|
+
return true;
|
|
4609
|
+
})) {
|
|
4610
|
+
return unwrapResult(unquotedText, { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
4611
|
+
}
|
|
4612
|
+
else {
|
|
4613
|
+
return processedText;
|
|
4614
|
+
}
|
|
3885
4615
|
}
|
|
3886
4616
|
/**
|
|
3887
|
-
* TODO: [
|
|
3888
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4617
|
+
* TODO: [🧠] Should this also unwrap the (parenthesis)
|
|
3889
4618
|
*/
|
|
3890
4619
|
|
|
3891
4620
|
/**
|
|
3892
|
-
*
|
|
4621
|
+
* Parses the task and returns the list of all parameter names
|
|
3893
4622
|
*
|
|
4623
|
+
* @param template the string template with parameters in {curly} braces
|
|
4624
|
+
* @returns the list of parameter names
|
|
3894
4625
|
* @public exported from `@promptbook/utils`
|
|
3895
4626
|
*/
|
|
3896
|
-
|
|
4627
|
+
function extractParameterNames(template) {
|
|
4628
|
+
const matches = template.matchAll(/{\w+}/g);
|
|
4629
|
+
const parameterNames = new Set();
|
|
4630
|
+
for (const match of matches) {
|
|
4631
|
+
const parameterName = match[0].slice(1, -1);
|
|
4632
|
+
parameterNames.add(parameterName);
|
|
4633
|
+
}
|
|
4634
|
+
return parameterNames;
|
|
4635
|
+
}
|
|
4636
|
+
|
|
3897
4637
|
/**
|
|
3898
|
-
*
|
|
3899
|
-
|
|
4638
|
+
* Recursively converts JSON strings to JSON objects
|
|
4639
|
+
|
|
3900
4640
|
* @public exported from `@promptbook/utils`
|
|
3901
4641
|
*/
|
|
3902
|
-
|
|
4642
|
+
function jsonStringsToJsons(object) {
|
|
4643
|
+
if (object === null) {
|
|
4644
|
+
return object;
|
|
4645
|
+
}
|
|
4646
|
+
if (Array.isArray(object)) {
|
|
4647
|
+
return object.map(jsonStringsToJsons);
|
|
4648
|
+
}
|
|
4649
|
+
if (typeof object !== 'object') {
|
|
4650
|
+
return object;
|
|
4651
|
+
}
|
|
4652
|
+
const newObject = { ...object };
|
|
4653
|
+
for (const [key, value] of Object.entries(object)) {
|
|
4654
|
+
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
4655
|
+
newObject[key] = jsonParse(value);
|
|
4656
|
+
}
|
|
4657
|
+
else {
|
|
4658
|
+
newObject[key] = jsonStringsToJsons(value);
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
return newObject;
|
|
4662
|
+
}
|
|
3903
4663
|
/**
|
|
3904
|
-
* TODO:
|
|
3905
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4664
|
+
* TODO: Type the return type correctly
|
|
3906
4665
|
*/
|
|
3907
4666
|
|
|
3908
4667
|
/**
|
|
3909
|
-
*
|
|
3910
|
-
*
|
|
3911
|
-
* Note: This does not check only for the presence of newlines, but also for the length of the standard line.
|
|
4668
|
+
* Create difference set of two sets.
|
|
3912
4669
|
*
|
|
4670
|
+
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
3913
4671
|
* @public exported from `@promptbook/utils`
|
|
3914
4672
|
*/
|
|
3915
|
-
function
|
|
3916
|
-
|
|
3917
|
-
|
|
4673
|
+
function difference(a, b, isEqual = (a, b) => a === b) {
|
|
4674
|
+
const diff = new Set();
|
|
4675
|
+
for (const itemA of Array.from(a)) {
|
|
4676
|
+
if (!Array.from(b).some((itemB) => isEqual(itemA, itemB))) {
|
|
4677
|
+
diff.add(itemA);
|
|
4678
|
+
}
|
|
3918
4679
|
}
|
|
3919
|
-
|
|
3920
|
-
text = text.replace('\r', '\n');
|
|
3921
|
-
const lines = text.split('\n');
|
|
3922
|
-
return lines.reduce((count, line) => count + Math.max(Math.ceil(line.length / CHARACTERS_PER_STANDARD_LINE), 1), 0);
|
|
4680
|
+
return diff;
|
|
3923
4681
|
}
|
|
3924
4682
|
/**
|
|
3925
|
-
* TODO: [
|
|
3926
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4683
|
+
* TODO: [🧠][💯] Maybe also implement symmetricDifference
|
|
3927
4684
|
*/
|
|
3928
4685
|
|
|
3929
4686
|
/**
|
|
3930
|
-
*
|
|
3931
|
-
*
|
|
3932
|
-
* Note: This does not check only for the count of newlines, but also for the length of the standard line and length of the standard page.
|
|
4687
|
+
* Creates a new set with all elements that are present in either set
|
|
3933
4688
|
*
|
|
4689
|
+
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
3934
4690
|
* @public exported from `@promptbook/utils`
|
|
3935
4691
|
*/
|
|
3936
|
-
function
|
|
3937
|
-
|
|
4692
|
+
function union(...sets) {
|
|
4693
|
+
const union = new Set();
|
|
4694
|
+
for (const set of sets) {
|
|
4695
|
+
for (const item of Array.from(set)) {
|
|
4696
|
+
union.add(item);
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
return union;
|
|
3938
4700
|
}
|
|
4701
|
+
|
|
3939
4702
|
/**
|
|
3940
|
-
*
|
|
3941
|
-
*
|
|
4703
|
+
* Checks if value is valid email
|
|
4704
|
+
*
|
|
4705
|
+
* @public exported from `@promptbook/utils`
|
|
3942
4706
|
*/
|
|
4707
|
+
function isValidEmail(email) {
|
|
4708
|
+
if (typeof email !== 'string') {
|
|
4709
|
+
return false;
|
|
4710
|
+
}
|
|
4711
|
+
if (email.split('\n').length > 1) {
|
|
4712
|
+
return false;
|
|
4713
|
+
}
|
|
4714
|
+
return /^.+@.+\..+$/.test(email);
|
|
4715
|
+
}
|
|
3943
4716
|
|
|
3944
4717
|
/**
|
|
3945
|
-
*
|
|
4718
|
+
* Checks if the given value is a valid JavaScript identifier name.
|
|
3946
4719
|
*
|
|
4720
|
+
* @param javascriptName The value to check for JavaScript identifier validity.
|
|
4721
|
+
* @returns `true` if the value is a valid JavaScript name, false otherwise.
|
|
3947
4722
|
* @public exported from `@promptbook/utils`
|
|
3948
4723
|
*/
|
|
3949
|
-
function
|
|
3950
|
-
|
|
4724
|
+
function isValidJavascriptName(javascriptName) {
|
|
4725
|
+
if (typeof javascriptName !== 'string') {
|
|
4726
|
+
return false;
|
|
4727
|
+
}
|
|
4728
|
+
return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/i.test(javascriptName);
|
|
3951
4729
|
}
|
|
4730
|
+
|
|
3952
4731
|
/**
|
|
3953
|
-
*
|
|
3954
|
-
*
|
|
4732
|
+
* Tests if given string is valid semantic version
|
|
4733
|
+
*
|
|
4734
|
+
* Note: There are two similar functions:
|
|
4735
|
+
* - `isValidSemanticVersion` which tests any semantic version
|
|
4736
|
+
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
4737
|
+
*
|
|
4738
|
+
* @public exported from `@promptbook/utils`
|
|
3955
4739
|
*/
|
|
4740
|
+
function isValidSemanticVersion(version) {
|
|
4741
|
+
if (typeof version !== 'string') {
|
|
4742
|
+
return false;
|
|
4743
|
+
}
|
|
4744
|
+
if (version.startsWith('0.0.0')) {
|
|
4745
|
+
return false;
|
|
4746
|
+
}
|
|
4747
|
+
return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
|
|
4748
|
+
}
|
|
3956
4749
|
|
|
3957
4750
|
/**
|
|
3958
|
-
*
|
|
4751
|
+
* Tests if given string is valid promptbook version
|
|
4752
|
+
* It looks into list of known promptbook versions.
|
|
4753
|
+
*
|
|
4754
|
+
* @see https://www.npmjs.com/package/promptbook?activeTab=versions
|
|
4755
|
+
* Note: When you are using for example promptbook 2.0.0 and there already is promptbook 3.0.0 it don`t know about it.
|
|
4756
|
+
* Note: There are two similar functions:
|
|
4757
|
+
* - `isValidSemanticVersion` which tests any semantic version
|
|
4758
|
+
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
3959
4759
|
*
|
|
3960
4760
|
* @public exported from `@promptbook/utils`
|
|
3961
4761
|
*/
|
|
3962
|
-
function
|
|
3963
|
-
|
|
4762
|
+
function isValidPromptbookVersion(version) {
|
|
4763
|
+
if (!isValidSemanticVersion(version)) {
|
|
4764
|
+
return false;
|
|
4765
|
+
}
|
|
4766
|
+
if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
|
|
4767
|
+
return false;
|
|
4768
|
+
}
|
|
4769
|
+
// <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
|
|
4770
|
+
return true;
|
|
3964
4771
|
}
|
|
4772
|
+
|
|
3965
4773
|
/**
|
|
3966
|
-
*
|
|
4774
|
+
* Tests if given string is valid pipeline URL URL.
|
|
4775
|
+
*
|
|
4776
|
+
* Note: There are two similar functions:
|
|
4777
|
+
* - `isValidUrl` which tests any URL
|
|
4778
|
+
* - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
|
|
3967
4779
|
*
|
|
3968
4780
|
* @public exported from `@promptbook/utils`
|
|
3969
4781
|
*/
|
|
3970
|
-
function
|
|
3971
|
-
|
|
4782
|
+
function isValidPipelineUrl(url) {
|
|
4783
|
+
if (!isValidUrl(url)) {
|
|
4784
|
+
return false;
|
|
4785
|
+
}
|
|
4786
|
+
if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
|
|
4787
|
+
return false;
|
|
4788
|
+
}
|
|
4789
|
+
if (url.includes('#')) {
|
|
4790
|
+
// TODO: [🐠]
|
|
4791
|
+
return false;
|
|
4792
|
+
}
|
|
4793
|
+
/*
|
|
4794
|
+
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
4795
|
+
if (isUrlOnPrivateNetwork(url)) {
|
|
4796
|
+
return false;
|
|
4797
|
+
}
|
|
4798
|
+
*/
|
|
4799
|
+
return true;
|
|
3972
4800
|
}
|
|
3973
4801
|
/**
|
|
3974
|
-
* TODO: [
|
|
3975
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4802
|
+
* TODO: [🐠] Maybe more info why the URL is invalid
|
|
3976
4803
|
*/
|
|
3977
4804
|
|
|
3978
4805
|
/**
|
|
3979
|
-
*
|
|
4806
|
+
* Extracts all code blocks from markdown.
|
|
3980
4807
|
*
|
|
3981
|
-
*
|
|
4808
|
+
* Note: There are multiple similar functions:
|
|
4809
|
+
* - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
|
|
4810
|
+
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
4811
|
+
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
4812
|
+
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
4813
|
+
*
|
|
4814
|
+
* @param markdown any valid markdown
|
|
4815
|
+
* @returns code blocks with language and content
|
|
4816
|
+
* @throws {ParseError} if block is not closed properly
|
|
4817
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
3982
4818
|
*/
|
|
3983
|
-
function
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
//
|
|
3987
|
-
|
|
3988
|
-
|
|
4819
|
+
function extractAllBlocksFromMarkdown(markdown) {
|
|
4820
|
+
const codeBlocks = [];
|
|
4821
|
+
const lines = markdown.split('\n');
|
|
4822
|
+
// Note: [0] Ensure that the last block notated by gt > will be closed
|
|
4823
|
+
lines.push('');
|
|
4824
|
+
let currentCodeBlock = null;
|
|
4825
|
+
for (const line of lines) {
|
|
4826
|
+
if (line.startsWith('> ') || line === '>') {
|
|
4827
|
+
if (currentCodeBlock === null) {
|
|
4828
|
+
currentCodeBlock = { blockNotation: '>', language: null, content: '' };
|
|
4829
|
+
} /* not else */
|
|
4830
|
+
if (currentCodeBlock.blockNotation === '>') {
|
|
4831
|
+
if (currentCodeBlock.content !== '') {
|
|
4832
|
+
currentCodeBlock.content += '\n';
|
|
4833
|
+
}
|
|
4834
|
+
currentCodeBlock.content += line.slice(2);
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '>' /* <- Note: [0] */) {
|
|
4838
|
+
codeBlocks.push(currentCodeBlock);
|
|
4839
|
+
currentCodeBlock = null;
|
|
4840
|
+
}
|
|
4841
|
+
/* not else */
|
|
4842
|
+
if (line.startsWith('```')) {
|
|
4843
|
+
const language = line.slice(3).trim() || null;
|
|
4844
|
+
if (currentCodeBlock === null) {
|
|
4845
|
+
currentCodeBlock = { blockNotation: '```', language, content: '' };
|
|
4846
|
+
}
|
|
4847
|
+
else {
|
|
4848
|
+
if (language !== null) {
|
|
4849
|
+
throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
|
|
4850
|
+
}
|
|
4851
|
+
codeBlocks.push(currentCodeBlock);
|
|
4852
|
+
currentCodeBlock = null;
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '```') {
|
|
4856
|
+
if (currentCodeBlock.content !== '') {
|
|
4857
|
+
currentCodeBlock.content += '\n';
|
|
4858
|
+
}
|
|
4859
|
+
currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
if (currentCodeBlock !== null) {
|
|
4863
|
+
throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed at the end of the markdown`);
|
|
4864
|
+
}
|
|
4865
|
+
return codeBlocks;
|
|
3989
4866
|
}
|
|
3990
4867
|
/**
|
|
3991
|
-
* TODO:
|
|
3992
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
3993
|
-
* TODO: [✌️] `countWords` should be just `splitWords(...).length`, and all other counters should use this pattern as well
|
|
4868
|
+
* TODO: Maybe name for `blockNotation` instead of '```' and '>'
|
|
3994
4869
|
*/
|
|
3995
4870
|
|
|
3996
4871
|
/**
|
|
3997
|
-
*
|
|
4872
|
+
* Extracts extracts exactly one valid JSON code block
|
|
3998
4873
|
*
|
|
3999
|
-
*
|
|
4874
|
+
* - When given string is a valid JSON as it is, it just returns it
|
|
4875
|
+
* - When there is no JSON code block the function throws a `ParseError`
|
|
4876
|
+
* - When there are multiple JSON code blocks the function throws a `ParseError`
|
|
4877
|
+
*
|
|
4878
|
+
* Note: It is not important if marked as ```json BUT if it is VALID JSON
|
|
4879
|
+
* Note: There are multiple similar function:
|
|
4880
|
+
* - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
|
|
4881
|
+
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
4882
|
+
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
4883
|
+
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
4884
|
+
*
|
|
4885
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
4886
|
+
* @throws {ParseError} if there is no valid JSON block in the markdown
|
|
4000
4887
|
*/
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4888
|
+
function extractJsonBlock(markdown) {
|
|
4889
|
+
if (isValidJsonString(markdown)) {
|
|
4890
|
+
return markdown;
|
|
4891
|
+
}
|
|
4892
|
+
const codeBlocks = extractAllBlocksFromMarkdown(markdown);
|
|
4893
|
+
const jsonBlocks = codeBlocks.filter(({ content }) => isValidJsonString(content));
|
|
4894
|
+
if (jsonBlocks.length === 0) {
|
|
4895
|
+
throw new Error('There is no valid JSON block in the markdown');
|
|
4896
|
+
}
|
|
4897
|
+
if (jsonBlocks.length > 1) {
|
|
4898
|
+
throw new Error('There are multiple JSON code blocks in the markdown');
|
|
4899
|
+
}
|
|
4900
|
+
return jsonBlocks[0].content;
|
|
4901
|
+
}
|
|
4009
4902
|
/**
|
|
4010
|
-
* TODO:
|
|
4011
|
-
*
|
|
4903
|
+
* TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
|
|
4904
|
+
* TODO: [🏢] Make this logic part of `JsonFormatParser` or `isValidJsonString`
|
|
4012
4905
|
*/
|
|
4013
4906
|
|
|
4014
4907
|
/**
|
|
@@ -4150,35 +5043,6 @@
|
|
|
4150
5043
|
}
|
|
4151
5044
|
}
|
|
4152
5045
|
|
|
4153
|
-
/**
|
|
4154
|
-
* Simple wrapper `new Date().toISOString()`
|
|
4155
|
-
*
|
|
4156
|
-
* Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
|
|
4157
|
-
*
|
|
4158
|
-
* @returns string_date branded type
|
|
4159
|
-
* @public exported from `@promptbook/utils`
|
|
4160
|
-
*/
|
|
4161
|
-
function $getCurrentDate() {
|
|
4162
|
-
return new Date().toISOString();
|
|
4163
|
-
}
|
|
4164
|
-
|
|
4165
|
-
/**
|
|
4166
|
-
* Parses the task and returns the list of all parameter names
|
|
4167
|
-
*
|
|
4168
|
-
* @param template the string template with parameters in {curly} braces
|
|
4169
|
-
* @returns the list of parameter names
|
|
4170
|
-
* @public exported from `@promptbook/utils`
|
|
4171
|
-
*/
|
|
4172
|
-
function extractParameterNames(template) {
|
|
4173
|
-
const matches = template.matchAll(/{\w+}/g);
|
|
4174
|
-
const parameterNames = new Set();
|
|
4175
|
-
for (const match of matches) {
|
|
4176
|
-
const parameterName = match[0].slice(1, -1);
|
|
4177
|
-
parameterNames.add(parameterName);
|
|
4178
|
-
}
|
|
4179
|
-
return parameterNames;
|
|
4180
|
-
}
|
|
4181
|
-
|
|
4182
5046
|
/**
|
|
4183
5047
|
* Intercepts LLM tools and counts total usage of the tools
|
|
4184
5048
|
*
|
|
@@ -4254,6 +5118,9 @@
|
|
|
4254
5118
|
case 'EMBEDDING':
|
|
4255
5119
|
promptResult = await llmTools.callEmbeddingModel(prompt);
|
|
4256
5120
|
break variant;
|
|
5121
|
+
case 'IMAGE_GENERATION':
|
|
5122
|
+
promptResult = await llmTools.callImageGenerationModel(prompt);
|
|
5123
|
+
break variant;
|
|
4257
5124
|
// <- case [🤖]:
|
|
4258
5125
|
default:
|
|
4259
5126
|
throw new PipelineExecutionError(`Unknown model variant "${prompt.modelRequirements.modelVariant}"`);
|
|
@@ -4290,12 +5157,13 @@
|
|
|
4290
5157
|
}
|
|
4291
5158
|
}
|
|
4292
5159
|
catch (error) {
|
|
5160
|
+
assertsError(error);
|
|
4293
5161
|
// If validation throws an unexpected error, don't cache
|
|
4294
5162
|
shouldCache = false;
|
|
4295
5163
|
if (isVerbose) {
|
|
4296
5164
|
console.info('Not caching result due to validation error for key:', key, {
|
|
4297
5165
|
content: promptResult.content,
|
|
4298
|
-
validationError:
|
|
5166
|
+
validationError: serializeError(error),
|
|
4299
5167
|
});
|
|
4300
5168
|
}
|
|
4301
5169
|
}
|
|
@@ -4341,6 +5209,11 @@
|
|
|
4341
5209
|
return /* not await */ callCommonModel(prompt);
|
|
4342
5210
|
};
|
|
4343
5211
|
}
|
|
5212
|
+
if (llmTools.callImageGenerationModel !== undefined) {
|
|
5213
|
+
proxyTools.callImageGenerationModel = async (prompt) => {
|
|
5214
|
+
return /* not await */ callCommonModel(prompt);
|
|
5215
|
+
};
|
|
5216
|
+
}
|
|
4344
5217
|
// <- Note: [🤖]
|
|
4345
5218
|
return proxyTools;
|
|
4346
5219
|
}
|
|
@@ -4529,6 +5402,15 @@
|
|
|
4529
5402
|
return promptResult;
|
|
4530
5403
|
};
|
|
4531
5404
|
}
|
|
5405
|
+
if (llmTools.callImageGenerationModel !== undefined) {
|
|
5406
|
+
proxyTools.callImageGenerationModel = async (prompt) => {
|
|
5407
|
+
// console.info('[🚕] callImageGenerationModel through countTotalUsage');
|
|
5408
|
+
const promptResult = await llmTools.callImageGenerationModel(prompt);
|
|
5409
|
+
totalUsage = addUsage(totalUsage, promptResult.usage);
|
|
5410
|
+
spending.next(promptResult.usage);
|
|
5411
|
+
return promptResult;
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
4532
5414
|
// <- Note: [🤖]
|
|
4533
5415
|
return proxyTools;
|
|
4534
5416
|
}
|
|
@@ -4537,7 +5419,7 @@
|
|
|
4537
5419
|
* TODO: [🧠] Is there some meaningfull way how to test this util
|
|
4538
5420
|
* TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
|
|
4539
5421
|
* > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
|
|
4540
|
-
* TODO: [👷♂️]
|
|
5422
|
+
* TODO: [👷♂️] Write a comprehensive manual explaining the construction and usage of LLM tools in the Promptbook ecosystem
|
|
4541
5423
|
*/
|
|
4542
5424
|
|
|
4543
5425
|
/**
|
|
@@ -4651,6 +5533,12 @@
|
|
|
4651
5533
|
callEmbeddingModel(prompt) {
|
|
4652
5534
|
return this.callCommonModel(prompt);
|
|
4653
5535
|
}
|
|
5536
|
+
/**
|
|
5537
|
+
* Calls the best available embedding model
|
|
5538
|
+
*/
|
|
5539
|
+
callImageGenerationModel(prompt) {
|
|
5540
|
+
return this.callCommonModel(prompt);
|
|
5541
|
+
}
|
|
4654
5542
|
// <- Note: [🤖]
|
|
4655
5543
|
/**
|
|
4656
5544
|
* Calls the best available model
|
|
@@ -4677,6 +5565,11 @@
|
|
|
4677
5565
|
continue llm;
|
|
4678
5566
|
}
|
|
4679
5567
|
return await llmExecutionTools.callEmbeddingModel(prompt);
|
|
5568
|
+
case 'IMAGE_GENERATION':
|
|
5569
|
+
if (llmExecutionTools.callImageGenerationModel === undefined) {
|
|
5570
|
+
continue llm;
|
|
5571
|
+
}
|
|
5572
|
+
return await llmExecutionTools.callImageGenerationModel(prompt);
|
|
4680
5573
|
// <- case [🤖]:
|
|
4681
5574
|
default:
|
|
4682
5575
|
throw new UnexpectedError(`Unknown model variant "${prompt.modelRequirements.modelVariant}" in ${llmExecutionTools.title}`);
|
|
@@ -5001,21 +5894,6 @@
|
|
|
5001
5894
|
* TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
|
|
5002
5895
|
*/
|
|
5003
5896
|
|
|
5004
|
-
/**
|
|
5005
|
-
* Checks if value is valid email
|
|
5006
|
-
*
|
|
5007
|
-
* @public exported from `@promptbook/utils`
|
|
5008
|
-
*/
|
|
5009
|
-
function isValidEmail(email) {
|
|
5010
|
-
if (typeof email !== 'string') {
|
|
5011
|
-
return false;
|
|
5012
|
-
}
|
|
5013
|
-
if (email.split('\n').length > 1) {
|
|
5014
|
-
return false;
|
|
5015
|
-
}
|
|
5016
|
-
return /^.+@.+\..+$/.test(email);
|
|
5017
|
-
}
|
|
5018
|
-
|
|
5019
5897
|
/**
|
|
5020
5898
|
* @private utility of CLI
|
|
5021
5899
|
*/
|
|
@@ -5694,113 +6572,39 @@
|
|
|
5694
6572
|
}));
|
|
5695
6573
|
}
|
|
5696
6574
|
/**
|
|
5697
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
5698
|
-
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5699
|
-
*/
|
|
5700
|
-
|
|
5701
|
-
/**
|
|
5702
|
-
* Initializes `login` command for Promptbook CLI utilities
|
|
5703
|
-
*
|
|
5704
|
-
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
5705
|
-
*
|
|
5706
|
-
* @private internal function of `promptbookCli`
|
|
5707
|
-
*/
|
|
5708
|
-
function $initializeLoginCommand(program) {
|
|
5709
|
-
const loginCommand = program.command('login');
|
|
5710
|
-
loginCommand.description(spaceTrim__default["default"](`
|
|
5711
|
-
Login to the remote Promptbook server
|
|
5712
|
-
`));
|
|
5713
|
-
loginCommand.action(handleActionErrors(async (cliOptions) => {
|
|
5714
|
-
// Note: Not interested in return value of this function but the side effect of logging in
|
|
5715
|
-
await $provideLlmToolsForCli({
|
|
5716
|
-
isLoginloaded: true,
|
|
5717
|
-
cliOptions: {
|
|
5718
|
-
...cliOptions,
|
|
5719
|
-
strategy: 'REMOTE_SERVER', // <- Note: Overriding strategy to `REMOTE_SERVER`
|
|
5720
|
-
// TODO: Do not allow flag `--strategy` in `login` command at all
|
|
5721
|
-
},
|
|
5722
|
-
});
|
|
5723
|
-
return process.exit(0);
|
|
5724
|
-
}));
|
|
5725
|
-
}
|
|
5726
|
-
/**
|
|
5727
|
-
* TODO: Implement non-interactive login
|
|
5728
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
5729
|
-
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5730
|
-
*/
|
|
5731
|
-
|
|
5732
|
-
/**
|
|
5733
|
-
* Tests if given string is valid semantic version
|
|
5734
|
-
*
|
|
5735
|
-
* Note: There are two similar functions:
|
|
5736
|
-
* - `isValidSemanticVersion` which tests any semantic version
|
|
5737
|
-
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
5738
|
-
*
|
|
5739
|
-
* @public exported from `@promptbook/utils`
|
|
5740
|
-
*/
|
|
5741
|
-
function isValidSemanticVersion(version) {
|
|
5742
|
-
if (typeof version !== 'string') {
|
|
5743
|
-
return false;
|
|
5744
|
-
}
|
|
5745
|
-
if (version.startsWith('0.0.0')) {
|
|
5746
|
-
return false;
|
|
5747
|
-
}
|
|
5748
|
-
return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
|
|
5749
|
-
}
|
|
5750
|
-
|
|
5751
|
-
/**
|
|
5752
|
-
* Tests if given string is valid promptbook version
|
|
5753
|
-
* It looks into list of known promptbook versions.
|
|
5754
|
-
*
|
|
5755
|
-
* @see https://www.npmjs.com/package/promptbook?activeTab=versions
|
|
5756
|
-
* Note: When you are using for example promptbook 2.0.0 and there already is promptbook 3.0.0 it don`t know about it.
|
|
5757
|
-
* Note: There are two similar functions:
|
|
5758
|
-
* - `isValidSemanticVersion` which tests any semantic version
|
|
5759
|
-
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
5760
|
-
*
|
|
5761
|
-
* @public exported from `@promptbook/utils`
|
|
6575
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6576
|
+
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5762
6577
|
*/
|
|
5763
|
-
function isValidPromptbookVersion(version) {
|
|
5764
|
-
if (!isValidSemanticVersion(version)) {
|
|
5765
|
-
return false;
|
|
5766
|
-
}
|
|
5767
|
-
if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
|
|
5768
|
-
return false;
|
|
5769
|
-
}
|
|
5770
|
-
// <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
|
|
5771
|
-
return true;
|
|
5772
|
-
}
|
|
5773
6578
|
|
|
5774
6579
|
/**
|
|
5775
|
-
*
|
|
6580
|
+
* Initializes `login` command for Promptbook CLI utilities
|
|
5776
6581
|
*
|
|
5777
|
-
* Note:
|
|
5778
|
-
* - `isValidUrl` which tests any URL
|
|
5779
|
-
* - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
|
|
6582
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
5780
6583
|
*
|
|
5781
|
-
* @
|
|
6584
|
+
* @private internal function of `promptbookCli`
|
|
5782
6585
|
*/
|
|
5783
|
-
function
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
return true;
|
|
6586
|
+
function $initializeLoginCommand(program) {
|
|
6587
|
+
const loginCommand = program.command('login');
|
|
6588
|
+
loginCommand.description(spaceTrim__default["default"](`
|
|
6589
|
+
Login to the remote Promptbook server
|
|
6590
|
+
`));
|
|
6591
|
+
loginCommand.action(handleActionErrors(async (cliOptions) => {
|
|
6592
|
+
// Note: Not interested in return value of this function but the side effect of logging in
|
|
6593
|
+
await $provideLlmToolsForCli({
|
|
6594
|
+
isLoginloaded: true,
|
|
6595
|
+
cliOptions: {
|
|
6596
|
+
...cliOptions,
|
|
6597
|
+
strategy: 'REMOTE_SERVER', // <- Note: Overriding strategy to `REMOTE_SERVER`
|
|
6598
|
+
// TODO: Do not allow flag `--strategy` in `login` command at all
|
|
6599
|
+
},
|
|
6600
|
+
});
|
|
6601
|
+
return process.exit(0);
|
|
6602
|
+
}));
|
|
5801
6603
|
}
|
|
5802
6604
|
/**
|
|
5803
|
-
* TODO:
|
|
6605
|
+
* TODO: Implement non-interactive login
|
|
6606
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6607
|
+
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5804
6608
|
*/
|
|
5805
6609
|
|
|
5806
6610
|
/**
|
|
@@ -6503,65 +7307,6 @@
|
|
|
6503
7307
|
* - [♨] Are tasks prepared
|
|
6504
7308
|
*/
|
|
6505
7309
|
|
|
6506
|
-
/**
|
|
6507
|
-
* Serializes an error into a [🚉] JSON-serializable object
|
|
6508
|
-
*
|
|
6509
|
-
* @public exported from `@promptbook/utils`
|
|
6510
|
-
*/
|
|
6511
|
-
function serializeError(error) {
|
|
6512
|
-
const { name, message, stack } = error;
|
|
6513
|
-
const { id } = error;
|
|
6514
|
-
if (!Object.keys(ALL_ERRORS).includes(name)) {
|
|
6515
|
-
console.error(spaceTrim__default["default"]((block) => `
|
|
6516
|
-
|
|
6517
|
-
Cannot serialize error with name "${name}"
|
|
6518
|
-
|
|
6519
|
-
Authors of Promptbook probably forgot to add this error into the list of errors:
|
|
6520
|
-
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
${block(stack || message)}
|
|
6524
|
-
|
|
6525
|
-
`));
|
|
6526
|
-
}
|
|
6527
|
-
return {
|
|
6528
|
-
name: name,
|
|
6529
|
-
message,
|
|
6530
|
-
stack,
|
|
6531
|
-
id, // Include id in the serialized object
|
|
6532
|
-
};
|
|
6533
|
-
}
|
|
6534
|
-
|
|
6535
|
-
/**
|
|
6536
|
-
* Recursively converts JSON strings to JSON objects
|
|
6537
|
-
|
|
6538
|
-
* @public exported from `@promptbook/utils`
|
|
6539
|
-
*/
|
|
6540
|
-
function jsonStringsToJsons(object) {
|
|
6541
|
-
if (object === null) {
|
|
6542
|
-
return object;
|
|
6543
|
-
}
|
|
6544
|
-
if (Array.isArray(object)) {
|
|
6545
|
-
return object.map(jsonStringsToJsons);
|
|
6546
|
-
}
|
|
6547
|
-
if (typeof object !== 'object') {
|
|
6548
|
-
return object;
|
|
6549
|
-
}
|
|
6550
|
-
const newObject = { ...object };
|
|
6551
|
-
for (const [key, value] of Object.entries(object)) {
|
|
6552
|
-
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
6553
|
-
newObject[key] = jsonParse(value);
|
|
6554
|
-
}
|
|
6555
|
-
else {
|
|
6556
|
-
newObject[key] = jsonStringsToJsons(value);
|
|
6557
|
-
}
|
|
6558
|
-
}
|
|
6559
|
-
return newObject;
|
|
6560
|
-
}
|
|
6561
|
-
/**
|
|
6562
|
-
* TODO: Type the return type correctly
|
|
6563
|
-
*/
|
|
6564
|
-
|
|
6565
7310
|
/**
|
|
6566
7311
|
* Asserts that the execution of a Promptbook is successful
|
|
6567
7312
|
*
|
|
@@ -6739,144 +7484,63 @@
|
|
|
6739
7484
|
message = `Working on ${current.title}`;
|
|
6740
7485
|
}
|
|
6741
7486
|
}
|
|
6742
|
-
if (!message) {
|
|
6743
|
-
if (errors.length) {
|
|
6744
|
-
message = errors[errors.length - 1].message || 'Error';
|
|
6745
|
-
}
|
|
6746
|
-
else if (warnings.length) {
|
|
6747
|
-
message = warnings[warnings.length - 1].message || 'Warning';
|
|
6748
|
-
}
|
|
6749
|
-
else if (status === 'FINISHED') {
|
|
6750
|
-
message = 'Finished';
|
|
6751
|
-
}
|
|
6752
|
-
else if (status === 'ERROR') {
|
|
6753
|
-
message = 'Error';
|
|
6754
|
-
}
|
|
6755
|
-
else {
|
|
6756
|
-
message = 'Running';
|
|
6757
|
-
}
|
|
6758
|
-
}
|
|
6759
|
-
}
|
|
6760
|
-
return {
|
|
6761
|
-
percent: percent,
|
|
6762
|
-
message: message + ' (!!!fallback)',
|
|
6763
|
-
};
|
|
6764
|
-
},
|
|
6765
|
-
get createdAt() {
|
|
6766
|
-
return createdAt;
|
|
6767
|
-
// <- Note: [1] --||--
|
|
6768
|
-
},
|
|
6769
|
-
get updatedAt() {
|
|
6770
|
-
return updatedAt;
|
|
6771
|
-
// <- Note: [1] --||--
|
|
6772
|
-
},
|
|
6773
|
-
asPromise,
|
|
6774
|
-
asObservable() {
|
|
6775
|
-
return partialResultSubject.asObservable();
|
|
6776
|
-
},
|
|
6777
|
-
get errors() {
|
|
6778
|
-
return errors;
|
|
6779
|
-
// <- Note: [1] --||--
|
|
6780
|
-
},
|
|
6781
|
-
get warnings() {
|
|
6782
|
-
return warnings;
|
|
6783
|
-
// <- Note: [1] --||--
|
|
6784
|
-
},
|
|
6785
|
-
get llmCalls() {
|
|
6786
|
-
return [...llmCalls, { foo: '!!! bar' }];
|
|
6787
|
-
// <- Note: [1] --||--
|
|
6788
|
-
},
|
|
6789
|
-
get currentValue() {
|
|
6790
|
-
return currentValue;
|
|
6791
|
-
// <- Note: [1] --||--
|
|
6792
|
-
},
|
|
6793
|
-
};
|
|
6794
|
-
}
|
|
6795
|
-
/**
|
|
6796
|
-
* TODO: Maybe allow to terminate the task and add getter `isFinished` or `status`
|
|
6797
|
-
* TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
|
|
6798
|
-
*/
|
|
6799
|
-
|
|
6800
|
-
/**
|
|
6801
|
-
* Format either small or big number
|
|
6802
|
-
*
|
|
6803
|
-
* @public exported from `@promptbook/utils`
|
|
6804
|
-
*/
|
|
6805
|
-
function numberToString(value) {
|
|
6806
|
-
if (value === 0) {
|
|
6807
|
-
return '0';
|
|
6808
|
-
}
|
|
6809
|
-
else if (Number.isNaN(value)) {
|
|
6810
|
-
return VALUE_STRINGS.nan;
|
|
6811
|
-
}
|
|
6812
|
-
else if (value === Infinity) {
|
|
6813
|
-
return VALUE_STRINGS.infinity;
|
|
6814
|
-
}
|
|
6815
|
-
else if (value === -Infinity) {
|
|
6816
|
-
return VALUE_STRINGS.negativeInfinity;
|
|
6817
|
-
}
|
|
6818
|
-
for (let exponent = 0; exponent < 15; exponent++) {
|
|
6819
|
-
const factor = 10 ** exponent;
|
|
6820
|
-
const valueRounded = Math.round(value * factor) / factor;
|
|
6821
|
-
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
6822
|
-
return valueRounded.toFixed(exponent);
|
|
6823
|
-
}
|
|
6824
|
-
}
|
|
6825
|
-
return value.toString();
|
|
6826
|
-
}
|
|
6827
|
-
|
|
6828
|
-
/**
|
|
6829
|
-
* Function `valueToString` will convert the given value to string
|
|
6830
|
-
* This is useful and used in the `templateParameters` function
|
|
6831
|
-
*
|
|
6832
|
-
* Note: This function is not just calling `toString` method
|
|
6833
|
-
* It's more complex and can handle this conversion specifically for LLM models
|
|
6834
|
-
* See `VALUE_STRINGS`
|
|
6835
|
-
*
|
|
6836
|
-
* Note: There are 2 similar functions
|
|
6837
|
-
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
6838
|
-
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
6839
|
-
*
|
|
6840
|
-
* @public exported from `@promptbook/utils`
|
|
6841
|
-
*/
|
|
6842
|
-
function valueToString(value) {
|
|
6843
|
-
try {
|
|
6844
|
-
if (value === '') {
|
|
6845
|
-
return VALUE_STRINGS.empty;
|
|
6846
|
-
}
|
|
6847
|
-
else if (value === null) {
|
|
6848
|
-
return VALUE_STRINGS.null;
|
|
6849
|
-
}
|
|
6850
|
-
else if (value === undefined) {
|
|
6851
|
-
return VALUE_STRINGS.undefined;
|
|
6852
|
-
}
|
|
6853
|
-
else if (typeof value === 'string') {
|
|
6854
|
-
return value;
|
|
6855
|
-
}
|
|
6856
|
-
else if (typeof value === 'number') {
|
|
6857
|
-
return numberToString(value);
|
|
6858
|
-
}
|
|
6859
|
-
else if (value instanceof Date) {
|
|
6860
|
-
return value.toISOString();
|
|
6861
|
-
}
|
|
6862
|
-
else {
|
|
6863
|
-
try {
|
|
6864
|
-
return JSON.stringify(value);
|
|
6865
|
-
}
|
|
6866
|
-
catch (error) {
|
|
6867
|
-
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
6868
|
-
return VALUE_STRINGS.circular;
|
|
7487
|
+
if (!message) {
|
|
7488
|
+
if (errors.length) {
|
|
7489
|
+
message = errors[errors.length - 1].message || 'Error';
|
|
7490
|
+
}
|
|
7491
|
+
else if (warnings.length) {
|
|
7492
|
+
message = warnings[warnings.length - 1].message || 'Warning';
|
|
7493
|
+
}
|
|
7494
|
+
else if (status === 'FINISHED') {
|
|
7495
|
+
message = 'Finished';
|
|
7496
|
+
}
|
|
7497
|
+
else if (status === 'ERROR') {
|
|
7498
|
+
message = 'Error';
|
|
7499
|
+
}
|
|
7500
|
+
else {
|
|
7501
|
+
message = 'Running';
|
|
7502
|
+
}
|
|
6869
7503
|
}
|
|
6870
|
-
throw error;
|
|
6871
7504
|
}
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
7505
|
+
return {
|
|
7506
|
+
percent: percent,
|
|
7507
|
+
message: message + ' (!!!fallback)',
|
|
7508
|
+
};
|
|
7509
|
+
},
|
|
7510
|
+
get createdAt() {
|
|
7511
|
+
return createdAt;
|
|
7512
|
+
// <- Note: [1] --||--
|
|
7513
|
+
},
|
|
7514
|
+
get updatedAt() {
|
|
7515
|
+
return updatedAt;
|
|
7516
|
+
// <- Note: [1] --||--
|
|
7517
|
+
},
|
|
7518
|
+
asPromise,
|
|
7519
|
+
asObservable() {
|
|
7520
|
+
return partialResultSubject.asObservable();
|
|
7521
|
+
},
|
|
7522
|
+
get errors() {
|
|
7523
|
+
return errors;
|
|
7524
|
+
// <- Note: [1] --||--
|
|
7525
|
+
},
|
|
7526
|
+
get warnings() {
|
|
7527
|
+
return warnings;
|
|
7528
|
+
// <- Note: [1] --||--
|
|
7529
|
+
},
|
|
7530
|
+
get llmCalls() {
|
|
7531
|
+
return [...llmCalls, { foo: '!!! bar' }];
|
|
7532
|
+
// <- Note: [1] --||--
|
|
7533
|
+
},
|
|
7534
|
+
get currentValue() {
|
|
7535
|
+
return currentValue;
|
|
7536
|
+
// <- Note: [1] --||--
|
|
7537
|
+
},
|
|
7538
|
+
};
|
|
6879
7539
|
}
|
|
7540
|
+
/**
|
|
7541
|
+
* TODO: Maybe allow to terminate the task and add getter `isFinished` or `status`
|
|
7542
|
+
* TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
|
|
7543
|
+
*/
|
|
6880
7544
|
|
|
6881
7545
|
/**
|
|
6882
7546
|
* Parses the given script and returns the list of all used variables that are not defined in the script
|
|
@@ -7003,41 +7667,6 @@
|
|
|
7003
7667
|
* TODO: [🔣] If script require contentLanguage
|
|
7004
7668
|
*/
|
|
7005
7669
|
|
|
7006
|
-
/**
|
|
7007
|
-
* Create difference set of two sets.
|
|
7008
|
-
*
|
|
7009
|
-
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
7010
|
-
* @public exported from `@promptbook/utils`
|
|
7011
|
-
*/
|
|
7012
|
-
function difference(a, b, isEqual = (a, b) => a === b) {
|
|
7013
|
-
const diff = new Set();
|
|
7014
|
-
for (const itemA of Array.from(a)) {
|
|
7015
|
-
if (!Array.from(b).some((itemB) => isEqual(itemA, itemB))) {
|
|
7016
|
-
diff.add(itemA);
|
|
7017
|
-
}
|
|
7018
|
-
}
|
|
7019
|
-
return diff;
|
|
7020
|
-
}
|
|
7021
|
-
/**
|
|
7022
|
-
* TODO: [🧠][💯] Maybe also implement symmetricDifference
|
|
7023
|
-
*/
|
|
7024
|
-
|
|
7025
|
-
/**
|
|
7026
|
-
* Creates a new set with all elements that are present in either set
|
|
7027
|
-
*
|
|
7028
|
-
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
7029
|
-
* @public exported from `@promptbook/utils`
|
|
7030
|
-
*/
|
|
7031
|
-
function union(...sets) {
|
|
7032
|
-
const union = new Set();
|
|
7033
|
-
for (const set of sets) {
|
|
7034
|
-
for (const item of Array.from(set)) {
|
|
7035
|
-
union.add(item);
|
|
7036
|
-
}
|
|
7037
|
-
}
|
|
7038
|
-
return union;
|
|
7039
|
-
}
|
|
7040
|
-
|
|
7041
7670
|
/**
|
|
7042
7671
|
* Contains configuration options for parsing and generating CSV files, such as delimiters and quoting rules.
|
|
7043
7672
|
*
|
|
@@ -7066,28 +7695,6 @@
|
|
|
7066
7695
|
return csv;
|
|
7067
7696
|
}
|
|
7068
7697
|
|
|
7069
|
-
/**
|
|
7070
|
-
* Function to check if a string is valid CSV
|
|
7071
|
-
*
|
|
7072
|
-
* @param value The string to check
|
|
7073
|
-
* @returns `true` if the string is a valid CSV string, false otherwise
|
|
7074
|
-
*
|
|
7075
|
-
* @public exported from `@promptbook/utils`
|
|
7076
|
-
*/
|
|
7077
|
-
function isValidCsvString(value) {
|
|
7078
|
-
try {
|
|
7079
|
-
// A simple check for CSV format: at least one comma and no invalid characters
|
|
7080
|
-
if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
|
|
7081
|
-
return true;
|
|
7082
|
-
}
|
|
7083
|
-
return false;
|
|
7084
|
-
}
|
|
7085
|
-
catch (error) {
|
|
7086
|
-
assertsError(error);
|
|
7087
|
-
return false;
|
|
7088
|
-
}
|
|
7089
|
-
}
|
|
7090
|
-
|
|
7091
7698
|
/**
|
|
7092
7699
|
* Definition for CSV spreadsheet
|
|
7093
7700
|
*
|
|
@@ -7267,30 +7874,6 @@
|
|
|
7267
7874
|
* TODO: [🏢] Allow to expect something inside each item of list and other formats
|
|
7268
7875
|
*/
|
|
7269
7876
|
|
|
7270
|
-
/**
|
|
7271
|
-
* Function to check if a string is valid XML
|
|
7272
|
-
*
|
|
7273
|
-
* @param value
|
|
7274
|
-
* @returns `true` if the string is a valid XML string, false otherwise
|
|
7275
|
-
*
|
|
7276
|
-
* @public exported from `@promptbook/utils`
|
|
7277
|
-
*/
|
|
7278
|
-
function isValidXmlString(value) {
|
|
7279
|
-
try {
|
|
7280
|
-
const parser = new DOMParser();
|
|
7281
|
-
const parsedDocument = parser.parseFromString(value, 'application/xml');
|
|
7282
|
-
const parserError = parsedDocument.getElementsByTagName('parsererror');
|
|
7283
|
-
if (parserError.length > 0) {
|
|
7284
|
-
return false;
|
|
7285
|
-
}
|
|
7286
|
-
return true;
|
|
7287
|
-
}
|
|
7288
|
-
catch (error) {
|
|
7289
|
-
assertsError(error);
|
|
7290
|
-
return false;
|
|
7291
|
-
}
|
|
7292
|
-
}
|
|
7293
|
-
|
|
7294
7877
|
/**
|
|
7295
7878
|
* Definition for XML format
|
|
7296
7879
|
*
|
|
@@ -7380,130 +7963,59 @@
|
|
|
7380
7963
|
${block(Array.from(expectedParameterNames)
|
|
7381
7964
|
.map((parameterName) => `- {${parameterName}}`)
|
|
7382
7965
|
.join('\n'))}
|
|
7383
|
-
|
|
7384
|
-
Remaining available parameters:
|
|
7385
|
-
${block(Array.from(availableParametersNames)
|
|
7386
|
-
.map((parameterName) => `- {${parameterName}}`)
|
|
7387
|
-
.join('\n'))}
|
|
7388
|
-
|
|
7389
|
-
`));
|
|
7390
|
-
}
|
|
7391
|
-
const expectedParameterNamesArray = Array.from(expectedParameterNames);
|
|
7392
|
-
const availableParametersNamesArray = Array.from(availableParametersNames);
|
|
7393
|
-
for (let i = 0; i < expectedParameterNames.size; i++) {
|
|
7394
|
-
mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
|
|
7395
|
-
}
|
|
7396
|
-
// Note: [👨👨👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
|
|
7397
|
-
Object.freeze(mappedParameters);
|
|
7398
|
-
return mappedParameters;
|
|
7399
|
-
}
|
|
7400
|
-
|
|
7401
|
-
/**
|
|
7402
|
-
* Takes an item or an array of items and returns an array of items
|
|
7403
|
-
*
|
|
7404
|
-
* 1) Any item except array and undefined returns array with that one item (also null)
|
|
7405
|
-
* 2) Undefined returns empty array
|
|
7406
|
-
* 3) Array returns itself
|
|
7407
|
-
*
|
|
7408
|
-
* @private internal utility
|
|
7409
|
-
*/
|
|
7410
|
-
function arrayableToArray(input) {
|
|
7411
|
-
if (input === undefined) {
|
|
7412
|
-
return [];
|
|
7413
|
-
}
|
|
7414
|
-
if (input instanceof Array) {
|
|
7415
|
-
return input;
|
|
7416
|
-
}
|
|
7417
|
-
return [input];
|
|
7418
|
-
}
|
|
7419
|
-
|
|
7420
|
-
/**
|
|
7421
|
-
* Just returns the given `LlmExecutionTools` or joins multiple into one
|
|
7422
|
-
*
|
|
7423
|
-
* @public exported from `@promptbook/core`
|
|
7424
|
-
*/
|
|
7425
|
-
function getSingleLlmExecutionTools(oneOrMoreLlmExecutionTools) {
|
|
7426
|
-
const _llms = arrayableToArray(oneOrMoreLlmExecutionTools);
|
|
7427
|
-
const llmTools = _llms.length === 1
|
|
7428
|
-
? _llms[0]
|
|
7429
|
-
: joinLlmExecutionTools('Multiple LLM Providers joined by `getSingleLlmExecutionTools`', ..._llms);
|
|
7430
|
-
return llmTools;
|
|
7431
|
-
}
|
|
7432
|
-
/**
|
|
7433
|
-
* TODO: [🙆] `getSingleLlmExecutionTools` vs `joinLlmExecutionTools` - explain difference or pick one
|
|
7434
|
-
* TODO: [👷♂️] @@@ Manual about construction of llmTools
|
|
7435
|
-
*/
|
|
7436
|
-
|
|
7437
|
-
/**
|
|
7438
|
-
* Replaces parameters in template with values from parameters object
|
|
7439
|
-
*
|
|
7440
|
-
* Note: This function is not places strings into string,
|
|
7441
|
-
* It's more complex and can handle this operation specifically for LLM models
|
|
7442
|
-
*
|
|
7443
|
-
* @param template the template with parameters in {curly} braces
|
|
7444
|
-
* @param parameters the object with parameters
|
|
7445
|
-
* @returns the template with replaced parameters
|
|
7446
|
-
* @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
|
|
7447
|
-
* @public exported from `@promptbook/utils`
|
|
7448
|
-
*/
|
|
7449
|
-
function templateParameters(template, parameters) {
|
|
7450
|
-
for (const [parameterName, parameterValue] of Object.entries(parameters)) {
|
|
7451
|
-
if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
|
|
7452
|
-
throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
|
|
7453
|
-
}
|
|
7454
|
-
else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
|
|
7455
|
-
// TODO: [🍵]
|
|
7456
|
-
throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
|
|
7457
|
-
}
|
|
7458
|
-
}
|
|
7459
|
-
let replacedTemplates = template;
|
|
7460
|
-
let match;
|
|
7461
|
-
let loopLimit = LOOP_LIMIT;
|
|
7462
|
-
while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
|
|
7463
|
-
.exec(replacedTemplates))) {
|
|
7464
|
-
if (loopLimit-- < 0) {
|
|
7465
|
-
throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
|
|
7466
|
-
}
|
|
7467
|
-
const precol = match.groups.precol;
|
|
7468
|
-
const parameterName = match.groups.parameterName;
|
|
7469
|
-
if (parameterName === '') {
|
|
7470
|
-
// Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
|
|
7471
|
-
continue;
|
|
7472
|
-
}
|
|
7473
|
-
if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
|
|
7474
|
-
throw new PipelineExecutionError('Parameter is already opened or not closed');
|
|
7475
|
-
}
|
|
7476
|
-
if (parameters[parameterName] === undefined) {
|
|
7477
|
-
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
7478
|
-
}
|
|
7479
|
-
let parameterValue = parameters[parameterName];
|
|
7480
|
-
if (parameterValue === undefined) {
|
|
7481
|
-
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
7482
|
-
}
|
|
7483
|
-
parameterValue = valueToString(parameterValue);
|
|
7484
|
-
// Escape curly braces in parameter values to prevent prompt-injection
|
|
7485
|
-
parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
|
|
7486
|
-
if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
|
|
7487
|
-
parameterValue = parameterValue
|
|
7488
|
-
.split('\n')
|
|
7489
|
-
.map((line, index) => (index === 0 ? line : `${precol}${line}`))
|
|
7490
|
-
.join('\n');
|
|
7491
|
-
}
|
|
7492
|
-
replacedTemplates =
|
|
7493
|
-
replacedTemplates.substring(0, match.index + precol.length) +
|
|
7494
|
-
parameterValue +
|
|
7495
|
-
replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
|
|
7966
|
+
|
|
7967
|
+
Remaining available parameters:
|
|
7968
|
+
${block(Array.from(availableParametersNames)
|
|
7969
|
+
.map((parameterName) => `- {${parameterName}}`)
|
|
7970
|
+
.join('\n'))}
|
|
7971
|
+
|
|
7972
|
+
`));
|
|
7496
7973
|
}
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7974
|
+
const expectedParameterNamesArray = Array.from(expectedParameterNames);
|
|
7975
|
+
const availableParametersNamesArray = Array.from(availableParametersNames);
|
|
7976
|
+
for (let i = 0; i < expectedParameterNames.size; i++) {
|
|
7977
|
+
mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
|
|
7500
7978
|
}
|
|
7501
|
-
// [
|
|
7502
|
-
|
|
7503
|
-
|
|
7979
|
+
// Note: [👨👨👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
|
|
7980
|
+
Object.freeze(mappedParameters);
|
|
7981
|
+
return mappedParameters;
|
|
7982
|
+
}
|
|
7983
|
+
|
|
7984
|
+
/**
|
|
7985
|
+
* Takes an item or an array of items and returns an array of items
|
|
7986
|
+
*
|
|
7987
|
+
* 1) Any item except array and undefined returns array with that one item (also null)
|
|
7988
|
+
* 2) Undefined returns empty array
|
|
7989
|
+
* 3) Array returns itself
|
|
7990
|
+
*
|
|
7991
|
+
* @private internal utility
|
|
7992
|
+
*/
|
|
7993
|
+
function arrayableToArray(input) {
|
|
7994
|
+
if (input === undefined) {
|
|
7995
|
+
return [];
|
|
7504
7996
|
}
|
|
7505
|
-
|
|
7997
|
+
if (input instanceof Array) {
|
|
7998
|
+
return input;
|
|
7999
|
+
}
|
|
8000
|
+
return [input];
|
|
8001
|
+
}
|
|
8002
|
+
|
|
8003
|
+
/**
|
|
8004
|
+
* Just returns the given `LlmExecutionTools` or joins multiple into one
|
|
8005
|
+
*
|
|
8006
|
+
* @public exported from `@promptbook/core`
|
|
8007
|
+
*/
|
|
8008
|
+
function getSingleLlmExecutionTools(oneOrMoreLlmExecutionTools) {
|
|
8009
|
+
const _llms = arrayableToArray(oneOrMoreLlmExecutionTools);
|
|
8010
|
+
const llmTools = _llms.length === 1
|
|
8011
|
+
? _llms[0]
|
|
8012
|
+
: joinLlmExecutionTools('Multiple LLM Providers joined by `getSingleLlmExecutionTools`', ..._llms);
|
|
8013
|
+
return llmTools;
|
|
7506
8014
|
}
|
|
8015
|
+
/**
|
|
8016
|
+
* TODO: [🙆] `getSingleLlmExecutionTools` vs `joinLlmExecutionTools` - explain difference or pick one
|
|
8017
|
+
* TODO: [👷♂️] @@@ Manual about construction of llmTools
|
|
8018
|
+
*/
|
|
7507
8019
|
|
|
7508
8020
|
/**
|
|
7509
8021
|
* Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
|
|
@@ -7599,8 +8111,9 @@
|
|
|
7599
8111
|
$ongoingTaskResult.$resultString = $ongoingTaskResult.$completionResult.content;
|
|
7600
8112
|
break variant;
|
|
7601
8113
|
case 'EMBEDDING':
|
|
8114
|
+
case 'IMAGE_GENERATION':
|
|
7602
8115
|
throw new PipelineExecutionError(spaceTrim$1.spaceTrim((block) => `
|
|
7603
|
-
|
|
8116
|
+
${modelRequirements.modelVariant} model can not be used in pipeline
|
|
7604
8117
|
|
|
7605
8118
|
This should be catched during parsing
|
|
7606
8119
|
|
|
@@ -8734,35 +9247,6 @@
|
|
|
8734
9247
|
return pipelineExecutor;
|
|
8735
9248
|
}
|
|
8736
9249
|
|
|
8737
|
-
/**
|
|
8738
|
-
* Async version of Array.forEach
|
|
8739
|
-
*
|
|
8740
|
-
* @param array - Array to iterate over
|
|
8741
|
-
* @param options - Options for the function
|
|
8742
|
-
* @param callbackfunction - Function to call for each item
|
|
8743
|
-
* @public exported from `@promptbook/utils`
|
|
8744
|
-
* @deprecated [🪂] Use queues instead
|
|
8745
|
-
*/
|
|
8746
|
-
async function forEachAsync(array, options, callbackfunction) {
|
|
8747
|
-
const { maxParallelCount = Infinity } = options;
|
|
8748
|
-
let index = 0;
|
|
8749
|
-
let runningTasks = [];
|
|
8750
|
-
const tasks = [];
|
|
8751
|
-
for (const item of array) {
|
|
8752
|
-
const currentIndex = index++;
|
|
8753
|
-
const task = callbackfunction(item, currentIndex, array);
|
|
8754
|
-
tasks.push(task);
|
|
8755
|
-
runningTasks.push(task);
|
|
8756
|
-
/* not await */ Promise.resolve(task).then(() => {
|
|
8757
|
-
runningTasks = runningTasks.filter((t) => t !== task);
|
|
8758
|
-
});
|
|
8759
|
-
if (maxParallelCount < runningTasks.length) {
|
|
8760
|
-
await Promise.race(runningTasks);
|
|
8761
|
-
}
|
|
8762
|
-
}
|
|
8763
|
-
await Promise.all(tasks);
|
|
8764
|
-
}
|
|
8765
|
-
|
|
8766
9250
|
/**
|
|
8767
9251
|
* Prepares the persona for the pipeline
|
|
8768
9252
|
*
|
|
@@ -9898,75 +10382,6 @@
|
|
|
9898
10382
|
* TODO: [💝] Unite object for expecting amount and format - remove format
|
|
9899
10383
|
*/
|
|
9900
10384
|
|
|
9901
|
-
/**
|
|
9902
|
-
* Function parseNumber will parse number from string
|
|
9903
|
-
*
|
|
9904
|
-
* Note: [🔂] This function is idempotent.
|
|
9905
|
-
* Unlike Number.parseInt, Number.parseFloat it will never ever result in NaN
|
|
9906
|
-
* Note: it also works only with decimal numbers
|
|
9907
|
-
*
|
|
9908
|
-
* @returns parsed number
|
|
9909
|
-
* @throws {ParseError} if the value is not a number
|
|
9910
|
-
*
|
|
9911
|
-
* @public exported from `@promptbook/utils`
|
|
9912
|
-
*/
|
|
9913
|
-
function parseNumber(value) {
|
|
9914
|
-
const originalValue = value;
|
|
9915
|
-
if (typeof value === 'number') {
|
|
9916
|
-
value = value.toString(); // <- TODO: Maybe more efficient way to do this
|
|
9917
|
-
}
|
|
9918
|
-
if (typeof value !== 'string') {
|
|
9919
|
-
return 0;
|
|
9920
|
-
}
|
|
9921
|
-
value = value.trim();
|
|
9922
|
-
if (value.startsWith('+')) {
|
|
9923
|
-
return parseNumber(value.substring(1));
|
|
9924
|
-
}
|
|
9925
|
-
if (value.startsWith('-')) {
|
|
9926
|
-
const number = parseNumber(value.substring(1));
|
|
9927
|
-
if (number === 0) {
|
|
9928
|
-
return 0; // <- Note: To prevent -0
|
|
9929
|
-
}
|
|
9930
|
-
return -number;
|
|
9931
|
-
}
|
|
9932
|
-
value = value.replace(/,/g, '.');
|
|
9933
|
-
value = value.toUpperCase();
|
|
9934
|
-
if (value === '') {
|
|
9935
|
-
return 0;
|
|
9936
|
-
}
|
|
9937
|
-
if (value === '♾' || value.startsWith('INF')) {
|
|
9938
|
-
return Infinity;
|
|
9939
|
-
}
|
|
9940
|
-
if (value.includes('/')) {
|
|
9941
|
-
const [numerator_, denominator_] = value.split('/');
|
|
9942
|
-
const numerator = parseNumber(numerator_);
|
|
9943
|
-
const denominator = parseNumber(denominator_);
|
|
9944
|
-
if (denominator === 0) {
|
|
9945
|
-
throw new ParseError(`Unable to parse number from "${originalValue}" because denominator is zero`);
|
|
9946
|
-
}
|
|
9947
|
-
return numerator / denominator;
|
|
9948
|
-
}
|
|
9949
|
-
if (/^(NAN|NULL|NONE|UNDEFINED|ZERO|NO.*)$/.test(value)) {
|
|
9950
|
-
return 0;
|
|
9951
|
-
}
|
|
9952
|
-
if (value.includes('E')) {
|
|
9953
|
-
const [significand, exponent] = value.split('E');
|
|
9954
|
-
return parseNumber(significand) * 10 ** parseNumber(exponent);
|
|
9955
|
-
}
|
|
9956
|
-
if (!/^[0-9.]+$/.test(value) || value.split('.').length > 2) {
|
|
9957
|
-
throw new ParseError(`Unable to parse number from "${originalValue}"`);
|
|
9958
|
-
}
|
|
9959
|
-
const num = parseFloat(value);
|
|
9960
|
-
if (isNaN(num)) {
|
|
9961
|
-
throw new ParseError(`Unexpected NaN when parsing number from "${originalValue}"`);
|
|
9962
|
-
}
|
|
9963
|
-
return num;
|
|
9964
|
-
}
|
|
9965
|
-
/**
|
|
9966
|
-
* TODO: Maybe use sth. like safe-eval in fraction/calculation case @see https://www.npmjs.com/package/safe-eval
|
|
9967
|
-
* TODO: [🧠][🌻] Maybe export through `@promptbook/markdown-utils` not `@promptbook/utils`
|
|
9968
|
-
*/
|
|
9969
|
-
|
|
9970
10385
|
/**
|
|
9971
10386
|
import { WrappedError } from '../../errors/WrappedError';
|
|
9972
10387
|
import { assertsError } from '../../errors/assertsError';
|
|
@@ -10109,84 +10524,6 @@
|
|
|
10109
10524
|
},
|
|
10110
10525
|
};
|
|
10111
10526
|
|
|
10112
|
-
/**
|
|
10113
|
-
* Normalizes a given text to camelCase format.
|
|
10114
|
-
*
|
|
10115
|
-
* Note: [🔂] This function is idempotent.
|
|
10116
|
-
*
|
|
10117
|
-
* @param text The text to be normalized.
|
|
10118
|
-
* @param _isFirstLetterCapital Whether the first letter should be capitalized.
|
|
10119
|
-
* @returns The camelCase formatted string.
|
|
10120
|
-
* @example 'helloWorld'
|
|
10121
|
-
* @example 'iLovePromptbook'
|
|
10122
|
-
* @public exported from `@promptbook/utils`
|
|
10123
|
-
*/
|
|
10124
|
-
function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
|
|
10125
|
-
let charType;
|
|
10126
|
-
let lastCharType = null;
|
|
10127
|
-
let normalizedName = '';
|
|
10128
|
-
for (const char of text) {
|
|
10129
|
-
let normalizedChar;
|
|
10130
|
-
if (/^[a-z]$/.test(char)) {
|
|
10131
|
-
charType = 'LOWERCASE';
|
|
10132
|
-
normalizedChar = char;
|
|
10133
|
-
}
|
|
10134
|
-
else if (/^[A-Z]$/.test(char)) {
|
|
10135
|
-
charType = 'UPPERCASE';
|
|
10136
|
-
normalizedChar = char.toLowerCase();
|
|
10137
|
-
}
|
|
10138
|
-
else if (/^[0-9]$/.test(char)) {
|
|
10139
|
-
charType = 'NUMBER';
|
|
10140
|
-
normalizedChar = char;
|
|
10141
|
-
}
|
|
10142
|
-
else {
|
|
10143
|
-
charType = 'OTHER';
|
|
10144
|
-
normalizedChar = '';
|
|
10145
|
-
}
|
|
10146
|
-
if (!lastCharType) {
|
|
10147
|
-
if (_isFirstLetterCapital) {
|
|
10148
|
-
normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
|
|
10149
|
-
}
|
|
10150
|
-
}
|
|
10151
|
-
else if (charType !== lastCharType &&
|
|
10152
|
-
!(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
|
|
10153
|
-
!(lastCharType === 'NUMBER') &&
|
|
10154
|
-
!(charType === 'NUMBER')) {
|
|
10155
|
-
normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
|
|
10156
|
-
}
|
|
10157
|
-
normalizedName += normalizedChar;
|
|
10158
|
-
lastCharType = charType;
|
|
10159
|
-
}
|
|
10160
|
-
return normalizedName;
|
|
10161
|
-
}
|
|
10162
|
-
/**
|
|
10163
|
-
* TODO: [🌺] Use some intermediate util splitWords
|
|
10164
|
-
*/
|
|
10165
|
-
|
|
10166
|
-
/**
|
|
10167
|
-
* Removes quotes from a string
|
|
10168
|
-
*
|
|
10169
|
-
* Note: [🔂] This function is idempotent.
|
|
10170
|
-
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
10171
|
-
* Note: This function removes only the same quotes from the beginning and the end of the string
|
|
10172
|
-
* Note: There are two similar functions:
|
|
10173
|
-
* - `removeQuotes` which removes only bounding quotes
|
|
10174
|
-
* - `unwrapResult` which removes whole introduce sentence
|
|
10175
|
-
*
|
|
10176
|
-
* @param text optionally quoted text
|
|
10177
|
-
* @returns text without quotes
|
|
10178
|
-
* @public exported from `@promptbook/utils`
|
|
10179
|
-
*/
|
|
10180
|
-
function removeQuotes(text) {
|
|
10181
|
-
if (text.startsWith('"') && text.endsWith('"')) {
|
|
10182
|
-
return text.slice(1, -1);
|
|
10183
|
-
}
|
|
10184
|
-
if (text.startsWith("'") && text.endsWith("'")) {
|
|
10185
|
-
return text.slice(1, -1);
|
|
10186
|
-
}
|
|
10187
|
-
return text;
|
|
10188
|
-
}
|
|
10189
|
-
|
|
10190
10527
|
/**
|
|
10191
10528
|
* Function `validateParameterName` will normalize and validate a parameter name for use in pipelines.
|
|
10192
10529
|
* It removes diacritics, emojis, and quotes, normalizes to camelCase, and checks for reserved names and invalid characters.
|
|
@@ -11076,11 +11413,7 @@
|
|
|
11076
11413
|
// TODO: [🚜] DRY
|
|
11077
11414
|
if ($taskJson.modelRequirements[command.key] !== undefined) {
|
|
11078
11415
|
if ($taskJson.modelRequirements[command.key] === command.value) {
|
|
11079
|
-
console.warn(`Multiple commands \`MODEL ${{
|
|
11080
|
-
modelName: 'NAME',
|
|
11081
|
-
modelVariant: 'VARIANT',
|
|
11082
|
-
maxTokens: '???',
|
|
11083
|
-
}[command.key]} ${command.value}\` in the task "${$taskJson.title || $taskJson.name}"`);
|
|
11416
|
+
console.warn(`Multiple commands \`MODEL ${command.key} ${command.value}\` in the task "${$taskJson.title || $taskJson.name}"`);
|
|
11084
11417
|
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
11085
11418
|
}
|
|
11086
11419
|
else {
|
|
@@ -11361,30 +11694,16 @@
|
|
|
11361
11694
|
}
|
|
11362
11695
|
console.warn(spaceTrim__default["default"](`
|
|
11363
11696
|
|
|
11364
|
-
Persona "${personaName}" is defined multiple times with different description:
|
|
11365
|
-
|
|
11366
|
-
First definition:
|
|
11367
|
-
${persona.description}
|
|
11368
|
-
|
|
11369
|
-
Second definition:
|
|
11370
|
-
${personaDescription}
|
|
11371
|
-
|
|
11372
|
-
`));
|
|
11373
|
-
persona.description += spaceTrim__default["default"]('\n\n' + personaDescription);
|
|
11374
|
-
}
|
|
11375
|
-
|
|
11376
|
-
/**
|
|
11377
|
-
* Checks if the given value is a valid JavaScript identifier name.
|
|
11378
|
-
*
|
|
11379
|
-
* @param javascriptName The value to check for JavaScript identifier validity.
|
|
11380
|
-
* @returns `true` if the value is a valid JavaScript name, false otherwise.
|
|
11381
|
-
* @public exported from `@promptbook/utils`
|
|
11382
|
-
*/
|
|
11383
|
-
function isValidJavascriptName(javascriptName) {
|
|
11384
|
-
if (typeof javascriptName !== 'string') {
|
|
11385
|
-
return false;
|
|
11386
|
-
}
|
|
11387
|
-
return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/i.test(javascriptName);
|
|
11697
|
+
Persona "${personaName}" is defined multiple times with different description:
|
|
11698
|
+
|
|
11699
|
+
First definition:
|
|
11700
|
+
${persona.description}
|
|
11701
|
+
|
|
11702
|
+
Second definition:
|
|
11703
|
+
${personaDescription}
|
|
11704
|
+
|
|
11705
|
+
`));
|
|
11706
|
+
persona.description += spaceTrim__default["default"]('\n\n' + personaDescription);
|
|
11388
11707
|
}
|
|
11389
11708
|
|
|
11390
11709
|
/**
|
|
@@ -12900,345 +13219,59 @@
|
|
|
12900
13219
|
if ($pipelineJson.formfactorName === undefined) {
|
|
12901
13220
|
$pipelineJson.formfactorName = 'GENERIC';
|
|
12902
13221
|
}
|
|
12903
|
-
// =============================================================
|
|
12904
|
-
return exportJson({
|
|
12905
|
-
name: 'pipelineJson',
|
|
12906
|
-
message: `Result of \`parsePipeline\``,
|
|
12907
|
-
order: ORDER_OF_PIPELINE_JSON,
|
|
12908
|
-
value: {
|
|
12909
|
-
formfactorName: 'GENERIC',
|
|
12910
|
-
// <- Note: [🔆] Setting `formfactorName` is redundant to satisfy the typescript
|
|
12911
|
-
...$pipelineJson,
|
|
12912
|
-
},
|
|
12913
|
-
});
|
|
12914
|
-
}
|
|
12915
|
-
/**
|
|
12916
|
-
* TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
|
|
12917
|
-
* TODO: [main] !!4 Warn if used only sync version
|
|
12918
|
-
* TODO: [🚞] Report here line/column of error
|
|
12919
|
-
* TODO: Use spaceTrim more effectively
|
|
12920
|
-
* TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
|
|
12921
|
-
* TODO: [🥞] Not optimal parsing because `splitMarkdownIntoSections` is executed twice with same string, once through `flattenMarkdown` and second directly here
|
|
12922
|
-
* TODO: [♈] Probably move expectations from tasks to parameters
|
|
12923
|
-
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
12924
|
-
* TODO: [🍙] Make some standard order of json properties
|
|
12925
|
-
*/
|
|
12926
|
-
|
|
12927
|
-
/**
|
|
12928
|
-
* Compile pipeline from string (markdown) format to JSON format
|
|
12929
|
-
*
|
|
12930
|
-
* @see https://github.com/webgptorg/promptbook/discussions/196
|
|
12931
|
-
*
|
|
12932
|
-
* Note: This function does not validate logic of the pipeline only the parsing
|
|
12933
|
-
* Note: This function acts as compilation process
|
|
12934
|
-
*
|
|
12935
|
-
* @param pipelineString {Promptbook} in string markdown format (.book.md)
|
|
12936
|
-
* @param tools - Tools for the preparation and scraping - if not provided together with `llm`, the preparation will be skipped
|
|
12937
|
-
* @param options - Options and tools for the compilation
|
|
12938
|
-
* @returns {Promptbook} compiled in JSON format (.bookc)
|
|
12939
|
-
* @throws {ParseError} if the promptbook string is not valid
|
|
12940
|
-
* @public exported from `@promptbook/core`
|
|
12941
|
-
*/
|
|
12942
|
-
async function compilePipeline(pipelineString, tools, options) {
|
|
12943
|
-
let pipelineJson = parsePipeline(pipelineString);
|
|
12944
|
-
if (tools !== undefined && tools.llm !== undefined) {
|
|
12945
|
-
pipelineJson = await preparePipeline(pipelineJson, tools, options || {
|
|
12946
|
-
rootDirname: null,
|
|
12947
|
-
});
|
|
12948
|
-
}
|
|
12949
|
-
// Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
|
|
12950
|
-
return pipelineJson;
|
|
12951
|
-
}
|
|
12952
|
-
/**
|
|
12953
|
-
* TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
|
|
12954
|
-
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
12955
|
-
* TODO: [🧠] Should be in generated JSON file GENERATOR_WARNING
|
|
12956
|
-
*/
|
|
12957
|
-
|
|
12958
|
-
/**
|
|
12959
|
-
* Creates a Mermaid graph based on the promptbook
|
|
12960
|
-
*
|
|
12961
|
-
* Note: The result is not wrapped in a Markdown code block
|
|
12962
|
-
*
|
|
12963
|
-
* @public exported from `@promptbook/utils`
|
|
12964
|
-
*/
|
|
12965
|
-
function renderPromptbookMermaid(pipelineJson, options) {
|
|
12966
|
-
const { linkTask = () => null } = options || {};
|
|
12967
|
-
const MERMAID_PREFIX = 'pipeline_';
|
|
12968
|
-
const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
|
|
12969
|
-
const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
|
|
12970
|
-
const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
|
|
12971
|
-
const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
|
|
12972
|
-
const parameterNameToTaskName = (parameterName) => {
|
|
12973
|
-
if (parameterName === 'knowledge') {
|
|
12974
|
-
return MERMAID_KNOWLEDGE_NAME;
|
|
12975
|
-
}
|
|
12976
|
-
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
12977
|
-
return MERMAID_RESERVED_NAME;
|
|
12978
|
-
}
|
|
12979
|
-
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
12980
|
-
if (!parameter) {
|
|
12981
|
-
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
12982
|
-
// <- TODO: This causes problems when {knowledge} and other reserved parameters are used
|
|
12983
|
-
}
|
|
12984
|
-
if (parameter.isInput) {
|
|
12985
|
-
return MERMAID_INPUT_NAME;
|
|
12986
|
-
}
|
|
12987
|
-
const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
|
|
12988
|
-
if (!task) {
|
|
12989
|
-
throw new Error(`Could not find task for {${parameterName}}`);
|
|
12990
|
-
}
|
|
12991
|
-
return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
|
|
12992
|
-
};
|
|
12993
|
-
const inputAndIntermediateParametersMermaid = pipelineJson.tasks
|
|
12994
|
-
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
12995
|
-
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
12996
|
-
...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
|
|
12997
|
-
])
|
|
12998
|
-
.join('\n');
|
|
12999
|
-
const outputParametersMermaid = pipelineJson.parameters
|
|
13000
|
-
.filter(({ isOutput }) => isOutput)
|
|
13001
|
-
.map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
|
|
13002
|
-
.join('\n');
|
|
13003
|
-
const linksMermaid = pipelineJson.tasks
|
|
13004
|
-
.map((task) => {
|
|
13005
|
-
const link = linkTask(task);
|
|
13006
|
-
if (link === null) {
|
|
13007
|
-
return '';
|
|
13008
|
-
}
|
|
13009
|
-
const { href, title } = link;
|
|
13010
|
-
const taskName = parameterNameToTaskName(task.resultingParameterName);
|
|
13011
|
-
return `click ${taskName} href "${href}" "${title}";`;
|
|
13012
|
-
})
|
|
13013
|
-
.filter((line) => line !== '')
|
|
13014
|
-
.join('\n');
|
|
13015
|
-
const interactionPointsMermaid = Object.entries({
|
|
13016
|
-
[MERMAID_INPUT_NAME]: 'Input',
|
|
13017
|
-
[MERMAID_OUTPUT_NAME]: 'Output',
|
|
13018
|
-
[MERMAID_RESERVED_NAME]: 'Other',
|
|
13019
|
-
[MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
|
|
13020
|
-
})
|
|
13021
|
-
.filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
|
|
13022
|
-
.map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
|
|
13023
|
-
.join('\n');
|
|
13024
|
-
const promptbookMermaid = spaceTrim$1.spaceTrim((block) => `
|
|
13025
|
-
|
|
13026
|
-
%% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
|
|
13027
|
-
|
|
13028
|
-
flowchart LR
|
|
13029
|
-
subgraph "${pipelineJson.title}"
|
|
13030
|
-
|
|
13031
|
-
%% Basic configuration
|
|
13032
|
-
direction TB
|
|
13033
|
-
|
|
13034
|
-
%% Interaction points from pipeline to outside
|
|
13035
|
-
${block(interactionPointsMermaid)}
|
|
13036
|
-
|
|
13037
|
-
%% Input and intermediate parameters
|
|
13038
|
-
${block(inputAndIntermediateParametersMermaid)}
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
%% Output parameters
|
|
13042
|
-
${block(outputParametersMermaid)}
|
|
13043
|
-
|
|
13044
|
-
%% Links
|
|
13045
|
-
${block(linksMermaid)}
|
|
13046
|
-
|
|
13047
|
-
%% Styles
|
|
13048
|
-
classDef ${MERMAID_INPUT_NAME} color: grey;
|
|
13049
|
-
classDef ${MERMAID_OUTPUT_NAME} color: grey;
|
|
13050
|
-
classDef ${MERMAID_RESERVED_NAME} color: grey;
|
|
13051
|
-
classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
|
|
13052
|
-
|
|
13053
|
-
end;
|
|
13054
|
-
|
|
13055
|
-
`);
|
|
13056
|
-
return promptbookMermaid;
|
|
13057
|
-
}
|
|
13058
|
-
/**
|
|
13059
|
-
* TODO: [🧠] FOREACH in mermaid graph
|
|
13060
|
-
* TODO: [🧠] Knowledge in mermaid graph
|
|
13061
|
-
* TODO: [🧠] Personas in mermaid graph
|
|
13062
|
-
* TODO: Maybe use some Mermaid package instead of string templating
|
|
13063
|
-
* TODO: [🕌] When more than 2 functionalities, split into separate functions
|
|
13064
|
-
*/
|
|
13065
|
-
|
|
13066
|
-
/**
|
|
13067
|
-
* Computes SHA-256 hash of the given object
|
|
13068
|
-
*
|
|
13069
|
-
* @public exported from `@promptbook/utils`
|
|
13070
|
-
*/
|
|
13071
|
-
function computeHash(value) {
|
|
13072
|
-
return cryptoJs.SHA256(hexEncoder__default["default"].parse(spaceTrim__default["default"](valueToString(value)))).toString( /* hex */);
|
|
13073
|
-
}
|
|
13074
|
-
/**
|
|
13075
|
-
* TODO: [🥬][🥬] Use this ACRY
|
|
13076
|
-
*/
|
|
13077
|
-
|
|
13078
|
-
/**
|
|
13079
|
-
* Makes first letter of a string lowercase
|
|
13080
|
-
*
|
|
13081
|
-
* Note: [🔂] This function is idempotent.
|
|
13082
|
-
*
|
|
13083
|
-
* @public exported from `@promptbook/utils`
|
|
13084
|
-
*/
|
|
13085
|
-
function decapitalize(word) {
|
|
13086
|
-
return word.substring(0, 1).toLowerCase() + word.substring(1);
|
|
13087
|
-
}
|
|
13088
|
-
|
|
13089
|
-
/**
|
|
13090
|
-
* Parses keywords from a string
|
|
13091
|
-
*
|
|
13092
|
-
* @param {string} input
|
|
13093
|
-
* @returns {Set} of keywords without diacritics in lowercase
|
|
13094
|
-
* @public exported from `@promptbook/utils`
|
|
13095
|
-
*/
|
|
13096
|
-
function parseKeywordsFromString(input) {
|
|
13097
|
-
const keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
|
|
13098
|
-
.toLowerCase()
|
|
13099
|
-
.split(/[^a-z0-9]+/gs)
|
|
13100
|
-
.filter((value) => value);
|
|
13101
|
-
return new Set(keywords);
|
|
13102
|
-
}
|
|
13103
|
-
|
|
13104
|
-
/**
|
|
13105
|
-
* Converts a name string into a URI-compatible format.
|
|
13106
|
-
*
|
|
13107
|
-
* @param name The string to be converted to a URI-compatible format.
|
|
13108
|
-
* @returns A URI-compatible string derived from the input name.
|
|
13109
|
-
* @example 'Hello World' -> 'hello-world'
|
|
13110
|
-
* @public exported from `@promptbook/utils`
|
|
13111
|
-
*/
|
|
13112
|
-
function nameToUriPart(name) {
|
|
13113
|
-
let uriPart = name;
|
|
13114
|
-
uriPart = uriPart.toLowerCase();
|
|
13115
|
-
uriPart = removeDiacritics(uriPart);
|
|
13116
|
-
uriPart = uriPart.replace(/[^a-zA-Z0-9]+/g, '-');
|
|
13117
|
-
uriPart = uriPart.replace(/^-+/, '');
|
|
13118
|
-
uriPart = uriPart.replace(/-+$/, '');
|
|
13119
|
-
return uriPart;
|
|
13120
|
-
}
|
|
13121
|
-
|
|
13122
|
-
/**
|
|
13123
|
-
* Converts a given name into URI-compatible parts.
|
|
13124
|
-
*
|
|
13125
|
-
* @param name The name to be converted into URI parts.
|
|
13126
|
-
* @returns An array of URI-compatible parts derived from the name.
|
|
13127
|
-
* @example 'Example Name' -> ['example', 'name']
|
|
13128
|
-
* @public exported from `@promptbook/utils`
|
|
13129
|
-
*/
|
|
13130
|
-
function nameToUriParts(name) {
|
|
13131
|
-
return nameToUriPart(name)
|
|
13132
|
-
.split('-')
|
|
13133
|
-
.filter((value) => value !== '');
|
|
13134
|
-
}
|
|
13135
|
-
|
|
13136
|
-
/**
|
|
13137
|
-
* Normalizes a given text to PascalCase format.
|
|
13138
|
-
*
|
|
13139
|
-
* Note: [🔂] This function is idempotent.
|
|
13140
|
-
*
|
|
13141
|
-
* @param text @public exported from `@promptbook/utils`
|
|
13142
|
-
* @returns
|
|
13143
|
-
* @example 'HelloWorld'
|
|
13144
|
-
* @example 'ILovePromptbook'
|
|
13145
|
-
* @public exported from `@promptbook/utils`
|
|
13146
|
-
*/
|
|
13147
|
-
function normalizeTo_PascalCase(text) {
|
|
13148
|
-
return normalizeTo_camelCase(text, true);
|
|
13222
|
+
// =============================================================
|
|
13223
|
+
return exportJson({
|
|
13224
|
+
name: 'pipelineJson',
|
|
13225
|
+
message: `Result of \`parsePipeline\``,
|
|
13226
|
+
order: ORDER_OF_PIPELINE_JSON,
|
|
13227
|
+
value: {
|
|
13228
|
+
formfactorName: 'GENERIC',
|
|
13229
|
+
// <- Note: [🔆] Setting `formfactorName` is redundant to satisfy the typescript
|
|
13230
|
+
...$pipelineJson,
|
|
13231
|
+
},
|
|
13232
|
+
});
|
|
13149
13233
|
}
|
|
13150
|
-
|
|
13151
13234
|
/**
|
|
13152
|
-
*
|
|
13153
|
-
*
|
|
13154
|
-
*
|
|
13155
|
-
*
|
|
13156
|
-
*
|
|
13235
|
+
* TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
|
|
13236
|
+
* TODO: [main] !!4 Warn if used only sync version
|
|
13237
|
+
* TODO: [🚞] Report here line/column of error
|
|
13238
|
+
* TODO: Use spaceTrim more effectively
|
|
13239
|
+
* TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
|
|
13240
|
+
* TODO: [🥞] Not optimal parsing because `splitMarkdownIntoSections` is executed twice with same string, once through `flattenMarkdown` and second directly here
|
|
13241
|
+
* TODO: [♈] Probably move expectations from tasks to parameters
|
|
13242
|
+
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
13243
|
+
* TODO: [🍙] Make some standard order of json properties
|
|
13157
13244
|
*/
|
|
13158
|
-
function normalizeWhitespaces(sentence) {
|
|
13159
|
-
return sentence.replace(/\s+/gs, ' ').trim();
|
|
13160
|
-
}
|
|
13161
13245
|
|
|
13162
13246
|
/**
|
|
13163
|
-
*
|
|
13247
|
+
* Compile pipeline from string (markdown) format to JSON format
|
|
13164
13248
|
*
|
|
13165
|
-
* @
|
|
13166
|
-
*/
|
|
13167
|
-
function suffixUrl(value, suffix) {
|
|
13168
|
-
const baseUrl = value.href.endsWith('/') ? value.href.slice(0, -1) : value.href;
|
|
13169
|
-
const normalizedSuffix = suffix.replace(/\/+/g, '/');
|
|
13170
|
-
return (baseUrl + normalizedSuffix);
|
|
13171
|
-
}
|
|
13172
|
-
|
|
13173
|
-
/**
|
|
13174
|
-
* Removes quotes and optional introduce text from a string
|
|
13249
|
+
* @see https://github.com/webgptorg/promptbook/discussions/196
|
|
13175
13250
|
*
|
|
13176
|
-
*
|
|
13177
|
-
* Note: This function
|
|
13178
|
-
* Note: There are two similar functions:
|
|
13179
|
-
* - `removeQuotes` which removes only bounding quotes
|
|
13180
|
-
* - `unwrapResult` which removes whole introduce sentence
|
|
13251
|
+
* Note: This function does not validate logic of the pipeline only the parsing
|
|
13252
|
+
* Note: This function acts as compilation process
|
|
13181
13253
|
*
|
|
13182
|
-
* @param
|
|
13183
|
-
* @
|
|
13184
|
-
* @
|
|
13254
|
+
* @param pipelineString {Promptbook} in string markdown format (.book.md)
|
|
13255
|
+
* @param tools - Tools for the preparation and scraping - if not provided together with `llm`, the preparation will be skipped
|
|
13256
|
+
* @param options - Options and tools for the compilation
|
|
13257
|
+
* @returns {Promptbook} compiled in JSON format (.bookc)
|
|
13258
|
+
* @throws {ParseError} if the promptbook string is not valid
|
|
13259
|
+
* @public exported from `@promptbook/core`
|
|
13185
13260
|
*/
|
|
13186
|
-
function
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
}
|
|
13193
|
-
let processedText = trimmedText;
|
|
13194
|
-
if (isIntroduceSentenceRemoved) {
|
|
13195
|
-
const introduceSentenceRegex = /^[a-zěščřžýáíéúů:\s]*:\s*/i;
|
|
13196
|
-
if (introduceSentenceRegex.test(text)) {
|
|
13197
|
-
// Remove the introduce sentence and quotes by replacing it with an empty string
|
|
13198
|
-
processedText = processedText.replace(introduceSentenceRegex, '');
|
|
13199
|
-
}
|
|
13200
|
-
processedText = spaceTrim$1.spaceTrim(processedText);
|
|
13201
|
-
}
|
|
13202
|
-
if (processedText.length < 3) {
|
|
13203
|
-
return trimmedText;
|
|
13204
|
-
}
|
|
13205
|
-
if (processedText.includes('\n')) {
|
|
13206
|
-
return trimmedText;
|
|
13207
|
-
}
|
|
13208
|
-
// Remove the quotes by extracting the substring without the first and last characters
|
|
13209
|
-
const unquotedText = processedText.slice(1, -1);
|
|
13210
|
-
// Check if the text starts and ends with quotes
|
|
13211
|
-
if ([
|
|
13212
|
-
['"', '"'],
|
|
13213
|
-
["'", "'"],
|
|
13214
|
-
['`', '`'],
|
|
13215
|
-
['*', '*'],
|
|
13216
|
-
['_', '_'],
|
|
13217
|
-
['„', '“'],
|
|
13218
|
-
['«', '»'] /* <- QUOTES to config */,
|
|
13219
|
-
].some(([startQuote, endQuote]) => {
|
|
13220
|
-
if (!processedText.startsWith(startQuote)) {
|
|
13221
|
-
return false;
|
|
13222
|
-
}
|
|
13223
|
-
if (!processedText.endsWith(endQuote)) {
|
|
13224
|
-
return false;
|
|
13225
|
-
}
|
|
13226
|
-
if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
|
|
13227
|
-
return false;
|
|
13228
|
-
}
|
|
13229
|
-
if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
|
|
13230
|
-
return false;
|
|
13231
|
-
}
|
|
13232
|
-
return true;
|
|
13233
|
-
})) {
|
|
13234
|
-
return unwrapResult(unquotedText, { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
13235
|
-
}
|
|
13236
|
-
else {
|
|
13237
|
-
return processedText;
|
|
13261
|
+
async function compilePipeline(pipelineString, tools, options) {
|
|
13262
|
+
let pipelineJson = parsePipeline(pipelineString);
|
|
13263
|
+
if (tools !== undefined && tools.llm !== undefined) {
|
|
13264
|
+
pipelineJson = await preparePipeline(pipelineJson, tools, options || {
|
|
13265
|
+
rootDirname: null,
|
|
13266
|
+
});
|
|
13238
13267
|
}
|
|
13268
|
+
// Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
|
|
13269
|
+
return pipelineJson;
|
|
13239
13270
|
}
|
|
13240
13271
|
/**
|
|
13241
|
-
* TODO: [
|
|
13272
|
+
* TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
|
|
13273
|
+
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
13274
|
+
* TODO: [🧠] Should be in generated JSON file GENERATOR_WARNING
|
|
13242
13275
|
*/
|
|
13243
13276
|
|
|
13244
13277
|
/**
|
|
@@ -19453,7 +19486,7 @@
|
|
|
19453
19486
|
let threadMessages = [];
|
|
19454
19487
|
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
19455
19488
|
threadMessages = prompt.thread.map((msg) => ({
|
|
19456
|
-
role: msg.
|
|
19489
|
+
role: msg.sender === 'assistant' ? 'assistant' : 'user',
|
|
19457
19490
|
content: msg.content,
|
|
19458
19491
|
}));
|
|
19459
19492
|
}
|
|
@@ -19866,13 +19899,14 @@
|
|
|
19866
19899
|
const modelName = currentModelRequirements.modelName || this.getDefaultImageGenerationModel().modelName;
|
|
19867
19900
|
const modelSettings = {
|
|
19868
19901
|
model: modelName,
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19902
|
+
size: currentModelRequirements.size,
|
|
19903
|
+
quality: currentModelRequirements.quality,
|
|
19904
|
+
style: currentModelRequirements.style,
|
|
19872
19905
|
};
|
|
19873
19906
|
const rawPromptContent = templateParameters(content, { ...parameters, modelName });
|
|
19874
19907
|
const rawRequest = {
|
|
19875
19908
|
...modelSettings,
|
|
19909
|
+
size: modelSettings.size || '1024x1024',
|
|
19876
19910
|
prompt: rawPromptContent,
|
|
19877
19911
|
user: (_a = this.options.userId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
19878
19912
|
response_format: 'url', // TODO: [🧠] Maybe allow b64_json
|
|
@@ -20409,10 +20443,10 @@
|
|
|
20409
20443
|
// <- TODO: [🛄]
|
|
20410
20444
|
}
|
|
20411
20445
|
/**
|
|
20412
|
-
* Default model for
|
|
20446
|
+
* Default model for completion variant.
|
|
20413
20447
|
*/
|
|
20414
20448
|
getDefaultImageGenerationModel() {
|
|
20415
|
-
return this.getDefaultModel('
|
|
20449
|
+
return this.getDefaultModel('dall-e-3');
|
|
20416
20450
|
// <- TODO: [🛄]
|
|
20417
20451
|
}
|
|
20418
20452
|
}
|
|
@@ -21227,11 +21261,10 @@
|
|
|
21227
21261
|
throw new PipelineExecutionError(`${this.title} does not support EMBEDDING model variant`);
|
|
21228
21262
|
}
|
|
21229
21263
|
/**
|
|
21230
|
-
* Default model for
|
|
21264
|
+
* Default model for completion variant.
|
|
21231
21265
|
*/
|
|
21232
21266
|
getDefaultImageGenerationModel() {
|
|
21233
|
-
|
|
21234
|
-
// <- TODO: [🛄]
|
|
21267
|
+
throw new PipelineExecutionError(`${this.title} does not support IMAGE_GENERATION model variant`);
|
|
21235
21268
|
}
|
|
21236
21269
|
}
|
|
21237
21270
|
/**
|
|
@@ -23107,6 +23140,114 @@
|
|
|
23107
23140
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23108
23141
|
*/
|
|
23109
23142
|
|
|
23143
|
+
/**
|
|
23144
|
+
* DICTIONARY commitment definition
|
|
23145
|
+
*
|
|
23146
|
+
* The DICTIONARY commitment defines specific terms and their meanings that the agent should use correctly
|
|
23147
|
+
* in its reasoning and responses. This ensures consistent terminology usage.
|
|
23148
|
+
*
|
|
23149
|
+
* Key features:
|
|
23150
|
+
* - Multiple DICTIONARY commitments are automatically merged into one
|
|
23151
|
+
* - Content is placed in a dedicated section of the system message
|
|
23152
|
+
* - Terms and definitions are stored in metadata.DICTIONARY for debugging
|
|
23153
|
+
* - Agent should use the defined terms correctly in responses
|
|
23154
|
+
*
|
|
23155
|
+
* Example usage in agent source:
|
|
23156
|
+
*
|
|
23157
|
+
* ```book
|
|
23158
|
+
* Legal Assistant
|
|
23159
|
+
*
|
|
23160
|
+
* PERSONA You are a knowledgeable legal assistant
|
|
23161
|
+
* DICTIONARY Misdemeanor is a minor wrongdoing or criminal offense
|
|
23162
|
+
* DICTIONARY Felony is a serious crime usually punishable by imprisonment for more than one year
|
|
23163
|
+
* DICTIONARY Tort is a civil wrong that causes harm or loss to another person, leading to legal liability
|
|
23164
|
+
* ```
|
|
23165
|
+
*
|
|
23166
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
23167
|
+
*/
|
|
23168
|
+
class DictionaryCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23169
|
+
constructor() {
|
|
23170
|
+
super('DICTIONARY');
|
|
23171
|
+
}
|
|
23172
|
+
/**
|
|
23173
|
+
* Short one-line description of DICTIONARY.
|
|
23174
|
+
*/
|
|
23175
|
+
get description() {
|
|
23176
|
+
return 'Define terms and their meanings for consistent terminology usage.';
|
|
23177
|
+
}
|
|
23178
|
+
/**
|
|
23179
|
+
* Icon for this commitment.
|
|
23180
|
+
*/
|
|
23181
|
+
get icon() {
|
|
23182
|
+
return '📚';
|
|
23183
|
+
}
|
|
23184
|
+
/**
|
|
23185
|
+
* Markdown documentation for DICTIONARY commitment.
|
|
23186
|
+
*/
|
|
23187
|
+
get documentation() {
|
|
23188
|
+
return spaceTrim$1.spaceTrim(`
|
|
23189
|
+
# DICTIONARY
|
|
23190
|
+
|
|
23191
|
+
Defines specific terms and their meanings that the agent should use correctly in reasoning and responses.
|
|
23192
|
+
|
|
23193
|
+
## Key aspects
|
|
23194
|
+
|
|
23195
|
+
- Multiple \`DICTIONARY\` commitments are merged together.
|
|
23196
|
+
- Terms are defined in the format: "Term is definition"
|
|
23197
|
+
- The agent should use these terms consistently in responses.
|
|
23198
|
+
- Definitions help ensure accurate and consistent terminology.
|
|
23199
|
+
|
|
23200
|
+
## Examples
|
|
23201
|
+
|
|
23202
|
+
\`\`\`book
|
|
23203
|
+
Legal Assistant
|
|
23204
|
+
|
|
23205
|
+
PERSONA You are a knowledgeable legal assistant specializing in criminal law
|
|
23206
|
+
DICTIONARY Misdemeanor is a minor wrongdoing or criminal offense
|
|
23207
|
+
DICTIONARY Felony is a serious crime usually punishable by imprisonment for more than one year
|
|
23208
|
+
DICTIONARY Tort is a civil wrong that causes harm or loss to another person, leading to legal liability
|
|
23209
|
+
\`\`\`
|
|
23210
|
+
|
|
23211
|
+
\`\`\`book
|
|
23212
|
+
Medical Assistant
|
|
23213
|
+
|
|
23214
|
+
PERSONA You are a helpful medical assistant
|
|
23215
|
+
DICTIONARY Hypertension is persistently high blood pressure
|
|
23216
|
+
DICTIONARY Diabetes is a chronic condition that affects how the body processes blood sugar
|
|
23217
|
+
DICTIONARY Vaccine is a biological preparation that provides active immunity to a particular disease
|
|
23218
|
+
\`\`\`
|
|
23219
|
+
`);
|
|
23220
|
+
}
|
|
23221
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
23222
|
+
var _a;
|
|
23223
|
+
const trimmedContent = content.trim();
|
|
23224
|
+
if (!trimmedContent) {
|
|
23225
|
+
return requirements;
|
|
23226
|
+
}
|
|
23227
|
+
// Get existing dictionary entries from metadata
|
|
23228
|
+
const existingDictionary = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.DICTIONARY) || '';
|
|
23229
|
+
// Merge the new dictionary entry with existing entries
|
|
23230
|
+
const mergedDictionary = existingDictionary
|
|
23231
|
+
? `${existingDictionary}\n${trimmedContent}`
|
|
23232
|
+
: trimmedContent;
|
|
23233
|
+
// Store the merged dictionary in metadata for debugging and inspection
|
|
23234
|
+
const updatedMetadata = {
|
|
23235
|
+
...requirements.metadata,
|
|
23236
|
+
DICTIONARY: mergedDictionary,
|
|
23237
|
+
};
|
|
23238
|
+
// Create the dictionary section for the system message
|
|
23239
|
+
// Format: "# DICTIONARY\nTerm: definition\nTerm: definition..."
|
|
23240
|
+
const dictionarySection = `# DICTIONARY\n${mergedDictionary}`;
|
|
23241
|
+
return {
|
|
23242
|
+
...this.appendToSystemMessage(requirements, dictionarySection),
|
|
23243
|
+
metadata: updatedMetadata,
|
|
23244
|
+
};
|
|
23245
|
+
}
|
|
23246
|
+
}
|
|
23247
|
+
/**
|
|
23248
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23249
|
+
*/
|
|
23250
|
+
|
|
23110
23251
|
/**
|
|
23111
23252
|
* FORMAT commitment definition
|
|
23112
23253
|
*
|
|
@@ -25927,6 +26068,7 @@
|
|
|
25927
26068
|
new DeleteCommitmentDefinition('CANCEL'),
|
|
25928
26069
|
new DeleteCommitmentDefinition('DISCARD'),
|
|
25929
26070
|
new DeleteCommitmentDefinition('REMOVE'),
|
|
26071
|
+
new DictionaryCommitmentDefinition(),
|
|
25930
26072
|
new OpenCommitmentDefinition(),
|
|
25931
26073
|
new ClosedCommitmentDefinition(),
|
|
25932
26074
|
new UseBrowserCommitmentDefinition(),
|
|
@@ -26011,17 +26153,64 @@
|
|
|
26011
26153
|
};
|
|
26012
26154
|
}
|
|
26013
26155
|
const lines = agentSource.split('\n');
|
|
26014
|
-
|
|
26156
|
+
let agentName = null;
|
|
26157
|
+
let agentNameLineIndex = -1;
|
|
26158
|
+
// Find the agent name: first non-empty line that is not a commitment and not a horizontal line
|
|
26159
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26160
|
+
const line = lines[i];
|
|
26161
|
+
if (line === undefined) {
|
|
26162
|
+
continue;
|
|
26163
|
+
}
|
|
26164
|
+
const trimmed = line.trim();
|
|
26165
|
+
if (!trimmed) {
|
|
26166
|
+
continue;
|
|
26167
|
+
}
|
|
26168
|
+
const isHorizontal = HORIZONTAL_LINE_PATTERN.test(line);
|
|
26169
|
+
if (isHorizontal) {
|
|
26170
|
+
continue;
|
|
26171
|
+
}
|
|
26172
|
+
let isCommitment = false;
|
|
26173
|
+
for (const definition of COMMITMENT_REGISTRY) {
|
|
26174
|
+
const typeRegex = definition.createTypeRegex();
|
|
26175
|
+
const match = typeRegex.exec(trimmed);
|
|
26176
|
+
if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
26177
|
+
isCommitment = true;
|
|
26178
|
+
break;
|
|
26179
|
+
}
|
|
26180
|
+
}
|
|
26181
|
+
if (!isCommitment) {
|
|
26182
|
+
agentName = trimmed;
|
|
26183
|
+
agentNameLineIndex = i;
|
|
26184
|
+
break;
|
|
26185
|
+
}
|
|
26186
|
+
}
|
|
26015
26187
|
const commitments = [];
|
|
26016
26188
|
const nonCommitmentLines = [];
|
|
26017
|
-
//
|
|
26018
|
-
|
|
26019
|
-
|
|
26189
|
+
// Add lines before agentName that are horizontal lines (they are non-commitment)
|
|
26190
|
+
for (let i = 0; i < agentNameLineIndex; i++) {
|
|
26191
|
+
const line = lines[i];
|
|
26192
|
+
if (line === undefined) {
|
|
26193
|
+
continue;
|
|
26194
|
+
}
|
|
26195
|
+
const trimmed = line.trim();
|
|
26196
|
+
if (!trimmed) {
|
|
26197
|
+
continue;
|
|
26198
|
+
}
|
|
26199
|
+
const isHorizontal = HORIZONTAL_LINE_PATTERN.test(line);
|
|
26200
|
+
if (isHorizontal) {
|
|
26201
|
+
nonCommitmentLines.push(line);
|
|
26202
|
+
}
|
|
26203
|
+
// Note: Commitments before agentName are not added to nonCommitmentLines
|
|
26204
|
+
}
|
|
26205
|
+
// Add the agent name line to non-commitment lines
|
|
26206
|
+
if (agentNameLineIndex >= 0) {
|
|
26207
|
+
nonCommitmentLines.push(lines[agentNameLineIndex]);
|
|
26020
26208
|
}
|
|
26021
26209
|
// Parse commitments with multiline support
|
|
26022
26210
|
let currentCommitment = null;
|
|
26023
|
-
// Process lines starting from the
|
|
26024
|
-
|
|
26211
|
+
// Process lines starting from after the agent name line
|
|
26212
|
+
const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
|
|
26213
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
26025
26214
|
const line = lines[i];
|
|
26026
26215
|
if (line === undefined) {
|
|
26027
26216
|
continue;
|
|
@@ -26241,7 +26430,12 @@
|
|
|
26241
26430
|
};
|
|
26242
26431
|
}
|
|
26243
26432
|
// Apply each commitment in order using reduce-like pattern
|
|
26244
|
-
for (
|
|
26433
|
+
for (let i = 0; i < filteredCommitments.length; i++) {
|
|
26434
|
+
const commitment = filteredCommitments[i];
|
|
26435
|
+
// CLOSED commitment should work only if its the last commitment in the book
|
|
26436
|
+
if (commitment.type === 'CLOSED' && i !== filteredCommitments.length - 1) {
|
|
26437
|
+
continue;
|
|
26438
|
+
}
|
|
26245
26439
|
const definition = getCommitmentDefinition(commitment.type);
|
|
26246
26440
|
if (definition) {
|
|
26247
26441
|
try {
|
|
@@ -26282,44 +26476,6 @@
|
|
|
26282
26476
|
};
|
|
26283
26477
|
}
|
|
26284
26478
|
|
|
26285
|
-
/**
|
|
26286
|
-
* Generates a gravatar URL based on agent name for fallback avatar
|
|
26287
|
-
*
|
|
26288
|
-
* @param agentName The agent name to generate avatar for
|
|
26289
|
-
* @returns Gravatar URL
|
|
26290
|
-
*
|
|
26291
|
-
* @private - [🤹] The fact that profile image is Gravatar is just implementation detail which should be hidden for consumer
|
|
26292
|
-
*/
|
|
26293
|
-
function generateGravatarUrl(agentName) {
|
|
26294
|
-
// Use a default name if none provided
|
|
26295
|
-
const safeName = agentName || 'Anonymous Agent';
|
|
26296
|
-
// Create a simple hash from the name for consistent avatar
|
|
26297
|
-
let hash = 0;
|
|
26298
|
-
for (let i = 0; i < safeName.length; i++) {
|
|
26299
|
-
const char = safeName.charCodeAt(i);
|
|
26300
|
-
hash = (hash << 5) - hash + char;
|
|
26301
|
-
hash = hash & hash; // Convert to 32bit integer
|
|
26302
|
-
}
|
|
26303
|
-
const avatarId = Math.abs(hash).toString();
|
|
26304
|
-
return `https://www.gravatar.com/avatar/${avatarId}?default=robohash&size=200&rating=x`;
|
|
26305
|
-
}
|
|
26306
|
-
|
|
26307
|
-
/**
|
|
26308
|
-
* Generates an image for the agent to use as profile image
|
|
26309
|
-
*
|
|
26310
|
-
* @param agentName The agent name to generate avatar for
|
|
26311
|
-
* @returns The placeholder profile image URL for the agent
|
|
26312
|
-
*
|
|
26313
|
-
* @public exported from `@promptbook/core`
|
|
26314
|
-
*/
|
|
26315
|
-
function generatePlaceholderAgentProfileImageUrl(agentName) {
|
|
26316
|
-
// Note: [🤹] The fact that profile image is Gravatar is just implementation detail which should be hidden for consumer
|
|
26317
|
-
return generateGravatarUrl(agentName);
|
|
26318
|
-
}
|
|
26319
|
-
/**
|
|
26320
|
-
* TODO: [🤹] Figure out best placeholder image generator https://i.pravatar.cc/1000?u=568
|
|
26321
|
-
*/
|
|
26322
|
-
|
|
26323
26479
|
/**
|
|
26324
26480
|
* Computes SHA-256 hash of the agent source
|
|
26325
26481
|
*
|
|
@@ -26387,7 +26543,57 @@
|
|
|
26387
26543
|
}
|
|
26388
26544
|
const meta = {};
|
|
26389
26545
|
const links = [];
|
|
26546
|
+
const capabilities = [];
|
|
26390
26547
|
for (const commitment of parseResult.commitments) {
|
|
26548
|
+
if (commitment.type === 'USE BROWSER') {
|
|
26549
|
+
capabilities.push({
|
|
26550
|
+
type: 'browser',
|
|
26551
|
+
label: 'Browser',
|
|
26552
|
+
iconName: 'Globe',
|
|
26553
|
+
});
|
|
26554
|
+
continue;
|
|
26555
|
+
}
|
|
26556
|
+
if (commitment.type === 'USE SEARCH ENGINE') {
|
|
26557
|
+
capabilities.push({
|
|
26558
|
+
type: 'search-engine',
|
|
26559
|
+
label: 'Search Internet',
|
|
26560
|
+
iconName: 'Search',
|
|
26561
|
+
});
|
|
26562
|
+
continue;
|
|
26563
|
+
}
|
|
26564
|
+
if (commitment.type === 'KNOWLEDGE') {
|
|
26565
|
+
const content = spaceTrim__default["default"](commitment.content).split('\n')[0] || '';
|
|
26566
|
+
let label = content;
|
|
26567
|
+
let iconName = 'Book';
|
|
26568
|
+
if (content.startsWith('http://') || content.startsWith('https://')) {
|
|
26569
|
+
try {
|
|
26570
|
+
const url = new URL(content);
|
|
26571
|
+
if (url.pathname.endsWith('.pdf')) {
|
|
26572
|
+
label = url.pathname.split('/').pop() || 'Document.pdf';
|
|
26573
|
+
iconName = 'FileText';
|
|
26574
|
+
}
|
|
26575
|
+
else {
|
|
26576
|
+
label = url.hostname.replace(/^www\./, '');
|
|
26577
|
+
}
|
|
26578
|
+
}
|
|
26579
|
+
catch (e) {
|
|
26580
|
+
// Invalid URL, treat as text
|
|
26581
|
+
}
|
|
26582
|
+
}
|
|
26583
|
+
else {
|
|
26584
|
+
// Text content - take first few words
|
|
26585
|
+
const words = content.split(/\s+/);
|
|
26586
|
+
if (words.length > 4) {
|
|
26587
|
+
label = words.slice(0, 4).join(' ') + '...';
|
|
26588
|
+
}
|
|
26589
|
+
}
|
|
26590
|
+
capabilities.push({
|
|
26591
|
+
type: 'knowledge',
|
|
26592
|
+
label,
|
|
26593
|
+
iconName,
|
|
26594
|
+
});
|
|
26595
|
+
continue;
|
|
26596
|
+
}
|
|
26391
26597
|
if (commitment.type === 'META LINK') {
|
|
26392
26598
|
const linkValue = spaceTrim__default["default"](commitment.content);
|
|
26393
26599
|
links.push(linkValue);
|
|
@@ -26417,10 +26623,6 @@
|
|
|
26417
26623
|
const metaType = normalizeTo_camelCase(metaTypeRaw);
|
|
26418
26624
|
meta[metaType] = spaceTrim__default["default"](commitment.content.substring(metaTypeRaw.length));
|
|
26419
26625
|
}
|
|
26420
|
-
// Generate gravatar fallback if no meta image specified
|
|
26421
|
-
if (!meta.image) {
|
|
26422
|
-
meta.image = generatePlaceholderAgentProfileImageUrl(parseResult.agentName || '!!');
|
|
26423
|
-
}
|
|
26424
26626
|
// Generate fullname fallback if no meta fullname specified
|
|
26425
26627
|
if (!meta.fullname) {
|
|
26426
26628
|
meta.fullname = parseResult.agentName || createDefaultAgentName(agentSource);
|
|
@@ -26432,11 +26634,13 @@
|
|
|
26432
26634
|
return {
|
|
26433
26635
|
agentName: normalizeAgentName(parseResult.agentName || createDefaultAgentName(agentSource)),
|
|
26434
26636
|
agentHash,
|
|
26637
|
+
permanentId: meta.id,
|
|
26435
26638
|
personaDescription,
|
|
26436
26639
|
initialMessage,
|
|
26437
26640
|
meta,
|
|
26438
26641
|
links,
|
|
26439
26642
|
parameters,
|
|
26643
|
+
capabilities,
|
|
26440
26644
|
};
|
|
26441
26645
|
}
|
|
26442
26646
|
/**
|