@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/esm/index.es.js
CHANGED
|
@@ -10,11 +10,11 @@ import hexEncoder from 'crypto-js/enc-hex';
|
|
|
10
10
|
import sha256 from 'crypto-js/sha256';
|
|
11
11
|
import { randomBytes } from 'crypto';
|
|
12
12
|
import { io } from 'socket.io-client';
|
|
13
|
+
import { SHA256 } from 'crypto-js';
|
|
13
14
|
import { Subject } from 'rxjs';
|
|
14
15
|
import { spawn } from 'child_process';
|
|
15
16
|
import JSZip from 'jszip';
|
|
16
17
|
import { parse, unparse } from 'papaparse';
|
|
17
|
-
import { SHA256 } from 'crypto-js';
|
|
18
18
|
import { lookup, extension } from 'mime-types';
|
|
19
19
|
import glob from 'glob-promise';
|
|
20
20
|
import moment from 'moment';
|
|
@@ -47,7 +47,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
47
47
|
* @generated
|
|
48
48
|
* @see https://github.com/webgptorg/promptbook
|
|
49
49
|
*/
|
|
50
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.104.0-
|
|
50
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.104.0-11';
|
|
51
51
|
/**
|
|
52
52
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
53
53
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -57,6 +57,8 @@ const PROMPTBOOK_ENGINE_VERSION = '0.104.0-1';
|
|
|
57
57
|
* Core Promptbook server configuration.
|
|
58
58
|
*
|
|
59
59
|
* This server is also used for auto-federation in the Agents Server.
|
|
60
|
+
*
|
|
61
|
+
* @public exported from `@promptbook/core`
|
|
60
62
|
*/
|
|
61
63
|
const CORE_SERVER = {
|
|
62
64
|
title: 'Promptbook Core',
|
|
@@ -1373,13 +1375,14 @@ class EnvironmentMismatchError extends Error {
|
|
|
1373
1375
|
*
|
|
1374
1376
|
* @public exported from `@promptbook/utils`
|
|
1375
1377
|
*/
|
|
1376
|
-
|
|
1378
|
+
function $isRunningInNode() {
|
|
1377
1379
|
try {
|
|
1378
|
-
return
|
|
1379
|
-
}
|
|
1380
|
+
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
1381
|
+
}
|
|
1382
|
+
catch (e) {
|
|
1380
1383
|
return false;
|
|
1381
1384
|
}
|
|
1382
|
-
|
|
1385
|
+
}
|
|
1383
1386
|
/**
|
|
1384
1387
|
* TODO: [🎺]
|
|
1385
1388
|
*/
|
|
@@ -1391,13 +1394,14 @@ const $isRunningInNode = new Function(`
|
|
|
1391
1394
|
*
|
|
1392
1395
|
* @public exported from `@promptbook/utils`
|
|
1393
1396
|
*/
|
|
1394
|
-
|
|
1397
|
+
function $isRunningInBrowser() {
|
|
1395
1398
|
try {
|
|
1396
|
-
return
|
|
1397
|
-
}
|
|
1399
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
1400
|
+
}
|
|
1401
|
+
catch (e) {
|
|
1398
1402
|
return false;
|
|
1399
1403
|
}
|
|
1400
|
-
|
|
1404
|
+
}
|
|
1401
1405
|
/**
|
|
1402
1406
|
* TODO: [🎺]
|
|
1403
1407
|
*/
|
|
@@ -1409,13 +1413,15 @@ const $isRunningInBrowser = new Function(`
|
|
|
1409
1413
|
*
|
|
1410
1414
|
* @public exported from `@promptbook/utils`
|
|
1411
1415
|
*/
|
|
1412
|
-
|
|
1416
|
+
function $isRunningInJest() {
|
|
1417
|
+
var _a;
|
|
1413
1418
|
try {
|
|
1414
|
-
return process.env.JEST_WORKER_ID !== undefined;
|
|
1415
|
-
}
|
|
1419
|
+
return typeof process !== 'undefined' && ((_a = process.env) === null || _a === void 0 ? void 0 : _a.JEST_WORKER_ID) !== undefined;
|
|
1420
|
+
}
|
|
1421
|
+
catch (e) {
|
|
1416
1422
|
return false;
|
|
1417
1423
|
}
|
|
1418
|
-
|
|
1424
|
+
}
|
|
1419
1425
|
/**
|
|
1420
1426
|
* TODO: [🎺]
|
|
1421
1427
|
*/
|
|
@@ -1427,17 +1433,17 @@ const $isRunningInJest = new Function(`
|
|
|
1427
1433
|
*
|
|
1428
1434
|
* @public exported from `@promptbook/utils`
|
|
1429
1435
|
*/
|
|
1430
|
-
|
|
1436
|
+
function $isRunningInWebWorker() {
|
|
1431
1437
|
try {
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
+
// Note: Check for importScripts which is specific to workers
|
|
1439
|
+
// and not available in the main browser thread
|
|
1440
|
+
return (typeof self !== 'undefined' &&
|
|
1441
|
+
typeof self.importScripts === 'function');
|
|
1442
|
+
}
|
|
1443
|
+
catch (e) {
|
|
1438
1444
|
return false;
|
|
1439
1445
|
}
|
|
1440
|
-
|
|
1446
|
+
}
|
|
1441
1447
|
/**
|
|
1442
1448
|
* TODO: [🎺]
|
|
1443
1449
|
*/
|
|
@@ -1955,7 +1961,7 @@ function $registeredLlmToolsMessage() {
|
|
|
1955
1961
|
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
1956
1962
|
${morePieces.join('; ')}
|
|
1957
1963
|
`);
|
|
1958
|
-
if ($isRunningInNode) {
|
|
1964
|
+
if ($isRunningInNode()) {
|
|
1959
1965
|
if (isInstalled && isFullyConfigured) {
|
|
1960
1966
|
providerMessage = colors.green(providerMessage);
|
|
1961
1967
|
}
|
|
@@ -3717,6 +3723,7 @@ class RemoteLlmExecutionTools {
|
|
|
3717
3723
|
}
|
|
3718
3724
|
}
|
|
3719
3725
|
/**
|
|
3726
|
+
* TODO: !!!! Deprecate pipeline server and all of its components
|
|
3720
3727
|
* TODO: Maybe use `$exportJson`
|
|
3721
3728
|
* TODO: [🧠][🛍] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`
|
|
3722
3729
|
* TODO: [🍓] Allow to list compatible models with each variant
|
|
@@ -3726,280 +3733,1166 @@ class RemoteLlmExecutionTools {
|
|
|
3726
3733
|
*/
|
|
3727
3734
|
|
|
3728
3735
|
/**
|
|
3729
|
-
*
|
|
3736
|
+
* Normalizes a given text to camelCase format.
|
|
3730
3737
|
*
|
|
3731
|
-
*
|
|
3732
|
-
* @returns `true` if the string is a valid JSON string, false otherwise
|
|
3738
|
+
* Note: [🔂] This function is idempotent.
|
|
3733
3739
|
*
|
|
3740
|
+
* @param text The text to be normalized.
|
|
3741
|
+
* @param _isFirstLetterCapital Whether the first letter should be capitalized.
|
|
3742
|
+
* @returns The camelCase formatted string.
|
|
3743
|
+
* @example 'helloWorld'
|
|
3744
|
+
* @example 'iLovePromptbook'
|
|
3734
3745
|
* @public exported from `@promptbook/utils`
|
|
3735
3746
|
*/
|
|
3736
|
-
function
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3747
|
+
function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
|
|
3748
|
+
let charType;
|
|
3749
|
+
let lastCharType = null;
|
|
3750
|
+
let normalizedName = '';
|
|
3751
|
+
for (const char of text) {
|
|
3752
|
+
let normalizedChar;
|
|
3753
|
+
if (/^[a-z]$/.test(char)) {
|
|
3754
|
+
charType = 'LOWERCASE';
|
|
3755
|
+
normalizedChar = char;
|
|
3745
3756
|
}
|
|
3746
|
-
|
|
3757
|
+
else if (/^[A-Z]$/.test(char)) {
|
|
3758
|
+
charType = 'UPPERCASE';
|
|
3759
|
+
normalizedChar = char.toLowerCase();
|
|
3760
|
+
}
|
|
3761
|
+
else if (/^[0-9]$/.test(char)) {
|
|
3762
|
+
charType = 'NUMBER';
|
|
3763
|
+
normalizedChar = char;
|
|
3764
|
+
}
|
|
3765
|
+
else {
|
|
3766
|
+
charType = 'OTHER';
|
|
3767
|
+
normalizedChar = '';
|
|
3768
|
+
}
|
|
3769
|
+
if (!lastCharType) {
|
|
3770
|
+
if (_isFirstLetterCapital) {
|
|
3771
|
+
normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
else if (charType !== lastCharType &&
|
|
3775
|
+
!(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
|
|
3776
|
+
!(lastCharType === 'NUMBER') &&
|
|
3777
|
+
!(charType === 'NUMBER')) {
|
|
3778
|
+
normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
|
|
3779
|
+
}
|
|
3780
|
+
normalizedName += normalizedChar;
|
|
3781
|
+
lastCharType = charType;
|
|
3747
3782
|
}
|
|
3783
|
+
return normalizedName;
|
|
3748
3784
|
}
|
|
3749
|
-
|
|
3750
3785
|
/**
|
|
3751
|
-
*
|
|
3752
|
-
*
|
|
3753
|
-
* Note: [🔂] This function is idempotent.
|
|
3754
|
-
*
|
|
3755
|
-
* @public exported from `@promptbook/utils`
|
|
3786
|
+
* TODO: [🌺] Use some intermediate util splitWords
|
|
3756
3787
|
*/
|
|
3757
|
-
function capitalize(word) {
|
|
3758
|
-
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
3759
|
-
}
|
|
3760
3788
|
|
|
3761
3789
|
/**
|
|
3762
|
-
*
|
|
3790
|
+
* Creates a Mermaid graph based on the promptbook
|
|
3763
3791
|
*
|
|
3764
|
-
* Note:
|
|
3765
|
-
* - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
|
|
3766
|
-
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
3767
|
-
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
3768
|
-
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
3792
|
+
* Note: The result is not wrapped in a Markdown code block
|
|
3769
3793
|
*
|
|
3770
|
-
* @
|
|
3771
|
-
* @returns code blocks with language and content
|
|
3772
|
-
* @throws {ParseError} if block is not closed properly
|
|
3773
|
-
* @public exported from `@promptbook/markdown-utils`
|
|
3794
|
+
* @public exported from `@promptbook/utils`
|
|
3774
3795
|
*/
|
|
3775
|
-
function
|
|
3776
|
-
const
|
|
3777
|
-
const
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
} /* not else */
|
|
3786
|
-
if (currentCodeBlock.blockNotation === '>') {
|
|
3787
|
-
if (currentCodeBlock.content !== '') {
|
|
3788
|
-
currentCodeBlock.content += '\n';
|
|
3789
|
-
}
|
|
3790
|
-
currentCodeBlock.content += line.slice(2);
|
|
3791
|
-
}
|
|
3796
|
+
function renderPromptbookMermaid(pipelineJson, options) {
|
|
3797
|
+
const { linkTask = () => null } = options || {};
|
|
3798
|
+
const MERMAID_PREFIX = 'pipeline_';
|
|
3799
|
+
const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
|
|
3800
|
+
const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
|
|
3801
|
+
const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
|
|
3802
|
+
const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
|
|
3803
|
+
const parameterNameToTaskName = (parameterName) => {
|
|
3804
|
+
if (parameterName === 'knowledge') {
|
|
3805
|
+
return MERMAID_KNOWLEDGE_NAME;
|
|
3792
3806
|
}
|
|
3793
|
-
else if (
|
|
3794
|
-
|
|
3795
|
-
currentCodeBlock = null;
|
|
3807
|
+
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
3808
|
+
return MERMAID_RESERVED_NAME;
|
|
3796
3809
|
}
|
|
3797
|
-
|
|
3798
|
-
if (
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
currentCodeBlock = { blockNotation: '```', language, content: '' };
|
|
3802
|
-
}
|
|
3803
|
-
else {
|
|
3804
|
-
if (language !== null) {
|
|
3805
|
-
throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
|
|
3806
|
-
}
|
|
3807
|
-
codeBlocks.push(currentCodeBlock);
|
|
3808
|
-
currentCodeBlock = null;
|
|
3809
|
-
}
|
|
3810
|
+
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
3811
|
+
if (!parameter) {
|
|
3812
|
+
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
3813
|
+
// <- TODO: This causes problems when {knowledge} and other reserved parameters are used
|
|
3810
3814
|
}
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
currentCodeBlock.content += '\n';
|
|
3814
|
-
}
|
|
3815
|
-
currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
|
|
3815
|
+
if (parameter.isInput) {
|
|
3816
|
+
return MERMAID_INPUT_NAME;
|
|
3816
3817
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3818
|
+
const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
|
|
3819
|
+
if (!task) {
|
|
3820
|
+
throw new Error(`Could not find task for {${parameterName}}`);
|
|
3821
|
+
}
|
|
3822
|
+
return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
|
|
3823
|
+
};
|
|
3824
|
+
const inputAndIntermediateParametersMermaid = pipelineJson.tasks
|
|
3825
|
+
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
3826
|
+
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
3827
|
+
...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
|
|
3828
|
+
])
|
|
3829
|
+
.join('\n');
|
|
3830
|
+
const outputParametersMermaid = pipelineJson.parameters
|
|
3831
|
+
.filter(({ isOutput }) => isOutput)
|
|
3832
|
+
.map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
|
|
3833
|
+
.join('\n');
|
|
3834
|
+
const linksMermaid = pipelineJson.tasks
|
|
3835
|
+
.map((task) => {
|
|
3836
|
+
const link = linkTask(task);
|
|
3837
|
+
if (link === null) {
|
|
3838
|
+
return '';
|
|
3839
|
+
}
|
|
3840
|
+
const { href, title } = link;
|
|
3841
|
+
const taskName = parameterNameToTaskName(task.resultingParameterName);
|
|
3842
|
+
return `click ${taskName} href "${href}" "${title}";`;
|
|
3843
|
+
})
|
|
3844
|
+
.filter((line) => line !== '')
|
|
3845
|
+
.join('\n');
|
|
3846
|
+
const interactionPointsMermaid = Object.entries({
|
|
3847
|
+
[MERMAID_INPUT_NAME]: 'Input',
|
|
3848
|
+
[MERMAID_OUTPUT_NAME]: 'Output',
|
|
3849
|
+
[MERMAID_RESERVED_NAME]: 'Other',
|
|
3850
|
+
[MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
|
|
3851
|
+
})
|
|
3852
|
+
.filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
|
|
3853
|
+
.map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
|
|
3854
|
+
.join('\n');
|
|
3855
|
+
const promptbookMermaid = spaceTrim$1((block) => `
|
|
3856
|
+
|
|
3857
|
+
%% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
|
|
3858
|
+
|
|
3859
|
+
flowchart LR
|
|
3860
|
+
subgraph "${pipelineJson.title}"
|
|
3861
|
+
|
|
3862
|
+
%% Basic configuration
|
|
3863
|
+
direction TB
|
|
3864
|
+
|
|
3865
|
+
%% Interaction points from pipeline to outside
|
|
3866
|
+
${block(interactionPointsMermaid)}
|
|
3867
|
+
|
|
3868
|
+
%% Input and intermediate parameters
|
|
3869
|
+
${block(inputAndIntermediateParametersMermaid)}
|
|
3870
|
+
|
|
3871
|
+
|
|
3872
|
+
%% Output parameters
|
|
3873
|
+
${block(outputParametersMermaid)}
|
|
3874
|
+
|
|
3875
|
+
%% Links
|
|
3876
|
+
${block(linksMermaid)}
|
|
3877
|
+
|
|
3878
|
+
%% Styles
|
|
3879
|
+
classDef ${MERMAID_INPUT_NAME} color: grey;
|
|
3880
|
+
classDef ${MERMAID_OUTPUT_NAME} color: grey;
|
|
3881
|
+
classDef ${MERMAID_RESERVED_NAME} color: grey;
|
|
3882
|
+
classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
|
|
3883
|
+
|
|
3884
|
+
end;
|
|
3885
|
+
|
|
3886
|
+
`);
|
|
3887
|
+
return promptbookMermaid;
|
|
3822
3888
|
}
|
|
3823
3889
|
/**
|
|
3824
|
-
* TODO:
|
|
3890
|
+
* TODO: [🧠] FOREACH in mermaid graph
|
|
3891
|
+
* TODO: [🧠] Knowledge in mermaid graph
|
|
3892
|
+
* TODO: [🧠] Personas in mermaid graph
|
|
3893
|
+
* TODO: Maybe use some Mermaid package instead of string templating
|
|
3894
|
+
* TODO: [🕌] When more than 2 functionalities, split into separate functions
|
|
3825
3895
|
*/
|
|
3826
3896
|
|
|
3827
3897
|
/**
|
|
3828
|
-
*
|
|
3829
|
-
*
|
|
3830
|
-
* - When given string is a valid JSON as it is, it just returns it
|
|
3831
|
-
* - When there is no JSON code block the function throws a `ParseError`
|
|
3832
|
-
* - When there are multiple JSON code blocks the function throws a `ParseError`
|
|
3833
|
-
*
|
|
3834
|
-
* Note: It is not important if marked as ```json BUT if it is VALID JSON
|
|
3835
|
-
* Note: There are multiple similar function:
|
|
3836
|
-
* - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
|
|
3837
|
-
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
3838
|
-
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
3839
|
-
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
3898
|
+
* Serializes an error into a [🚉] JSON-serializable object
|
|
3840
3899
|
*
|
|
3841
|
-
* @public exported from `@promptbook/
|
|
3842
|
-
* @throws {ParseError} if there is no valid JSON block in the markdown
|
|
3900
|
+
* @public exported from `@promptbook/utils`
|
|
3843
3901
|
*/
|
|
3844
|
-
function
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3902
|
+
function serializeError(error) {
|
|
3903
|
+
const { name, message, stack } = error;
|
|
3904
|
+
const { id } = error;
|
|
3905
|
+
if (!Object.keys(ALL_ERRORS).includes(name)) {
|
|
3906
|
+
console.error(spaceTrim$2((block) => `
|
|
3907
|
+
|
|
3908
|
+
Cannot serialize error with name "${name}"
|
|
3909
|
+
|
|
3910
|
+
Authors of Promptbook probably forgot to add this error into the list of errors:
|
|
3911
|
+
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
3912
|
+
|
|
3913
|
+
|
|
3914
|
+
${block(stack || message)}
|
|
3915
|
+
|
|
3916
|
+
`));
|
|
3917
|
+
}
|
|
3918
|
+
return {
|
|
3919
|
+
name: name,
|
|
3920
|
+
message,
|
|
3921
|
+
stack,
|
|
3922
|
+
id, // Include id in the serialized object
|
|
3923
|
+
};
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
/**
|
|
3927
|
+
* Async version of Array.forEach
|
|
3928
|
+
*
|
|
3929
|
+
* @param array - Array to iterate over
|
|
3930
|
+
* @param options - Options for the function
|
|
3931
|
+
* @param callbackfunction - Function to call for each item
|
|
3932
|
+
* @public exported from `@promptbook/utils`
|
|
3933
|
+
* @deprecated [🪂] Use queues instead
|
|
3934
|
+
*/
|
|
3935
|
+
async function forEachAsync(array, options, callbackfunction) {
|
|
3936
|
+
const { maxParallelCount = Infinity } = options;
|
|
3937
|
+
let index = 0;
|
|
3938
|
+
let runningTasks = [];
|
|
3939
|
+
const tasks = [];
|
|
3940
|
+
for (const item of array) {
|
|
3941
|
+
const currentIndex = index++;
|
|
3942
|
+
const task = callbackfunction(item, currentIndex, array);
|
|
3943
|
+
tasks.push(task);
|
|
3944
|
+
runningTasks.push(task);
|
|
3945
|
+
/* not await */ Promise.resolve(task).then(() => {
|
|
3946
|
+
runningTasks = runningTasks.filter((t) => t !== task);
|
|
3947
|
+
});
|
|
3948
|
+
if (maxParallelCount < runningTasks.length) {
|
|
3949
|
+
await Promise.race(runningTasks);
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
await Promise.all(tasks);
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
/**
|
|
3956
|
+
* Function to check if a string is valid CSV
|
|
3957
|
+
*
|
|
3958
|
+
* @param value The string to check
|
|
3959
|
+
* @returns `true` if the string is a valid CSV string, false otherwise
|
|
3960
|
+
*
|
|
3961
|
+
* @public exported from `@promptbook/utils`
|
|
3962
|
+
*/
|
|
3963
|
+
function isValidCsvString(value) {
|
|
3964
|
+
try {
|
|
3965
|
+
// A simple check for CSV format: at least one comma and no invalid characters
|
|
3966
|
+
if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
|
|
3967
|
+
return true;
|
|
3968
|
+
}
|
|
3969
|
+
return false;
|
|
3970
|
+
}
|
|
3971
|
+
catch (error) {
|
|
3972
|
+
assertsError(error);
|
|
3973
|
+
return false;
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
/**
|
|
3978
|
+
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
3979
|
+
*
|
|
3980
|
+
* @param value The string to check
|
|
3981
|
+
* @returns `true` if the string is a valid JSON string, false otherwise
|
|
3982
|
+
*
|
|
3983
|
+
* @public exported from `@promptbook/utils`
|
|
3984
|
+
*/
|
|
3985
|
+
function isValidJsonString(value /* <- [👨⚖️] */) {
|
|
3986
|
+
try {
|
|
3987
|
+
JSON.parse(value);
|
|
3988
|
+
return true;
|
|
3989
|
+
}
|
|
3990
|
+
catch (error) {
|
|
3991
|
+
assertsError(error);
|
|
3992
|
+
if (error.message.includes('Unexpected token')) {
|
|
3993
|
+
return false;
|
|
3994
|
+
}
|
|
3995
|
+
return false;
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
/**
|
|
4000
|
+
* Function to check if a string is valid XML
|
|
4001
|
+
*
|
|
4002
|
+
* @param value
|
|
4003
|
+
* @returns `true` if the string is a valid XML string, false otherwise
|
|
4004
|
+
*
|
|
4005
|
+
* @public exported from `@promptbook/utils`
|
|
4006
|
+
*/
|
|
4007
|
+
function isValidXmlString(value) {
|
|
4008
|
+
try {
|
|
4009
|
+
const parser = new DOMParser();
|
|
4010
|
+
const parsedDocument = parser.parseFromString(value, 'application/xml');
|
|
4011
|
+
const parserError = parsedDocument.getElementsByTagName('parsererror');
|
|
4012
|
+
if (parserError.length > 0) {
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
return true;
|
|
4016
|
+
}
|
|
4017
|
+
catch (error) {
|
|
4018
|
+
assertsError(error);
|
|
4019
|
+
return false;
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
/**
|
|
4024
|
+
* Format either small or big number
|
|
4025
|
+
*
|
|
4026
|
+
* @public exported from `@promptbook/utils`
|
|
4027
|
+
*/
|
|
4028
|
+
function numberToString(value) {
|
|
4029
|
+
if (value === 0) {
|
|
4030
|
+
return '0';
|
|
4031
|
+
}
|
|
4032
|
+
else if (Number.isNaN(value)) {
|
|
4033
|
+
return VALUE_STRINGS.nan;
|
|
4034
|
+
}
|
|
4035
|
+
else if (value === Infinity) {
|
|
4036
|
+
return VALUE_STRINGS.infinity;
|
|
4037
|
+
}
|
|
4038
|
+
else if (value === -Infinity) {
|
|
4039
|
+
return VALUE_STRINGS.negativeInfinity;
|
|
4040
|
+
}
|
|
4041
|
+
for (let exponent = 0; exponent < 15; exponent++) {
|
|
4042
|
+
const factor = 10 ** exponent;
|
|
4043
|
+
const valueRounded = Math.round(value * factor) / factor;
|
|
4044
|
+
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
4045
|
+
return valueRounded.toFixed(exponent);
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
return value.toString();
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
/**
|
|
4052
|
+
* Function `valueToString` will convert the given value to string
|
|
4053
|
+
* This is useful and used in the `templateParameters` function
|
|
4054
|
+
*
|
|
4055
|
+
* Note: This function is not just calling `toString` method
|
|
4056
|
+
* It's more complex and can handle this conversion specifically for LLM models
|
|
4057
|
+
* See `VALUE_STRINGS`
|
|
4058
|
+
*
|
|
4059
|
+
* Note: There are 2 similar functions
|
|
4060
|
+
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
4061
|
+
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
4062
|
+
*
|
|
4063
|
+
* @public exported from `@promptbook/utils`
|
|
4064
|
+
*/
|
|
4065
|
+
function valueToString(value) {
|
|
4066
|
+
try {
|
|
4067
|
+
if (value === '') {
|
|
4068
|
+
return VALUE_STRINGS.empty;
|
|
4069
|
+
}
|
|
4070
|
+
else if (value === null) {
|
|
4071
|
+
return VALUE_STRINGS.null;
|
|
4072
|
+
}
|
|
4073
|
+
else if (value === undefined) {
|
|
4074
|
+
return VALUE_STRINGS.undefined;
|
|
4075
|
+
}
|
|
4076
|
+
else if (typeof value === 'string') {
|
|
4077
|
+
return value;
|
|
4078
|
+
}
|
|
4079
|
+
else if (typeof value === 'number') {
|
|
4080
|
+
return numberToString(value);
|
|
4081
|
+
}
|
|
4082
|
+
else if (value instanceof Date) {
|
|
4083
|
+
return value.toISOString();
|
|
4084
|
+
}
|
|
4085
|
+
else {
|
|
4086
|
+
try {
|
|
4087
|
+
return JSON.stringify(value);
|
|
4088
|
+
}
|
|
4089
|
+
catch (error) {
|
|
4090
|
+
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
4091
|
+
return VALUE_STRINGS.circular;
|
|
4092
|
+
}
|
|
4093
|
+
throw error;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
catch (error) {
|
|
4098
|
+
assertsError(error);
|
|
4099
|
+
console.error(error);
|
|
4100
|
+
return VALUE_STRINGS.unserializable;
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
/**
|
|
4105
|
+
* Replaces parameters in template with values from parameters object
|
|
4106
|
+
*
|
|
4107
|
+
* Note: This function is not places strings into string,
|
|
4108
|
+
* It's more complex and can handle this operation specifically for LLM models
|
|
4109
|
+
*
|
|
4110
|
+
* @param template the template with parameters in {curly} braces
|
|
4111
|
+
* @param parameters the object with parameters
|
|
4112
|
+
* @returns the template with replaced parameters
|
|
4113
|
+
* @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
|
|
4114
|
+
* @public exported from `@promptbook/utils`
|
|
4115
|
+
*/
|
|
4116
|
+
function templateParameters(template, parameters) {
|
|
4117
|
+
for (const [parameterName, parameterValue] of Object.entries(parameters)) {
|
|
4118
|
+
if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
|
|
4119
|
+
throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
|
|
4120
|
+
}
|
|
4121
|
+
else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
|
|
4122
|
+
// TODO: [🍵]
|
|
4123
|
+
throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
let replacedTemplates = template;
|
|
4127
|
+
let match;
|
|
4128
|
+
let loopLimit = LOOP_LIMIT;
|
|
4129
|
+
while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
|
|
4130
|
+
.exec(replacedTemplates))) {
|
|
4131
|
+
if (loopLimit-- < 0) {
|
|
4132
|
+
throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
|
|
4133
|
+
}
|
|
4134
|
+
const precol = match.groups.precol;
|
|
4135
|
+
const parameterName = match.groups.parameterName;
|
|
4136
|
+
if (parameterName === '') {
|
|
4137
|
+
// Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
|
|
4138
|
+
continue;
|
|
4139
|
+
}
|
|
4140
|
+
if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
|
|
4141
|
+
throw new PipelineExecutionError('Parameter is already opened or not closed');
|
|
4142
|
+
}
|
|
4143
|
+
if (parameters[parameterName] === undefined) {
|
|
4144
|
+
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
4145
|
+
}
|
|
4146
|
+
let parameterValue = parameters[parameterName];
|
|
4147
|
+
if (parameterValue === undefined) {
|
|
4148
|
+
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
4149
|
+
}
|
|
4150
|
+
parameterValue = valueToString(parameterValue);
|
|
4151
|
+
// Escape curly braces in parameter values to prevent prompt-injection
|
|
4152
|
+
parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
|
|
4153
|
+
if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
|
|
4154
|
+
parameterValue = parameterValue
|
|
4155
|
+
.split('\n')
|
|
4156
|
+
.map((line, index) => (index === 0 ? line : `${precol}${line}`))
|
|
4157
|
+
.join('\n');
|
|
4158
|
+
}
|
|
4159
|
+
replacedTemplates =
|
|
4160
|
+
replacedTemplates.substring(0, match.index + precol.length) +
|
|
4161
|
+
parameterValue +
|
|
4162
|
+
replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
|
|
4163
|
+
}
|
|
4164
|
+
// [💫] Check if there are parameters that are not closed properly
|
|
4165
|
+
if (/{\w+$/.test(replacedTemplates)) {
|
|
4166
|
+
throw new PipelineExecutionError('Parameter is not closed');
|
|
4167
|
+
}
|
|
4168
|
+
// [💫] Check if there are parameters that are not opened properly
|
|
4169
|
+
if (/^\w+}/.test(replacedTemplates)) {
|
|
4170
|
+
throw new PipelineExecutionError('Parameter is not opened');
|
|
4171
|
+
}
|
|
4172
|
+
return replacedTemplates;
|
|
4173
|
+
}
|
|
4174
|
+
|
|
4175
|
+
/**
|
|
4176
|
+
* Number of characters per standard line with 11pt Arial font size.
|
|
4177
|
+
*
|
|
4178
|
+
* @public exported from `@promptbook/utils`
|
|
4179
|
+
*/
|
|
4180
|
+
const CHARACTERS_PER_STANDARD_LINE = 63;
|
|
4181
|
+
/**
|
|
4182
|
+
* Number of lines per standard A4 page with 11pt Arial font size and standard margins and spacing.
|
|
4183
|
+
*
|
|
4184
|
+
* @public exported from `@promptbook/utils`
|
|
4185
|
+
*/
|
|
4186
|
+
const LINES_PER_STANDARD_PAGE = 44;
|
|
4187
|
+
/**
|
|
4188
|
+
* TODO: [🧠] Should be this `constants.ts` or `config.ts`?
|
|
4189
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4190
|
+
*/
|
|
4191
|
+
|
|
4192
|
+
/**
|
|
4193
|
+
* Counts number of characters in the text
|
|
4194
|
+
*
|
|
4195
|
+
* @public exported from `@promptbook/utils`
|
|
4196
|
+
*/
|
|
4197
|
+
function countCharacters(text) {
|
|
4198
|
+
// Remove null characters
|
|
4199
|
+
text = text.replace(/\0/g, '');
|
|
4200
|
+
// Replace emojis (and also ZWJ sequence) with hyphens
|
|
4201
|
+
text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
|
|
4202
|
+
text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
|
|
4203
|
+
text = text.replace(/\p{Extended_Pictographic}(\u{200D}\p{Extended_Pictographic})*/gu, '-');
|
|
4204
|
+
return text.length;
|
|
4205
|
+
}
|
|
4206
|
+
/**
|
|
4207
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4208
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4209
|
+
*/
|
|
4210
|
+
|
|
4211
|
+
/**
|
|
4212
|
+
* Counts number of lines in the text
|
|
4213
|
+
*
|
|
4214
|
+
* Note: This does not check only for the presence of newlines, but also for the length of the standard line.
|
|
4215
|
+
*
|
|
4216
|
+
* @public exported from `@promptbook/utils`
|
|
4217
|
+
*/
|
|
4218
|
+
function countLines(text) {
|
|
4219
|
+
if (text === '') {
|
|
4220
|
+
return 0;
|
|
4221
|
+
}
|
|
4222
|
+
text = text.replace('\r\n', '\n');
|
|
4223
|
+
text = text.replace('\r', '\n');
|
|
4224
|
+
const lines = text.split('\n');
|
|
4225
|
+
return lines.reduce((count, line) => count + Math.max(Math.ceil(line.length / CHARACTERS_PER_STANDARD_LINE), 1), 0);
|
|
4226
|
+
}
|
|
4227
|
+
/**
|
|
4228
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4229
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4230
|
+
*/
|
|
4231
|
+
|
|
4232
|
+
/**
|
|
4233
|
+
* Counts number of pages in the text
|
|
4234
|
+
*
|
|
4235
|
+
* 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.
|
|
4236
|
+
*
|
|
4237
|
+
* @public exported from `@promptbook/utils`
|
|
4238
|
+
*/
|
|
4239
|
+
function countPages(text) {
|
|
4240
|
+
return Math.ceil(countLines(text) / LINES_PER_STANDARD_PAGE);
|
|
4241
|
+
}
|
|
4242
|
+
/**
|
|
4243
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4244
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4245
|
+
*/
|
|
4246
|
+
|
|
4247
|
+
/**
|
|
4248
|
+
* Counts number of paragraphs in the text
|
|
4249
|
+
*
|
|
4250
|
+
* @public exported from `@promptbook/utils`
|
|
4251
|
+
*/
|
|
4252
|
+
function countParagraphs(text) {
|
|
4253
|
+
return text.split(/\n\s*\n/).filter((paragraph) => paragraph.trim() !== '').length;
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4257
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4258
|
+
*/
|
|
4259
|
+
|
|
4260
|
+
/**
|
|
4261
|
+
* Split text into sentences
|
|
4262
|
+
*
|
|
4263
|
+
* @public exported from `@promptbook/utils`
|
|
4264
|
+
*/
|
|
4265
|
+
function splitIntoSentences(text) {
|
|
4266
|
+
return text.split(/[.!?]+/).filter((sentence) => sentence.trim() !== '');
|
|
4267
|
+
}
|
|
4268
|
+
/**
|
|
4269
|
+
* Counts number of sentences in the text
|
|
4270
|
+
*
|
|
4271
|
+
* @public exported from `@promptbook/utils`
|
|
4272
|
+
*/
|
|
4273
|
+
function countSentences(text) {
|
|
4274
|
+
return splitIntoSentences(text).length;
|
|
4275
|
+
}
|
|
4276
|
+
/**
|
|
4277
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4278
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4279
|
+
*/
|
|
4280
|
+
|
|
4281
|
+
/**
|
|
4282
|
+
* Counts number of words in the text
|
|
4283
|
+
*
|
|
4284
|
+
* @public exported from `@promptbook/utils`
|
|
4285
|
+
*/
|
|
4286
|
+
function countWords(text) {
|
|
4287
|
+
text = text.replace(/[\p{Extended_Pictographic}]/gu, 'a');
|
|
4288
|
+
text = removeDiacritics(text);
|
|
4289
|
+
// Add spaces before uppercase letters preceded by lowercase letters (for camelCase)
|
|
4290
|
+
text = text.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
4291
|
+
return text.split(/[^a-zа-я0-9]+/i).filter((word) => word.length > 0).length;
|
|
4292
|
+
}
|
|
4293
|
+
/**
|
|
4294
|
+
* TODO: [🥴] Implement counting in formats - like JSON, CSV, XML,...
|
|
4295
|
+
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4296
|
+
* TODO: [✌️] `countWords` should be just `splitWords(...).length`, and all other counters should use this pattern as well
|
|
4297
|
+
*/
|
|
4298
|
+
|
|
4299
|
+
/**
|
|
4300
|
+
* Index of all counter functions
|
|
4301
|
+
*
|
|
4302
|
+
* @public exported from `@promptbook/utils`
|
|
4303
|
+
*/
|
|
4304
|
+
const CountUtils = {
|
|
4305
|
+
CHARACTERS: countCharacters,
|
|
4306
|
+
WORDS: countWords,
|
|
4307
|
+
SENTENCES: countSentences,
|
|
4308
|
+
PARAGRAPHS: countParagraphs,
|
|
4309
|
+
LINES: countLines,
|
|
4310
|
+
PAGES: countPages,
|
|
4311
|
+
};
|
|
4312
|
+
/**
|
|
4313
|
+
* TODO: [🧠][🤠] This should be probably as part of `TextFormatParser`
|
|
4314
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4315
|
+
*/
|
|
4316
|
+
|
|
4317
|
+
/**
|
|
4318
|
+
* Simple wrapper `new Date().toISOString()`
|
|
4319
|
+
*
|
|
4320
|
+
* Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
|
|
4321
|
+
*
|
|
4322
|
+
* @returns string_date branded type
|
|
4323
|
+
* @public exported from `@promptbook/utils`
|
|
4324
|
+
*/
|
|
4325
|
+
function $getCurrentDate() {
|
|
4326
|
+
return new Date().toISOString();
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
/**
|
|
4330
|
+
* Computes SHA-256 hash of the given object
|
|
4331
|
+
*
|
|
4332
|
+
* @public exported from `@promptbook/utils`
|
|
4333
|
+
*/
|
|
4334
|
+
function computeHash(value) {
|
|
4335
|
+
return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
|
|
4336
|
+
}
|
|
4337
|
+
/**
|
|
4338
|
+
* TODO: [🥬][🥬] Use this ACRY
|
|
4339
|
+
*/
|
|
4340
|
+
|
|
4341
|
+
/**
|
|
4342
|
+
* Function parseNumber will parse number from string
|
|
4343
|
+
*
|
|
4344
|
+
* Note: [🔂] This function is idempotent.
|
|
4345
|
+
* Unlike Number.parseInt, Number.parseFloat it will never ever result in NaN
|
|
4346
|
+
* Note: it also works only with decimal numbers
|
|
4347
|
+
*
|
|
4348
|
+
* @returns parsed number
|
|
4349
|
+
* @throws {ParseError} if the value is not a number
|
|
4350
|
+
*
|
|
4351
|
+
* @public exported from `@promptbook/utils`
|
|
4352
|
+
*/
|
|
4353
|
+
function parseNumber(value) {
|
|
4354
|
+
const originalValue = value;
|
|
4355
|
+
if (typeof value === 'number') {
|
|
4356
|
+
value = value.toString(); // <- TODO: Maybe more efficient way to do this
|
|
4357
|
+
}
|
|
4358
|
+
if (typeof value !== 'string') {
|
|
4359
|
+
return 0;
|
|
4360
|
+
}
|
|
4361
|
+
value = value.trim();
|
|
4362
|
+
if (value.startsWith('+')) {
|
|
4363
|
+
return parseNumber(value.substring(1));
|
|
4364
|
+
}
|
|
4365
|
+
if (value.startsWith('-')) {
|
|
4366
|
+
const number = parseNumber(value.substring(1));
|
|
4367
|
+
if (number === 0) {
|
|
4368
|
+
return 0; // <- Note: To prevent -0
|
|
4369
|
+
}
|
|
4370
|
+
return -number;
|
|
4371
|
+
}
|
|
4372
|
+
value = value.replace(/,/g, '.');
|
|
4373
|
+
value = value.toUpperCase();
|
|
4374
|
+
if (value === '') {
|
|
4375
|
+
return 0;
|
|
4376
|
+
}
|
|
4377
|
+
if (value === '♾' || value.startsWith('INF')) {
|
|
4378
|
+
return Infinity;
|
|
4379
|
+
}
|
|
4380
|
+
if (value.includes('/')) {
|
|
4381
|
+
const [numerator_, denominator_] = value.split('/');
|
|
4382
|
+
const numerator = parseNumber(numerator_);
|
|
4383
|
+
const denominator = parseNumber(denominator_);
|
|
4384
|
+
if (denominator === 0) {
|
|
4385
|
+
throw new ParseError(`Unable to parse number from "${originalValue}" because denominator is zero`);
|
|
4386
|
+
}
|
|
4387
|
+
return numerator / denominator;
|
|
4388
|
+
}
|
|
4389
|
+
if (/^(NAN|NULL|NONE|UNDEFINED|ZERO|NO.*)$/.test(value)) {
|
|
4390
|
+
return 0;
|
|
4391
|
+
}
|
|
4392
|
+
if (value.includes('E')) {
|
|
4393
|
+
const [significand, exponent] = value.split('E');
|
|
4394
|
+
return parseNumber(significand) * 10 ** parseNumber(exponent);
|
|
4395
|
+
}
|
|
4396
|
+
if (!/^[0-9.]+$/.test(value) || value.split('.').length > 2) {
|
|
4397
|
+
throw new ParseError(`Unable to parse number from "${originalValue}"`);
|
|
4398
|
+
}
|
|
4399
|
+
const num = parseFloat(value);
|
|
4400
|
+
if (isNaN(num)) {
|
|
4401
|
+
throw new ParseError(`Unexpected NaN when parsing number from "${originalValue}"`);
|
|
4402
|
+
}
|
|
4403
|
+
return num;
|
|
4404
|
+
}
|
|
4405
|
+
/**
|
|
4406
|
+
* TODO: Maybe use sth. like safe-eval in fraction/calculation case @see https://www.npmjs.com/package/safe-eval
|
|
4407
|
+
* TODO: [🧠][🌻] Maybe export through `@promptbook/markdown-utils` not `@promptbook/utils`
|
|
4408
|
+
*/
|
|
4409
|
+
|
|
4410
|
+
/**
|
|
4411
|
+
* Makes first letter of a string uppercase
|
|
4412
|
+
*
|
|
4413
|
+
* Note: [🔂] This function is idempotent.
|
|
4414
|
+
*
|
|
4415
|
+
* @public exported from `@promptbook/utils`
|
|
4416
|
+
*/
|
|
4417
|
+
function capitalize(word) {
|
|
4418
|
+
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
4419
|
+
}
|
|
4420
|
+
|
|
4421
|
+
/**
|
|
4422
|
+
* Makes first letter of a string lowercase
|
|
4423
|
+
*
|
|
4424
|
+
* Note: [🔂] This function is idempotent.
|
|
4425
|
+
*
|
|
4426
|
+
* @public exported from `@promptbook/utils`
|
|
4427
|
+
*/
|
|
4428
|
+
function decapitalize(word) {
|
|
4429
|
+
return word.substring(0, 1).toLowerCase() + word.substring(1);
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
/**
|
|
4433
|
+
* Parses keywords from a string
|
|
4434
|
+
*
|
|
4435
|
+
* @param {string} input
|
|
4436
|
+
* @returns {Set} of keywords without diacritics in lowercase
|
|
4437
|
+
* @public exported from `@promptbook/utils`
|
|
4438
|
+
*/
|
|
4439
|
+
function parseKeywordsFromString(input) {
|
|
4440
|
+
const keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
|
|
4441
|
+
.toLowerCase()
|
|
4442
|
+
.split(/[^a-z0-9]+/gs)
|
|
4443
|
+
.filter((value) => value);
|
|
4444
|
+
return new Set(keywords);
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4447
|
+
/**
|
|
4448
|
+
* Converts a name string into a URI-compatible format.
|
|
4449
|
+
*
|
|
4450
|
+
* @param name The string to be converted to a URI-compatible format.
|
|
4451
|
+
* @returns A URI-compatible string derived from the input name.
|
|
4452
|
+
* @example 'Hello World' -> 'hello-world'
|
|
4453
|
+
* @public exported from `@promptbook/utils`
|
|
4454
|
+
*/
|
|
4455
|
+
function nameToUriPart(name) {
|
|
4456
|
+
let uriPart = name;
|
|
4457
|
+
uriPart = uriPart.toLowerCase();
|
|
4458
|
+
uriPart = removeDiacritics(uriPart);
|
|
4459
|
+
uriPart = uriPart.replace(/[^a-zA-Z0-9]+/g, '-');
|
|
4460
|
+
uriPart = uriPart.replace(/^-+/, '');
|
|
4461
|
+
uriPart = uriPart.replace(/-+$/, '');
|
|
4462
|
+
return uriPart;
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
/**
|
|
4466
|
+
* Converts a given name into URI-compatible parts.
|
|
4467
|
+
*
|
|
4468
|
+
* @param name The name to be converted into URI parts.
|
|
4469
|
+
* @returns An array of URI-compatible parts derived from the name.
|
|
4470
|
+
* @example 'Example Name' -> ['example', 'name']
|
|
4471
|
+
* @public exported from `@promptbook/utils`
|
|
4472
|
+
*/
|
|
4473
|
+
function nameToUriParts(name) {
|
|
4474
|
+
return nameToUriPart(name)
|
|
4475
|
+
.split('-')
|
|
4476
|
+
.filter((value) => value !== '');
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
/**
|
|
4480
|
+
* Normalizes a given text to PascalCase format.
|
|
4481
|
+
*
|
|
4482
|
+
* Note: [🔂] This function is idempotent.
|
|
4483
|
+
*
|
|
4484
|
+
* @param text @public exported from `@promptbook/utils`
|
|
4485
|
+
* @returns
|
|
4486
|
+
* @example 'HelloWorld'
|
|
4487
|
+
* @example 'ILovePromptbook'
|
|
4488
|
+
* @public exported from `@promptbook/utils`
|
|
4489
|
+
*/
|
|
4490
|
+
function normalizeTo_PascalCase(text) {
|
|
4491
|
+
return normalizeTo_camelCase(text, true);
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4494
|
+
/**
|
|
4495
|
+
* Take every whitespace (space, new line, tab) and replace it with a single space
|
|
4496
|
+
*
|
|
4497
|
+
* Note: [🔂] This function is idempotent.
|
|
4498
|
+
*
|
|
4499
|
+
* @public exported from `@promptbook/utils`
|
|
4500
|
+
*/
|
|
4501
|
+
function normalizeWhitespaces(sentence) {
|
|
4502
|
+
return sentence.replace(/\s+/gs, ' ').trim();
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
/**
|
|
4506
|
+
* Removes quotes from a string
|
|
4507
|
+
*
|
|
4508
|
+
* Note: [🔂] This function is idempotent.
|
|
4509
|
+
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
4510
|
+
* Note: This function removes only the same quotes from the beginning and the end of the string
|
|
4511
|
+
* Note: There are two similar functions:
|
|
4512
|
+
* - `removeQuotes` which removes only bounding quotes
|
|
4513
|
+
* - `unwrapResult` which removes whole introduce sentence
|
|
4514
|
+
*
|
|
4515
|
+
* @param text optionally quoted text
|
|
4516
|
+
* @returns text without quotes
|
|
4517
|
+
* @public exported from `@promptbook/utils`
|
|
4518
|
+
*/
|
|
4519
|
+
function removeQuotes(text) {
|
|
4520
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
4521
|
+
return text.slice(1, -1);
|
|
3852
4522
|
}
|
|
3853
|
-
if (
|
|
3854
|
-
|
|
4523
|
+
if (text.startsWith("'") && text.endsWith("'")) {
|
|
4524
|
+
return text.slice(1, -1);
|
|
3855
4525
|
}
|
|
3856
|
-
return
|
|
4526
|
+
return text;
|
|
3857
4527
|
}
|
|
4528
|
+
|
|
3858
4529
|
/**
|
|
3859
|
-
*
|
|
3860
|
-
*
|
|
4530
|
+
* Adds suffix to the URL
|
|
4531
|
+
*
|
|
4532
|
+
* @public exported from `@promptbook/utils`
|
|
3861
4533
|
*/
|
|
4534
|
+
function suffixUrl(value, suffix) {
|
|
4535
|
+
const baseUrl = value.href.endsWith('/') ? value.href.slice(0, -1) : value.href;
|
|
4536
|
+
const normalizedSuffix = suffix.replace(/\/+/g, '/');
|
|
4537
|
+
return (baseUrl + normalizedSuffix);
|
|
4538
|
+
}
|
|
3862
4539
|
|
|
3863
4540
|
/**
|
|
3864
|
-
*
|
|
4541
|
+
* Removes quotes and optional introduce text from a string
|
|
4542
|
+
*
|
|
4543
|
+
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
4544
|
+
* Note: This function trims the text and removes whole introduce sentence if it is present
|
|
4545
|
+
* Note: There are two similar functions:
|
|
4546
|
+
* - `removeQuotes` which removes only bounding quotes
|
|
4547
|
+
* - `unwrapResult` which removes whole introduce sentence
|
|
3865
4548
|
*
|
|
4549
|
+
* @param text optionally quoted text
|
|
4550
|
+
* @returns text without quotes
|
|
3866
4551
|
* @public exported from `@promptbook/utils`
|
|
3867
4552
|
*/
|
|
3868
|
-
function
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
//
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4553
|
+
function unwrapResult(text, options) {
|
|
4554
|
+
const { isTrimmed = true, isIntroduceSentenceRemoved = true } = options || {};
|
|
4555
|
+
let trimmedText = text;
|
|
4556
|
+
// Remove leading and trailing spaces and newlines
|
|
4557
|
+
if (isTrimmed) {
|
|
4558
|
+
trimmedText = spaceTrim$1(trimmedText);
|
|
4559
|
+
}
|
|
4560
|
+
let processedText = trimmedText;
|
|
4561
|
+
if (isIntroduceSentenceRemoved) {
|
|
4562
|
+
const introduceSentenceRegex = /^[a-zěščřžýáíéúů:\s]*:\s*/i;
|
|
4563
|
+
if (introduceSentenceRegex.test(text)) {
|
|
4564
|
+
// Remove the introduce sentence and quotes by replacing it with an empty string
|
|
4565
|
+
processedText = processedText.replace(introduceSentenceRegex, '');
|
|
4566
|
+
}
|
|
4567
|
+
processedText = spaceTrim$1(processedText);
|
|
4568
|
+
}
|
|
4569
|
+
if (processedText.length < 3) {
|
|
4570
|
+
return trimmedText;
|
|
4571
|
+
}
|
|
4572
|
+
if (processedText.includes('\n')) {
|
|
4573
|
+
return trimmedText;
|
|
4574
|
+
}
|
|
4575
|
+
// Remove the quotes by extracting the substring without the first and last characters
|
|
4576
|
+
const unquotedText = processedText.slice(1, -1);
|
|
4577
|
+
// Check if the text starts and ends with quotes
|
|
4578
|
+
if ([
|
|
4579
|
+
['"', '"'],
|
|
4580
|
+
["'", "'"],
|
|
4581
|
+
['`', '`'],
|
|
4582
|
+
['*', '*'],
|
|
4583
|
+
['_', '_'],
|
|
4584
|
+
['„', '“'],
|
|
4585
|
+
['«', '»'] /* <- QUOTES to config */,
|
|
4586
|
+
].some(([startQuote, endQuote]) => {
|
|
4587
|
+
if (!processedText.startsWith(startQuote)) {
|
|
4588
|
+
return false;
|
|
4589
|
+
}
|
|
4590
|
+
if (!processedText.endsWith(endQuote)) {
|
|
4591
|
+
return false;
|
|
4592
|
+
}
|
|
4593
|
+
if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
|
|
4594
|
+
return false;
|
|
4595
|
+
}
|
|
4596
|
+
if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
|
|
4597
|
+
return false;
|
|
4598
|
+
}
|
|
4599
|
+
return true;
|
|
4600
|
+
})) {
|
|
4601
|
+
return unwrapResult(unquotedText, { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
4602
|
+
}
|
|
4603
|
+
else {
|
|
4604
|
+
return processedText;
|
|
4605
|
+
}
|
|
3876
4606
|
}
|
|
3877
4607
|
/**
|
|
3878
|
-
* TODO: [
|
|
3879
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4608
|
+
* TODO: [🧠] Should this also unwrap the (parenthesis)
|
|
3880
4609
|
*/
|
|
3881
4610
|
|
|
3882
4611
|
/**
|
|
3883
|
-
*
|
|
4612
|
+
* Parses the task and returns the list of all parameter names
|
|
3884
4613
|
*
|
|
4614
|
+
* @param template the string template with parameters in {curly} braces
|
|
4615
|
+
* @returns the list of parameter names
|
|
3885
4616
|
* @public exported from `@promptbook/utils`
|
|
3886
4617
|
*/
|
|
3887
|
-
|
|
4618
|
+
function extractParameterNames(template) {
|
|
4619
|
+
const matches = template.matchAll(/{\w+}/g);
|
|
4620
|
+
const parameterNames = new Set();
|
|
4621
|
+
for (const match of matches) {
|
|
4622
|
+
const parameterName = match[0].slice(1, -1);
|
|
4623
|
+
parameterNames.add(parameterName);
|
|
4624
|
+
}
|
|
4625
|
+
return parameterNames;
|
|
4626
|
+
}
|
|
4627
|
+
|
|
3888
4628
|
/**
|
|
3889
|
-
*
|
|
3890
|
-
|
|
4629
|
+
* Recursively converts JSON strings to JSON objects
|
|
4630
|
+
|
|
3891
4631
|
* @public exported from `@promptbook/utils`
|
|
3892
4632
|
*/
|
|
3893
|
-
|
|
4633
|
+
function jsonStringsToJsons(object) {
|
|
4634
|
+
if (object === null) {
|
|
4635
|
+
return object;
|
|
4636
|
+
}
|
|
4637
|
+
if (Array.isArray(object)) {
|
|
4638
|
+
return object.map(jsonStringsToJsons);
|
|
4639
|
+
}
|
|
4640
|
+
if (typeof object !== 'object') {
|
|
4641
|
+
return object;
|
|
4642
|
+
}
|
|
4643
|
+
const newObject = { ...object };
|
|
4644
|
+
for (const [key, value] of Object.entries(object)) {
|
|
4645
|
+
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
4646
|
+
newObject[key] = jsonParse(value);
|
|
4647
|
+
}
|
|
4648
|
+
else {
|
|
4649
|
+
newObject[key] = jsonStringsToJsons(value);
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
return newObject;
|
|
4653
|
+
}
|
|
3894
4654
|
/**
|
|
3895
|
-
* TODO:
|
|
3896
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4655
|
+
* TODO: Type the return type correctly
|
|
3897
4656
|
*/
|
|
3898
4657
|
|
|
3899
4658
|
/**
|
|
3900
|
-
*
|
|
3901
|
-
*
|
|
3902
|
-
* Note: This does not check only for the presence of newlines, but also for the length of the standard line.
|
|
4659
|
+
* Create difference set of two sets.
|
|
3903
4660
|
*
|
|
4661
|
+
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
3904
4662
|
* @public exported from `@promptbook/utils`
|
|
3905
4663
|
*/
|
|
3906
|
-
function
|
|
3907
|
-
|
|
3908
|
-
|
|
4664
|
+
function difference(a, b, isEqual = (a, b) => a === b) {
|
|
4665
|
+
const diff = new Set();
|
|
4666
|
+
for (const itemA of Array.from(a)) {
|
|
4667
|
+
if (!Array.from(b).some((itemB) => isEqual(itemA, itemB))) {
|
|
4668
|
+
diff.add(itemA);
|
|
4669
|
+
}
|
|
3909
4670
|
}
|
|
3910
|
-
|
|
3911
|
-
text = text.replace('\r', '\n');
|
|
3912
|
-
const lines = text.split('\n');
|
|
3913
|
-
return lines.reduce((count, line) => count + Math.max(Math.ceil(line.length / CHARACTERS_PER_STANDARD_LINE), 1), 0);
|
|
4671
|
+
return diff;
|
|
3914
4672
|
}
|
|
3915
4673
|
/**
|
|
3916
|
-
* TODO: [
|
|
3917
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4674
|
+
* TODO: [🧠][💯] Maybe also implement symmetricDifference
|
|
3918
4675
|
*/
|
|
3919
4676
|
|
|
3920
4677
|
/**
|
|
3921
|
-
*
|
|
3922
|
-
*
|
|
3923
|
-
* 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.
|
|
4678
|
+
* Creates a new set with all elements that are present in either set
|
|
3924
4679
|
*
|
|
4680
|
+
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
3925
4681
|
* @public exported from `@promptbook/utils`
|
|
3926
4682
|
*/
|
|
3927
|
-
function
|
|
3928
|
-
|
|
4683
|
+
function union(...sets) {
|
|
4684
|
+
const union = new Set();
|
|
4685
|
+
for (const set of sets) {
|
|
4686
|
+
for (const item of Array.from(set)) {
|
|
4687
|
+
union.add(item);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
return union;
|
|
3929
4691
|
}
|
|
4692
|
+
|
|
3930
4693
|
/**
|
|
3931
|
-
*
|
|
3932
|
-
*
|
|
4694
|
+
* Checks if value is valid email
|
|
4695
|
+
*
|
|
4696
|
+
* @public exported from `@promptbook/utils`
|
|
3933
4697
|
*/
|
|
4698
|
+
function isValidEmail(email) {
|
|
4699
|
+
if (typeof email !== 'string') {
|
|
4700
|
+
return false;
|
|
4701
|
+
}
|
|
4702
|
+
if (email.split('\n').length > 1) {
|
|
4703
|
+
return false;
|
|
4704
|
+
}
|
|
4705
|
+
return /^.+@.+\..+$/.test(email);
|
|
4706
|
+
}
|
|
3934
4707
|
|
|
3935
4708
|
/**
|
|
3936
|
-
*
|
|
4709
|
+
* Checks if the given value is a valid JavaScript identifier name.
|
|
3937
4710
|
*
|
|
4711
|
+
* @param javascriptName The value to check for JavaScript identifier validity.
|
|
4712
|
+
* @returns `true` if the value is a valid JavaScript name, false otherwise.
|
|
3938
4713
|
* @public exported from `@promptbook/utils`
|
|
3939
4714
|
*/
|
|
3940
|
-
function
|
|
3941
|
-
|
|
4715
|
+
function isValidJavascriptName(javascriptName) {
|
|
4716
|
+
if (typeof javascriptName !== 'string') {
|
|
4717
|
+
return false;
|
|
4718
|
+
}
|
|
4719
|
+
return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/i.test(javascriptName);
|
|
3942
4720
|
}
|
|
4721
|
+
|
|
3943
4722
|
/**
|
|
3944
|
-
*
|
|
3945
|
-
*
|
|
4723
|
+
* Tests if given string is valid semantic version
|
|
4724
|
+
*
|
|
4725
|
+
* Note: There are two similar functions:
|
|
4726
|
+
* - `isValidSemanticVersion` which tests any semantic version
|
|
4727
|
+
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
4728
|
+
*
|
|
4729
|
+
* @public exported from `@promptbook/utils`
|
|
3946
4730
|
*/
|
|
4731
|
+
function isValidSemanticVersion(version) {
|
|
4732
|
+
if (typeof version !== 'string') {
|
|
4733
|
+
return false;
|
|
4734
|
+
}
|
|
4735
|
+
if (version.startsWith('0.0.0')) {
|
|
4736
|
+
return false;
|
|
4737
|
+
}
|
|
4738
|
+
return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
|
|
4739
|
+
}
|
|
3947
4740
|
|
|
3948
4741
|
/**
|
|
3949
|
-
*
|
|
4742
|
+
* Tests if given string is valid promptbook version
|
|
4743
|
+
* It looks into list of known promptbook versions.
|
|
4744
|
+
*
|
|
4745
|
+
* @see https://www.npmjs.com/package/promptbook?activeTab=versions
|
|
4746
|
+
* 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.
|
|
4747
|
+
* Note: There are two similar functions:
|
|
4748
|
+
* - `isValidSemanticVersion` which tests any semantic version
|
|
4749
|
+
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
3950
4750
|
*
|
|
3951
4751
|
* @public exported from `@promptbook/utils`
|
|
3952
4752
|
*/
|
|
3953
|
-
function
|
|
3954
|
-
|
|
4753
|
+
function isValidPromptbookVersion(version) {
|
|
4754
|
+
if (!isValidSemanticVersion(version)) {
|
|
4755
|
+
return false;
|
|
4756
|
+
}
|
|
4757
|
+
if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
|
|
4758
|
+
return false;
|
|
4759
|
+
}
|
|
4760
|
+
// <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
|
|
4761
|
+
return true;
|
|
3955
4762
|
}
|
|
4763
|
+
|
|
3956
4764
|
/**
|
|
3957
|
-
*
|
|
4765
|
+
* Tests if given string is valid pipeline URL URL.
|
|
4766
|
+
*
|
|
4767
|
+
* Note: There are two similar functions:
|
|
4768
|
+
* - `isValidUrl` which tests any URL
|
|
4769
|
+
* - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
|
|
3958
4770
|
*
|
|
3959
4771
|
* @public exported from `@promptbook/utils`
|
|
3960
4772
|
*/
|
|
3961
|
-
function
|
|
3962
|
-
|
|
4773
|
+
function isValidPipelineUrl(url) {
|
|
4774
|
+
if (!isValidUrl(url)) {
|
|
4775
|
+
return false;
|
|
4776
|
+
}
|
|
4777
|
+
if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
|
|
4778
|
+
return false;
|
|
4779
|
+
}
|
|
4780
|
+
if (url.includes('#')) {
|
|
4781
|
+
// TODO: [🐠]
|
|
4782
|
+
return false;
|
|
4783
|
+
}
|
|
4784
|
+
/*
|
|
4785
|
+
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
4786
|
+
if (isUrlOnPrivateNetwork(url)) {
|
|
4787
|
+
return false;
|
|
4788
|
+
}
|
|
4789
|
+
*/
|
|
4790
|
+
return true;
|
|
3963
4791
|
}
|
|
3964
4792
|
/**
|
|
3965
|
-
* TODO: [
|
|
3966
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
4793
|
+
* TODO: [🐠] Maybe more info why the URL is invalid
|
|
3967
4794
|
*/
|
|
3968
4795
|
|
|
3969
4796
|
/**
|
|
3970
|
-
*
|
|
4797
|
+
* Extracts all code blocks from markdown.
|
|
3971
4798
|
*
|
|
3972
|
-
*
|
|
4799
|
+
* Note: There are multiple similar functions:
|
|
4800
|
+
* - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
|
|
4801
|
+
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
4802
|
+
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
4803
|
+
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
4804
|
+
*
|
|
4805
|
+
* @param markdown any valid markdown
|
|
4806
|
+
* @returns code blocks with language and content
|
|
4807
|
+
* @throws {ParseError} if block is not closed properly
|
|
4808
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
3973
4809
|
*/
|
|
3974
|
-
function
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
//
|
|
3978
|
-
|
|
3979
|
-
|
|
4810
|
+
function extractAllBlocksFromMarkdown(markdown) {
|
|
4811
|
+
const codeBlocks = [];
|
|
4812
|
+
const lines = markdown.split('\n');
|
|
4813
|
+
// Note: [0] Ensure that the last block notated by gt > will be closed
|
|
4814
|
+
lines.push('');
|
|
4815
|
+
let currentCodeBlock = null;
|
|
4816
|
+
for (const line of lines) {
|
|
4817
|
+
if (line.startsWith('> ') || line === '>') {
|
|
4818
|
+
if (currentCodeBlock === null) {
|
|
4819
|
+
currentCodeBlock = { blockNotation: '>', language: null, content: '' };
|
|
4820
|
+
} /* not else */
|
|
4821
|
+
if (currentCodeBlock.blockNotation === '>') {
|
|
4822
|
+
if (currentCodeBlock.content !== '') {
|
|
4823
|
+
currentCodeBlock.content += '\n';
|
|
4824
|
+
}
|
|
4825
|
+
currentCodeBlock.content += line.slice(2);
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '>' /* <- Note: [0] */) {
|
|
4829
|
+
codeBlocks.push(currentCodeBlock);
|
|
4830
|
+
currentCodeBlock = null;
|
|
4831
|
+
}
|
|
4832
|
+
/* not else */
|
|
4833
|
+
if (line.startsWith('```')) {
|
|
4834
|
+
const language = line.slice(3).trim() || null;
|
|
4835
|
+
if (currentCodeBlock === null) {
|
|
4836
|
+
currentCodeBlock = { blockNotation: '```', language, content: '' };
|
|
4837
|
+
}
|
|
4838
|
+
else {
|
|
4839
|
+
if (language !== null) {
|
|
4840
|
+
throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
|
|
4841
|
+
}
|
|
4842
|
+
codeBlocks.push(currentCodeBlock);
|
|
4843
|
+
currentCodeBlock = null;
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '```') {
|
|
4847
|
+
if (currentCodeBlock.content !== '') {
|
|
4848
|
+
currentCodeBlock.content += '\n';
|
|
4849
|
+
}
|
|
4850
|
+
currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
if (currentCodeBlock !== null) {
|
|
4854
|
+
throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed at the end of the markdown`);
|
|
4855
|
+
}
|
|
4856
|
+
return codeBlocks;
|
|
3980
4857
|
}
|
|
3981
4858
|
/**
|
|
3982
|
-
* TODO:
|
|
3983
|
-
* TODO: [🧠][✌️] Make some Promptbook-native token system
|
|
3984
|
-
* TODO: [✌️] `countWords` should be just `splitWords(...).length`, and all other counters should use this pattern as well
|
|
4859
|
+
* TODO: Maybe name for `blockNotation` instead of '```' and '>'
|
|
3985
4860
|
*/
|
|
3986
4861
|
|
|
3987
4862
|
/**
|
|
3988
|
-
*
|
|
4863
|
+
* Extracts extracts exactly one valid JSON code block
|
|
3989
4864
|
*
|
|
3990
|
-
*
|
|
4865
|
+
* - When given string is a valid JSON as it is, it just returns it
|
|
4866
|
+
* - When there is no JSON code block the function throws a `ParseError`
|
|
4867
|
+
* - When there are multiple JSON code blocks the function throws a `ParseError`
|
|
4868
|
+
*
|
|
4869
|
+
* Note: It is not important if marked as ```json BUT if it is VALID JSON
|
|
4870
|
+
* Note: There are multiple similar function:
|
|
4871
|
+
* - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
|
|
4872
|
+
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
4873
|
+
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
4874
|
+
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
4875
|
+
*
|
|
4876
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
4877
|
+
* @throws {ParseError} if there is no valid JSON block in the markdown
|
|
3991
4878
|
*/
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4879
|
+
function extractJsonBlock(markdown) {
|
|
4880
|
+
if (isValidJsonString(markdown)) {
|
|
4881
|
+
return markdown;
|
|
4882
|
+
}
|
|
4883
|
+
const codeBlocks = extractAllBlocksFromMarkdown(markdown);
|
|
4884
|
+
const jsonBlocks = codeBlocks.filter(({ content }) => isValidJsonString(content));
|
|
4885
|
+
if (jsonBlocks.length === 0) {
|
|
4886
|
+
throw new Error('There is no valid JSON block in the markdown');
|
|
4887
|
+
}
|
|
4888
|
+
if (jsonBlocks.length > 1) {
|
|
4889
|
+
throw new Error('There are multiple JSON code blocks in the markdown');
|
|
4890
|
+
}
|
|
4891
|
+
return jsonBlocks[0].content;
|
|
4892
|
+
}
|
|
4000
4893
|
/**
|
|
4001
|
-
* TODO:
|
|
4002
|
-
*
|
|
4894
|
+
* TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
|
|
4895
|
+
* TODO: [🏢] Make this logic part of `JsonFormatParser` or `isValidJsonString`
|
|
4003
4896
|
*/
|
|
4004
4897
|
|
|
4005
4898
|
/**
|
|
@@ -4141,35 +5034,6 @@ class MemoryStorage {
|
|
|
4141
5034
|
}
|
|
4142
5035
|
}
|
|
4143
5036
|
|
|
4144
|
-
/**
|
|
4145
|
-
* Simple wrapper `new Date().toISOString()`
|
|
4146
|
-
*
|
|
4147
|
-
* Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
|
|
4148
|
-
*
|
|
4149
|
-
* @returns string_date branded type
|
|
4150
|
-
* @public exported from `@promptbook/utils`
|
|
4151
|
-
*/
|
|
4152
|
-
function $getCurrentDate() {
|
|
4153
|
-
return new Date().toISOString();
|
|
4154
|
-
}
|
|
4155
|
-
|
|
4156
|
-
/**
|
|
4157
|
-
* Parses the task and returns the list of all parameter names
|
|
4158
|
-
*
|
|
4159
|
-
* @param template the string template with parameters in {curly} braces
|
|
4160
|
-
* @returns the list of parameter names
|
|
4161
|
-
* @public exported from `@promptbook/utils`
|
|
4162
|
-
*/
|
|
4163
|
-
function extractParameterNames(template) {
|
|
4164
|
-
const matches = template.matchAll(/{\w+}/g);
|
|
4165
|
-
const parameterNames = new Set();
|
|
4166
|
-
for (const match of matches) {
|
|
4167
|
-
const parameterName = match[0].slice(1, -1);
|
|
4168
|
-
parameterNames.add(parameterName);
|
|
4169
|
-
}
|
|
4170
|
-
return parameterNames;
|
|
4171
|
-
}
|
|
4172
|
-
|
|
4173
5037
|
/**
|
|
4174
5038
|
* Intercepts LLM tools and counts total usage of the tools
|
|
4175
5039
|
*
|
|
@@ -4245,6 +5109,9 @@ function cacheLlmTools(llmTools, options = {}) {
|
|
|
4245
5109
|
case 'EMBEDDING':
|
|
4246
5110
|
promptResult = await llmTools.callEmbeddingModel(prompt);
|
|
4247
5111
|
break variant;
|
|
5112
|
+
case 'IMAGE_GENERATION':
|
|
5113
|
+
promptResult = await llmTools.callImageGenerationModel(prompt);
|
|
5114
|
+
break variant;
|
|
4248
5115
|
// <- case [🤖]:
|
|
4249
5116
|
default:
|
|
4250
5117
|
throw new PipelineExecutionError(`Unknown model variant "${prompt.modelRequirements.modelVariant}"`);
|
|
@@ -4281,12 +5148,13 @@ function cacheLlmTools(llmTools, options = {}) {
|
|
|
4281
5148
|
}
|
|
4282
5149
|
}
|
|
4283
5150
|
catch (error) {
|
|
5151
|
+
assertsError(error);
|
|
4284
5152
|
// If validation throws an unexpected error, don't cache
|
|
4285
5153
|
shouldCache = false;
|
|
4286
5154
|
if (isVerbose) {
|
|
4287
5155
|
console.info('Not caching result due to validation error for key:', key, {
|
|
4288
5156
|
content: promptResult.content,
|
|
4289
|
-
validationError:
|
|
5157
|
+
validationError: serializeError(error),
|
|
4290
5158
|
});
|
|
4291
5159
|
}
|
|
4292
5160
|
}
|
|
@@ -4332,6 +5200,11 @@ function cacheLlmTools(llmTools, options = {}) {
|
|
|
4332
5200
|
return /* not await */ callCommonModel(prompt);
|
|
4333
5201
|
};
|
|
4334
5202
|
}
|
|
5203
|
+
if (llmTools.callImageGenerationModel !== undefined) {
|
|
5204
|
+
proxyTools.callImageGenerationModel = async (prompt) => {
|
|
5205
|
+
return /* not await */ callCommonModel(prompt);
|
|
5206
|
+
};
|
|
5207
|
+
}
|
|
4335
5208
|
// <- Note: [🤖]
|
|
4336
5209
|
return proxyTools;
|
|
4337
5210
|
}
|
|
@@ -4520,6 +5393,15 @@ function countUsage(llmTools) {
|
|
|
4520
5393
|
return promptResult;
|
|
4521
5394
|
};
|
|
4522
5395
|
}
|
|
5396
|
+
if (llmTools.callImageGenerationModel !== undefined) {
|
|
5397
|
+
proxyTools.callImageGenerationModel = async (prompt) => {
|
|
5398
|
+
// console.info('[🚕] callImageGenerationModel through countTotalUsage');
|
|
5399
|
+
const promptResult = await llmTools.callImageGenerationModel(prompt);
|
|
5400
|
+
totalUsage = addUsage(totalUsage, promptResult.usage);
|
|
5401
|
+
spending.next(promptResult.usage);
|
|
5402
|
+
return promptResult;
|
|
5403
|
+
};
|
|
5404
|
+
}
|
|
4523
5405
|
// <- Note: [🤖]
|
|
4524
5406
|
return proxyTools;
|
|
4525
5407
|
}
|
|
@@ -4528,7 +5410,7 @@ function countUsage(llmTools) {
|
|
|
4528
5410
|
* TODO: [🧠] Is there some meaningfull way how to test this util
|
|
4529
5411
|
* TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
|
|
4530
5412
|
* > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
|
|
4531
|
-
* TODO: [👷♂️]
|
|
5413
|
+
* TODO: [👷♂️] Write a comprehensive manual explaining the construction and usage of LLM tools in the Promptbook ecosystem
|
|
4532
5414
|
*/
|
|
4533
5415
|
|
|
4534
5416
|
/**
|
|
@@ -4642,6 +5524,12 @@ class MultipleLlmExecutionTools {
|
|
|
4642
5524
|
callEmbeddingModel(prompt) {
|
|
4643
5525
|
return this.callCommonModel(prompt);
|
|
4644
5526
|
}
|
|
5527
|
+
/**
|
|
5528
|
+
* Calls the best available embedding model
|
|
5529
|
+
*/
|
|
5530
|
+
callImageGenerationModel(prompt) {
|
|
5531
|
+
return this.callCommonModel(prompt);
|
|
5532
|
+
}
|
|
4645
5533
|
// <- Note: [🤖]
|
|
4646
5534
|
/**
|
|
4647
5535
|
* Calls the best available model
|
|
@@ -4668,6 +5556,11 @@ class MultipleLlmExecutionTools {
|
|
|
4668
5556
|
continue llm;
|
|
4669
5557
|
}
|
|
4670
5558
|
return await llmExecutionTools.callEmbeddingModel(prompt);
|
|
5559
|
+
case 'IMAGE_GENERATION':
|
|
5560
|
+
if (llmExecutionTools.callImageGenerationModel === undefined) {
|
|
5561
|
+
continue llm;
|
|
5562
|
+
}
|
|
5563
|
+
return await llmExecutionTools.callImageGenerationModel(prompt);
|
|
4671
5564
|
// <- case [🤖]:
|
|
4672
5565
|
default:
|
|
4673
5566
|
throw new UnexpectedError(`Unknown model variant "${prompt.modelRequirements.modelVariant}" in ${llmExecutionTools.title}`);
|
|
@@ -4992,21 +5885,6 @@ const promptbookFetch = async (urlOrRequest, init) => {
|
|
|
4992
5885
|
* TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
|
|
4993
5886
|
*/
|
|
4994
5887
|
|
|
4995
|
-
/**
|
|
4996
|
-
* Checks if value is valid email
|
|
4997
|
-
*
|
|
4998
|
-
* @public exported from `@promptbook/utils`
|
|
4999
|
-
*/
|
|
5000
|
-
function isValidEmail(email) {
|
|
5001
|
-
if (typeof email !== 'string') {
|
|
5002
|
-
return false;
|
|
5003
|
-
}
|
|
5004
|
-
if (email.split('\n').length > 1) {
|
|
5005
|
-
return false;
|
|
5006
|
-
}
|
|
5007
|
-
return /^.+@.+\..+$/.test(email);
|
|
5008
|
-
}
|
|
5009
|
-
|
|
5010
5888
|
/**
|
|
5011
5889
|
* @private utility of CLI
|
|
5012
5890
|
*/
|
|
@@ -5685,113 +6563,39 @@ function $initializeListScrapersCommand(program) {
|
|
|
5685
6563
|
}));
|
|
5686
6564
|
}
|
|
5687
6565
|
/**
|
|
5688
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
5689
|
-
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5690
|
-
*/
|
|
5691
|
-
|
|
5692
|
-
/**
|
|
5693
|
-
* Initializes `login` command for Promptbook CLI utilities
|
|
5694
|
-
*
|
|
5695
|
-
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
5696
|
-
*
|
|
5697
|
-
* @private internal function of `promptbookCli`
|
|
5698
|
-
*/
|
|
5699
|
-
function $initializeLoginCommand(program) {
|
|
5700
|
-
const loginCommand = program.command('login');
|
|
5701
|
-
loginCommand.description(spaceTrim$2(`
|
|
5702
|
-
Login to the remote Promptbook server
|
|
5703
|
-
`));
|
|
5704
|
-
loginCommand.action(handleActionErrors(async (cliOptions) => {
|
|
5705
|
-
// Note: Not interested in return value of this function but the side effect of logging in
|
|
5706
|
-
await $provideLlmToolsForCli({
|
|
5707
|
-
isLoginloaded: true,
|
|
5708
|
-
cliOptions: {
|
|
5709
|
-
...cliOptions,
|
|
5710
|
-
strategy: 'REMOTE_SERVER', // <- Note: Overriding strategy to `REMOTE_SERVER`
|
|
5711
|
-
// TODO: Do not allow flag `--strategy` in `login` command at all
|
|
5712
|
-
},
|
|
5713
|
-
});
|
|
5714
|
-
return process.exit(0);
|
|
5715
|
-
}));
|
|
5716
|
-
}
|
|
5717
|
-
/**
|
|
5718
|
-
* TODO: Implement non-interactive login
|
|
5719
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
5720
|
-
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5721
|
-
*/
|
|
5722
|
-
|
|
5723
|
-
/**
|
|
5724
|
-
* Tests if given string is valid semantic version
|
|
5725
|
-
*
|
|
5726
|
-
* Note: There are two similar functions:
|
|
5727
|
-
* - `isValidSemanticVersion` which tests any semantic version
|
|
5728
|
-
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
5729
|
-
*
|
|
5730
|
-
* @public exported from `@promptbook/utils`
|
|
5731
|
-
*/
|
|
5732
|
-
function isValidSemanticVersion(version) {
|
|
5733
|
-
if (typeof version !== 'string') {
|
|
5734
|
-
return false;
|
|
5735
|
-
}
|
|
5736
|
-
if (version.startsWith('0.0.0')) {
|
|
5737
|
-
return false;
|
|
5738
|
-
}
|
|
5739
|
-
return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
|
|
5740
|
-
}
|
|
5741
|
-
|
|
5742
|
-
/**
|
|
5743
|
-
* Tests if given string is valid promptbook version
|
|
5744
|
-
* It looks into list of known promptbook versions.
|
|
5745
|
-
*
|
|
5746
|
-
* @see https://www.npmjs.com/package/promptbook?activeTab=versions
|
|
5747
|
-
* 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.
|
|
5748
|
-
* Note: There are two similar functions:
|
|
5749
|
-
* - `isValidSemanticVersion` which tests any semantic version
|
|
5750
|
-
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
5751
|
-
*
|
|
5752
|
-
* @public exported from `@promptbook/utils`
|
|
6566
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6567
|
+
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5753
6568
|
*/
|
|
5754
|
-
function isValidPromptbookVersion(version) {
|
|
5755
|
-
if (!isValidSemanticVersion(version)) {
|
|
5756
|
-
return false;
|
|
5757
|
-
}
|
|
5758
|
-
if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
|
|
5759
|
-
return false;
|
|
5760
|
-
}
|
|
5761
|
-
// <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
|
|
5762
|
-
return true;
|
|
5763
|
-
}
|
|
5764
6569
|
|
|
5765
6570
|
/**
|
|
5766
|
-
*
|
|
6571
|
+
* Initializes `login` command for Promptbook CLI utilities
|
|
5767
6572
|
*
|
|
5768
|
-
* Note:
|
|
5769
|
-
* - `isValidUrl` which tests any URL
|
|
5770
|
-
* - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
|
|
6573
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
5771
6574
|
*
|
|
5772
|
-
* @
|
|
6575
|
+
* @private internal function of `promptbookCli`
|
|
5773
6576
|
*/
|
|
5774
|
-
function
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
return true;
|
|
6577
|
+
function $initializeLoginCommand(program) {
|
|
6578
|
+
const loginCommand = program.command('login');
|
|
6579
|
+
loginCommand.description(spaceTrim$2(`
|
|
6580
|
+
Login to the remote Promptbook server
|
|
6581
|
+
`));
|
|
6582
|
+
loginCommand.action(handleActionErrors(async (cliOptions) => {
|
|
6583
|
+
// Note: Not interested in return value of this function but the side effect of logging in
|
|
6584
|
+
await $provideLlmToolsForCli({
|
|
6585
|
+
isLoginloaded: true,
|
|
6586
|
+
cliOptions: {
|
|
6587
|
+
...cliOptions,
|
|
6588
|
+
strategy: 'REMOTE_SERVER', // <- Note: Overriding strategy to `REMOTE_SERVER`
|
|
6589
|
+
// TODO: Do not allow flag `--strategy` in `login` command at all
|
|
6590
|
+
},
|
|
6591
|
+
});
|
|
6592
|
+
return process.exit(0);
|
|
6593
|
+
}));
|
|
5792
6594
|
}
|
|
5793
6595
|
/**
|
|
5794
|
-
* TODO:
|
|
6596
|
+
* TODO: Implement non-interactive login
|
|
6597
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6598
|
+
* Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
|
|
5795
6599
|
*/
|
|
5796
6600
|
|
|
5797
6601
|
/**
|
|
@@ -6494,65 +7298,6 @@ function isPipelinePrepared(pipeline) {
|
|
|
6494
7298
|
* - [♨] Are tasks prepared
|
|
6495
7299
|
*/
|
|
6496
7300
|
|
|
6497
|
-
/**
|
|
6498
|
-
* Serializes an error into a [🚉] JSON-serializable object
|
|
6499
|
-
*
|
|
6500
|
-
* @public exported from `@promptbook/utils`
|
|
6501
|
-
*/
|
|
6502
|
-
function serializeError(error) {
|
|
6503
|
-
const { name, message, stack } = error;
|
|
6504
|
-
const { id } = error;
|
|
6505
|
-
if (!Object.keys(ALL_ERRORS).includes(name)) {
|
|
6506
|
-
console.error(spaceTrim$2((block) => `
|
|
6507
|
-
|
|
6508
|
-
Cannot serialize error with name "${name}"
|
|
6509
|
-
|
|
6510
|
-
Authors of Promptbook probably forgot to add this error into the list of errors:
|
|
6511
|
-
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
${block(stack || message)}
|
|
6515
|
-
|
|
6516
|
-
`));
|
|
6517
|
-
}
|
|
6518
|
-
return {
|
|
6519
|
-
name: name,
|
|
6520
|
-
message,
|
|
6521
|
-
stack,
|
|
6522
|
-
id, // Include id in the serialized object
|
|
6523
|
-
};
|
|
6524
|
-
}
|
|
6525
|
-
|
|
6526
|
-
/**
|
|
6527
|
-
* Recursively converts JSON strings to JSON objects
|
|
6528
|
-
|
|
6529
|
-
* @public exported from `@promptbook/utils`
|
|
6530
|
-
*/
|
|
6531
|
-
function jsonStringsToJsons(object) {
|
|
6532
|
-
if (object === null) {
|
|
6533
|
-
return object;
|
|
6534
|
-
}
|
|
6535
|
-
if (Array.isArray(object)) {
|
|
6536
|
-
return object.map(jsonStringsToJsons);
|
|
6537
|
-
}
|
|
6538
|
-
if (typeof object !== 'object') {
|
|
6539
|
-
return object;
|
|
6540
|
-
}
|
|
6541
|
-
const newObject = { ...object };
|
|
6542
|
-
for (const [key, value] of Object.entries(object)) {
|
|
6543
|
-
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
6544
|
-
newObject[key] = jsonParse(value);
|
|
6545
|
-
}
|
|
6546
|
-
else {
|
|
6547
|
-
newObject[key] = jsonStringsToJsons(value);
|
|
6548
|
-
}
|
|
6549
|
-
}
|
|
6550
|
-
return newObject;
|
|
6551
|
-
}
|
|
6552
|
-
/**
|
|
6553
|
-
* TODO: Type the return type correctly
|
|
6554
|
-
*/
|
|
6555
|
-
|
|
6556
7301
|
/**
|
|
6557
7302
|
* Asserts that the execution of a Promptbook is successful
|
|
6558
7303
|
*
|
|
@@ -6730,144 +7475,63 @@ function createTask(options) {
|
|
|
6730
7475
|
message = `Working on ${current.title}`;
|
|
6731
7476
|
}
|
|
6732
7477
|
}
|
|
6733
|
-
if (!message) {
|
|
6734
|
-
if (errors.length) {
|
|
6735
|
-
message = errors[errors.length - 1].message || 'Error';
|
|
6736
|
-
}
|
|
6737
|
-
else if (warnings.length) {
|
|
6738
|
-
message = warnings[warnings.length - 1].message || 'Warning';
|
|
6739
|
-
}
|
|
6740
|
-
else if (status === 'FINISHED') {
|
|
6741
|
-
message = 'Finished';
|
|
6742
|
-
}
|
|
6743
|
-
else if (status === 'ERROR') {
|
|
6744
|
-
message = 'Error';
|
|
6745
|
-
}
|
|
6746
|
-
else {
|
|
6747
|
-
message = 'Running';
|
|
6748
|
-
}
|
|
6749
|
-
}
|
|
6750
|
-
}
|
|
6751
|
-
return {
|
|
6752
|
-
percent: percent,
|
|
6753
|
-
message: message + ' (!!!fallback)',
|
|
6754
|
-
};
|
|
6755
|
-
},
|
|
6756
|
-
get createdAt() {
|
|
6757
|
-
return createdAt;
|
|
6758
|
-
// <- Note: [1] --||--
|
|
6759
|
-
},
|
|
6760
|
-
get updatedAt() {
|
|
6761
|
-
return updatedAt;
|
|
6762
|
-
// <- Note: [1] --||--
|
|
6763
|
-
},
|
|
6764
|
-
asPromise,
|
|
6765
|
-
asObservable() {
|
|
6766
|
-
return partialResultSubject.asObservable();
|
|
6767
|
-
},
|
|
6768
|
-
get errors() {
|
|
6769
|
-
return errors;
|
|
6770
|
-
// <- Note: [1] --||--
|
|
6771
|
-
},
|
|
6772
|
-
get warnings() {
|
|
6773
|
-
return warnings;
|
|
6774
|
-
// <- Note: [1] --||--
|
|
6775
|
-
},
|
|
6776
|
-
get llmCalls() {
|
|
6777
|
-
return [...llmCalls, { foo: '!!! bar' }];
|
|
6778
|
-
// <- Note: [1] --||--
|
|
6779
|
-
},
|
|
6780
|
-
get currentValue() {
|
|
6781
|
-
return currentValue;
|
|
6782
|
-
// <- Note: [1] --||--
|
|
6783
|
-
},
|
|
6784
|
-
};
|
|
6785
|
-
}
|
|
6786
|
-
/**
|
|
6787
|
-
* TODO: Maybe allow to terminate the task and add getter `isFinished` or `status`
|
|
6788
|
-
* TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
|
|
6789
|
-
*/
|
|
6790
|
-
|
|
6791
|
-
/**
|
|
6792
|
-
* Format either small or big number
|
|
6793
|
-
*
|
|
6794
|
-
* @public exported from `@promptbook/utils`
|
|
6795
|
-
*/
|
|
6796
|
-
function numberToString(value) {
|
|
6797
|
-
if (value === 0) {
|
|
6798
|
-
return '0';
|
|
6799
|
-
}
|
|
6800
|
-
else if (Number.isNaN(value)) {
|
|
6801
|
-
return VALUE_STRINGS.nan;
|
|
6802
|
-
}
|
|
6803
|
-
else if (value === Infinity) {
|
|
6804
|
-
return VALUE_STRINGS.infinity;
|
|
6805
|
-
}
|
|
6806
|
-
else if (value === -Infinity) {
|
|
6807
|
-
return VALUE_STRINGS.negativeInfinity;
|
|
6808
|
-
}
|
|
6809
|
-
for (let exponent = 0; exponent < 15; exponent++) {
|
|
6810
|
-
const factor = 10 ** exponent;
|
|
6811
|
-
const valueRounded = Math.round(value * factor) / factor;
|
|
6812
|
-
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
6813
|
-
return valueRounded.toFixed(exponent);
|
|
6814
|
-
}
|
|
6815
|
-
}
|
|
6816
|
-
return value.toString();
|
|
6817
|
-
}
|
|
6818
|
-
|
|
6819
|
-
/**
|
|
6820
|
-
* Function `valueToString` will convert the given value to string
|
|
6821
|
-
* This is useful and used in the `templateParameters` function
|
|
6822
|
-
*
|
|
6823
|
-
* Note: This function is not just calling `toString` method
|
|
6824
|
-
* It's more complex and can handle this conversion specifically for LLM models
|
|
6825
|
-
* See `VALUE_STRINGS`
|
|
6826
|
-
*
|
|
6827
|
-
* Note: There are 2 similar functions
|
|
6828
|
-
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
6829
|
-
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
6830
|
-
*
|
|
6831
|
-
* @public exported from `@promptbook/utils`
|
|
6832
|
-
*/
|
|
6833
|
-
function valueToString(value) {
|
|
6834
|
-
try {
|
|
6835
|
-
if (value === '') {
|
|
6836
|
-
return VALUE_STRINGS.empty;
|
|
6837
|
-
}
|
|
6838
|
-
else if (value === null) {
|
|
6839
|
-
return VALUE_STRINGS.null;
|
|
6840
|
-
}
|
|
6841
|
-
else if (value === undefined) {
|
|
6842
|
-
return VALUE_STRINGS.undefined;
|
|
6843
|
-
}
|
|
6844
|
-
else if (typeof value === 'string') {
|
|
6845
|
-
return value;
|
|
6846
|
-
}
|
|
6847
|
-
else if (typeof value === 'number') {
|
|
6848
|
-
return numberToString(value);
|
|
6849
|
-
}
|
|
6850
|
-
else if (value instanceof Date) {
|
|
6851
|
-
return value.toISOString();
|
|
6852
|
-
}
|
|
6853
|
-
else {
|
|
6854
|
-
try {
|
|
6855
|
-
return JSON.stringify(value);
|
|
6856
|
-
}
|
|
6857
|
-
catch (error) {
|
|
6858
|
-
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
6859
|
-
return VALUE_STRINGS.circular;
|
|
7478
|
+
if (!message) {
|
|
7479
|
+
if (errors.length) {
|
|
7480
|
+
message = errors[errors.length - 1].message || 'Error';
|
|
7481
|
+
}
|
|
7482
|
+
else if (warnings.length) {
|
|
7483
|
+
message = warnings[warnings.length - 1].message || 'Warning';
|
|
7484
|
+
}
|
|
7485
|
+
else if (status === 'FINISHED') {
|
|
7486
|
+
message = 'Finished';
|
|
7487
|
+
}
|
|
7488
|
+
else if (status === 'ERROR') {
|
|
7489
|
+
message = 'Error';
|
|
7490
|
+
}
|
|
7491
|
+
else {
|
|
7492
|
+
message = 'Running';
|
|
7493
|
+
}
|
|
6860
7494
|
}
|
|
6861
|
-
throw error;
|
|
6862
7495
|
}
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
7496
|
+
return {
|
|
7497
|
+
percent: percent,
|
|
7498
|
+
message: message + ' (!!!fallback)',
|
|
7499
|
+
};
|
|
7500
|
+
},
|
|
7501
|
+
get createdAt() {
|
|
7502
|
+
return createdAt;
|
|
7503
|
+
// <- Note: [1] --||--
|
|
7504
|
+
},
|
|
7505
|
+
get updatedAt() {
|
|
7506
|
+
return updatedAt;
|
|
7507
|
+
// <- Note: [1] --||--
|
|
7508
|
+
},
|
|
7509
|
+
asPromise,
|
|
7510
|
+
asObservable() {
|
|
7511
|
+
return partialResultSubject.asObservable();
|
|
7512
|
+
},
|
|
7513
|
+
get errors() {
|
|
7514
|
+
return errors;
|
|
7515
|
+
// <- Note: [1] --||--
|
|
7516
|
+
},
|
|
7517
|
+
get warnings() {
|
|
7518
|
+
return warnings;
|
|
7519
|
+
// <- Note: [1] --||--
|
|
7520
|
+
},
|
|
7521
|
+
get llmCalls() {
|
|
7522
|
+
return [...llmCalls, { foo: '!!! bar' }];
|
|
7523
|
+
// <- Note: [1] --||--
|
|
7524
|
+
},
|
|
7525
|
+
get currentValue() {
|
|
7526
|
+
return currentValue;
|
|
7527
|
+
// <- Note: [1] --||--
|
|
7528
|
+
},
|
|
7529
|
+
};
|
|
6870
7530
|
}
|
|
7531
|
+
/**
|
|
7532
|
+
* TODO: Maybe allow to terminate the task and add getter `isFinished` or `status`
|
|
7533
|
+
* TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
|
|
7534
|
+
*/
|
|
6871
7535
|
|
|
6872
7536
|
/**
|
|
6873
7537
|
* Parses the given script and returns the list of all used variables that are not defined in the script
|
|
@@ -6994,41 +7658,6 @@ function extractParameterNamesFromTask(task) {
|
|
|
6994
7658
|
* TODO: [🔣] If script require contentLanguage
|
|
6995
7659
|
*/
|
|
6996
7660
|
|
|
6997
|
-
/**
|
|
6998
|
-
* Create difference set of two sets.
|
|
6999
|
-
*
|
|
7000
|
-
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
7001
|
-
* @public exported from `@promptbook/utils`
|
|
7002
|
-
*/
|
|
7003
|
-
function difference(a, b, isEqual = (a, b) => a === b) {
|
|
7004
|
-
const diff = new Set();
|
|
7005
|
-
for (const itemA of Array.from(a)) {
|
|
7006
|
-
if (!Array.from(b).some((itemB) => isEqual(itemA, itemB))) {
|
|
7007
|
-
diff.add(itemA);
|
|
7008
|
-
}
|
|
7009
|
-
}
|
|
7010
|
-
return diff;
|
|
7011
|
-
}
|
|
7012
|
-
/**
|
|
7013
|
-
* TODO: [🧠][💯] Maybe also implement symmetricDifference
|
|
7014
|
-
*/
|
|
7015
|
-
|
|
7016
|
-
/**
|
|
7017
|
-
* Creates a new set with all elements that are present in either set
|
|
7018
|
-
*
|
|
7019
|
-
* @deprecated use new javascript set methods instead @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
7020
|
-
* @public exported from `@promptbook/utils`
|
|
7021
|
-
*/
|
|
7022
|
-
function union(...sets) {
|
|
7023
|
-
const union = new Set();
|
|
7024
|
-
for (const set of sets) {
|
|
7025
|
-
for (const item of Array.from(set)) {
|
|
7026
|
-
union.add(item);
|
|
7027
|
-
}
|
|
7028
|
-
}
|
|
7029
|
-
return union;
|
|
7030
|
-
}
|
|
7031
|
-
|
|
7032
7661
|
/**
|
|
7033
7662
|
* Contains configuration options for parsing and generating CSV files, such as delimiters and quoting rules.
|
|
7034
7663
|
*
|
|
@@ -7057,28 +7686,6 @@ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO:
|
|
|
7057
7686
|
return csv;
|
|
7058
7687
|
}
|
|
7059
7688
|
|
|
7060
|
-
/**
|
|
7061
|
-
* Function to check if a string is valid CSV
|
|
7062
|
-
*
|
|
7063
|
-
* @param value The string to check
|
|
7064
|
-
* @returns `true` if the string is a valid CSV string, false otherwise
|
|
7065
|
-
*
|
|
7066
|
-
* @public exported from `@promptbook/utils`
|
|
7067
|
-
*/
|
|
7068
|
-
function isValidCsvString(value) {
|
|
7069
|
-
try {
|
|
7070
|
-
// A simple check for CSV format: at least one comma and no invalid characters
|
|
7071
|
-
if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
|
|
7072
|
-
return true;
|
|
7073
|
-
}
|
|
7074
|
-
return false;
|
|
7075
|
-
}
|
|
7076
|
-
catch (error) {
|
|
7077
|
-
assertsError(error);
|
|
7078
|
-
return false;
|
|
7079
|
-
}
|
|
7080
|
-
}
|
|
7081
|
-
|
|
7082
7689
|
/**
|
|
7083
7690
|
* Definition for CSV spreadsheet
|
|
7084
7691
|
*
|
|
@@ -7258,30 +7865,6 @@ const TextFormatParser = {
|
|
|
7258
7865
|
* TODO: [🏢] Allow to expect something inside each item of list and other formats
|
|
7259
7866
|
*/
|
|
7260
7867
|
|
|
7261
|
-
/**
|
|
7262
|
-
* Function to check if a string is valid XML
|
|
7263
|
-
*
|
|
7264
|
-
* @param value
|
|
7265
|
-
* @returns `true` if the string is a valid XML string, false otherwise
|
|
7266
|
-
*
|
|
7267
|
-
* @public exported from `@promptbook/utils`
|
|
7268
|
-
*/
|
|
7269
|
-
function isValidXmlString(value) {
|
|
7270
|
-
try {
|
|
7271
|
-
const parser = new DOMParser();
|
|
7272
|
-
const parsedDocument = parser.parseFromString(value, 'application/xml');
|
|
7273
|
-
const parserError = parsedDocument.getElementsByTagName('parsererror');
|
|
7274
|
-
if (parserError.length > 0) {
|
|
7275
|
-
return false;
|
|
7276
|
-
}
|
|
7277
|
-
return true;
|
|
7278
|
-
}
|
|
7279
|
-
catch (error) {
|
|
7280
|
-
assertsError(error);
|
|
7281
|
-
return false;
|
|
7282
|
-
}
|
|
7283
|
-
}
|
|
7284
|
-
|
|
7285
7868
|
/**
|
|
7286
7869
|
* Definition for XML format
|
|
7287
7870
|
*
|
|
@@ -7371,130 +7954,59 @@ function mapAvailableToExpectedParameters(options) {
|
|
|
7371
7954
|
${block(Array.from(expectedParameterNames)
|
|
7372
7955
|
.map((parameterName) => `- {${parameterName}}`)
|
|
7373
7956
|
.join('\n'))}
|
|
7374
|
-
|
|
7375
|
-
Remaining available parameters:
|
|
7376
|
-
${block(Array.from(availableParametersNames)
|
|
7377
|
-
.map((parameterName) => `- {${parameterName}}`)
|
|
7378
|
-
.join('\n'))}
|
|
7379
|
-
|
|
7380
|
-
`));
|
|
7381
|
-
}
|
|
7382
|
-
const expectedParameterNamesArray = Array.from(expectedParameterNames);
|
|
7383
|
-
const availableParametersNamesArray = Array.from(availableParametersNames);
|
|
7384
|
-
for (let i = 0; i < expectedParameterNames.size; i++) {
|
|
7385
|
-
mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
|
|
7386
|
-
}
|
|
7387
|
-
// Note: [👨👨👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
|
|
7388
|
-
Object.freeze(mappedParameters);
|
|
7389
|
-
return mappedParameters;
|
|
7390
|
-
}
|
|
7391
|
-
|
|
7392
|
-
/**
|
|
7393
|
-
* Takes an item or an array of items and returns an array of items
|
|
7394
|
-
*
|
|
7395
|
-
* 1) Any item except array and undefined returns array with that one item (also null)
|
|
7396
|
-
* 2) Undefined returns empty array
|
|
7397
|
-
* 3) Array returns itself
|
|
7398
|
-
*
|
|
7399
|
-
* @private internal utility
|
|
7400
|
-
*/
|
|
7401
|
-
function arrayableToArray(input) {
|
|
7402
|
-
if (input === undefined) {
|
|
7403
|
-
return [];
|
|
7404
|
-
}
|
|
7405
|
-
if (input instanceof Array) {
|
|
7406
|
-
return input;
|
|
7407
|
-
}
|
|
7408
|
-
return [input];
|
|
7409
|
-
}
|
|
7410
|
-
|
|
7411
|
-
/**
|
|
7412
|
-
* Just returns the given `LlmExecutionTools` or joins multiple into one
|
|
7413
|
-
*
|
|
7414
|
-
* @public exported from `@promptbook/core`
|
|
7415
|
-
*/
|
|
7416
|
-
function getSingleLlmExecutionTools(oneOrMoreLlmExecutionTools) {
|
|
7417
|
-
const _llms = arrayableToArray(oneOrMoreLlmExecutionTools);
|
|
7418
|
-
const llmTools = _llms.length === 1
|
|
7419
|
-
? _llms[0]
|
|
7420
|
-
: joinLlmExecutionTools('Multiple LLM Providers joined by `getSingleLlmExecutionTools`', ..._llms);
|
|
7421
|
-
return llmTools;
|
|
7422
|
-
}
|
|
7423
|
-
/**
|
|
7424
|
-
* TODO: [🙆] `getSingleLlmExecutionTools` vs `joinLlmExecutionTools` - explain difference or pick one
|
|
7425
|
-
* TODO: [👷♂️] @@@ Manual about construction of llmTools
|
|
7426
|
-
*/
|
|
7427
|
-
|
|
7428
|
-
/**
|
|
7429
|
-
* Replaces parameters in template with values from parameters object
|
|
7430
|
-
*
|
|
7431
|
-
* Note: This function is not places strings into string,
|
|
7432
|
-
* It's more complex and can handle this operation specifically for LLM models
|
|
7433
|
-
*
|
|
7434
|
-
* @param template the template with parameters in {curly} braces
|
|
7435
|
-
* @param parameters the object with parameters
|
|
7436
|
-
* @returns the template with replaced parameters
|
|
7437
|
-
* @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
|
|
7438
|
-
* @public exported from `@promptbook/utils`
|
|
7439
|
-
*/
|
|
7440
|
-
function templateParameters(template, parameters) {
|
|
7441
|
-
for (const [parameterName, parameterValue] of Object.entries(parameters)) {
|
|
7442
|
-
if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
|
|
7443
|
-
throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
|
|
7444
|
-
}
|
|
7445
|
-
else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
|
|
7446
|
-
// TODO: [🍵]
|
|
7447
|
-
throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
|
|
7448
|
-
}
|
|
7449
|
-
}
|
|
7450
|
-
let replacedTemplates = template;
|
|
7451
|
-
let match;
|
|
7452
|
-
let loopLimit = LOOP_LIMIT;
|
|
7453
|
-
while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
|
|
7454
|
-
.exec(replacedTemplates))) {
|
|
7455
|
-
if (loopLimit-- < 0) {
|
|
7456
|
-
throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
|
|
7457
|
-
}
|
|
7458
|
-
const precol = match.groups.precol;
|
|
7459
|
-
const parameterName = match.groups.parameterName;
|
|
7460
|
-
if (parameterName === '') {
|
|
7461
|
-
// Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
|
|
7462
|
-
continue;
|
|
7463
|
-
}
|
|
7464
|
-
if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
|
|
7465
|
-
throw new PipelineExecutionError('Parameter is already opened or not closed');
|
|
7466
|
-
}
|
|
7467
|
-
if (parameters[parameterName] === undefined) {
|
|
7468
|
-
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
7469
|
-
}
|
|
7470
|
-
let parameterValue = parameters[parameterName];
|
|
7471
|
-
if (parameterValue === undefined) {
|
|
7472
|
-
throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
|
|
7473
|
-
}
|
|
7474
|
-
parameterValue = valueToString(parameterValue);
|
|
7475
|
-
// Escape curly braces in parameter values to prevent prompt-injection
|
|
7476
|
-
parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
|
|
7477
|
-
if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
|
|
7478
|
-
parameterValue = parameterValue
|
|
7479
|
-
.split('\n')
|
|
7480
|
-
.map((line, index) => (index === 0 ? line : `${precol}${line}`))
|
|
7481
|
-
.join('\n');
|
|
7482
|
-
}
|
|
7483
|
-
replacedTemplates =
|
|
7484
|
-
replacedTemplates.substring(0, match.index + precol.length) +
|
|
7485
|
-
parameterValue +
|
|
7486
|
-
replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
|
|
7957
|
+
|
|
7958
|
+
Remaining available parameters:
|
|
7959
|
+
${block(Array.from(availableParametersNames)
|
|
7960
|
+
.map((parameterName) => `- {${parameterName}}`)
|
|
7961
|
+
.join('\n'))}
|
|
7962
|
+
|
|
7963
|
+
`));
|
|
7487
7964
|
}
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7965
|
+
const expectedParameterNamesArray = Array.from(expectedParameterNames);
|
|
7966
|
+
const availableParametersNamesArray = Array.from(availableParametersNames);
|
|
7967
|
+
for (let i = 0; i < expectedParameterNames.size; i++) {
|
|
7968
|
+
mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
|
|
7491
7969
|
}
|
|
7492
|
-
// [
|
|
7493
|
-
|
|
7494
|
-
|
|
7970
|
+
// Note: [👨👨👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
|
|
7971
|
+
Object.freeze(mappedParameters);
|
|
7972
|
+
return mappedParameters;
|
|
7973
|
+
}
|
|
7974
|
+
|
|
7975
|
+
/**
|
|
7976
|
+
* Takes an item or an array of items and returns an array of items
|
|
7977
|
+
*
|
|
7978
|
+
* 1) Any item except array and undefined returns array with that one item (also null)
|
|
7979
|
+
* 2) Undefined returns empty array
|
|
7980
|
+
* 3) Array returns itself
|
|
7981
|
+
*
|
|
7982
|
+
* @private internal utility
|
|
7983
|
+
*/
|
|
7984
|
+
function arrayableToArray(input) {
|
|
7985
|
+
if (input === undefined) {
|
|
7986
|
+
return [];
|
|
7495
7987
|
}
|
|
7496
|
-
|
|
7988
|
+
if (input instanceof Array) {
|
|
7989
|
+
return input;
|
|
7990
|
+
}
|
|
7991
|
+
return [input];
|
|
7992
|
+
}
|
|
7993
|
+
|
|
7994
|
+
/**
|
|
7995
|
+
* Just returns the given `LlmExecutionTools` or joins multiple into one
|
|
7996
|
+
*
|
|
7997
|
+
* @public exported from `@promptbook/core`
|
|
7998
|
+
*/
|
|
7999
|
+
function getSingleLlmExecutionTools(oneOrMoreLlmExecutionTools) {
|
|
8000
|
+
const _llms = arrayableToArray(oneOrMoreLlmExecutionTools);
|
|
8001
|
+
const llmTools = _llms.length === 1
|
|
8002
|
+
? _llms[0]
|
|
8003
|
+
: joinLlmExecutionTools('Multiple LLM Providers joined by `getSingleLlmExecutionTools`', ..._llms);
|
|
8004
|
+
return llmTools;
|
|
7497
8005
|
}
|
|
8006
|
+
/**
|
|
8007
|
+
* TODO: [🙆] `getSingleLlmExecutionTools` vs `joinLlmExecutionTools` - explain difference or pick one
|
|
8008
|
+
* TODO: [👷♂️] @@@ Manual about construction of llmTools
|
|
8009
|
+
*/
|
|
7498
8010
|
|
|
7499
8011
|
/**
|
|
7500
8012
|
* Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
|
|
@@ -7590,8 +8102,9 @@ async function executeAttempts(options) {
|
|
|
7590
8102
|
$ongoingTaskResult.$resultString = $ongoingTaskResult.$completionResult.content;
|
|
7591
8103
|
break variant;
|
|
7592
8104
|
case 'EMBEDDING':
|
|
8105
|
+
case 'IMAGE_GENERATION':
|
|
7593
8106
|
throw new PipelineExecutionError(spaceTrim$1((block) => `
|
|
7594
|
-
|
|
8107
|
+
${modelRequirements.modelVariant} model can not be used in pipeline
|
|
7595
8108
|
|
|
7596
8109
|
This should be catched during parsing
|
|
7597
8110
|
|
|
@@ -8725,35 +9238,6 @@ function createPipelineExecutor(options) {
|
|
|
8725
9238
|
return pipelineExecutor;
|
|
8726
9239
|
}
|
|
8727
9240
|
|
|
8728
|
-
/**
|
|
8729
|
-
* Async version of Array.forEach
|
|
8730
|
-
*
|
|
8731
|
-
* @param array - Array to iterate over
|
|
8732
|
-
* @param options - Options for the function
|
|
8733
|
-
* @param callbackfunction - Function to call for each item
|
|
8734
|
-
* @public exported from `@promptbook/utils`
|
|
8735
|
-
* @deprecated [🪂] Use queues instead
|
|
8736
|
-
*/
|
|
8737
|
-
async function forEachAsync(array, options, callbackfunction) {
|
|
8738
|
-
const { maxParallelCount = Infinity } = options;
|
|
8739
|
-
let index = 0;
|
|
8740
|
-
let runningTasks = [];
|
|
8741
|
-
const tasks = [];
|
|
8742
|
-
for (const item of array) {
|
|
8743
|
-
const currentIndex = index++;
|
|
8744
|
-
const task = callbackfunction(item, currentIndex, array);
|
|
8745
|
-
tasks.push(task);
|
|
8746
|
-
runningTasks.push(task);
|
|
8747
|
-
/* not await */ Promise.resolve(task).then(() => {
|
|
8748
|
-
runningTasks = runningTasks.filter((t) => t !== task);
|
|
8749
|
-
});
|
|
8750
|
-
if (maxParallelCount < runningTasks.length) {
|
|
8751
|
-
await Promise.race(runningTasks);
|
|
8752
|
-
}
|
|
8753
|
-
}
|
|
8754
|
-
await Promise.all(tasks);
|
|
8755
|
-
}
|
|
8756
|
-
|
|
8757
9241
|
/**
|
|
8758
9242
|
* Prepares the persona for the pipeline
|
|
8759
9243
|
*
|
|
@@ -9889,75 +10373,6 @@ const EXPECTATION_UNITS = ['CHARACTERS', 'WORDS', 'SENTENCES', 'LINES', 'PARAGRA
|
|
|
9889
10373
|
* TODO: [💝] Unite object for expecting amount and format - remove format
|
|
9890
10374
|
*/
|
|
9891
10375
|
|
|
9892
|
-
/**
|
|
9893
|
-
* Function parseNumber will parse number from string
|
|
9894
|
-
*
|
|
9895
|
-
* Note: [🔂] This function is idempotent.
|
|
9896
|
-
* Unlike Number.parseInt, Number.parseFloat it will never ever result in NaN
|
|
9897
|
-
* Note: it also works only with decimal numbers
|
|
9898
|
-
*
|
|
9899
|
-
* @returns parsed number
|
|
9900
|
-
* @throws {ParseError} if the value is not a number
|
|
9901
|
-
*
|
|
9902
|
-
* @public exported from `@promptbook/utils`
|
|
9903
|
-
*/
|
|
9904
|
-
function parseNumber(value) {
|
|
9905
|
-
const originalValue = value;
|
|
9906
|
-
if (typeof value === 'number') {
|
|
9907
|
-
value = value.toString(); // <- TODO: Maybe more efficient way to do this
|
|
9908
|
-
}
|
|
9909
|
-
if (typeof value !== 'string') {
|
|
9910
|
-
return 0;
|
|
9911
|
-
}
|
|
9912
|
-
value = value.trim();
|
|
9913
|
-
if (value.startsWith('+')) {
|
|
9914
|
-
return parseNumber(value.substring(1));
|
|
9915
|
-
}
|
|
9916
|
-
if (value.startsWith('-')) {
|
|
9917
|
-
const number = parseNumber(value.substring(1));
|
|
9918
|
-
if (number === 0) {
|
|
9919
|
-
return 0; // <- Note: To prevent -0
|
|
9920
|
-
}
|
|
9921
|
-
return -number;
|
|
9922
|
-
}
|
|
9923
|
-
value = value.replace(/,/g, '.');
|
|
9924
|
-
value = value.toUpperCase();
|
|
9925
|
-
if (value === '') {
|
|
9926
|
-
return 0;
|
|
9927
|
-
}
|
|
9928
|
-
if (value === '♾' || value.startsWith('INF')) {
|
|
9929
|
-
return Infinity;
|
|
9930
|
-
}
|
|
9931
|
-
if (value.includes('/')) {
|
|
9932
|
-
const [numerator_, denominator_] = value.split('/');
|
|
9933
|
-
const numerator = parseNumber(numerator_);
|
|
9934
|
-
const denominator = parseNumber(denominator_);
|
|
9935
|
-
if (denominator === 0) {
|
|
9936
|
-
throw new ParseError(`Unable to parse number from "${originalValue}" because denominator is zero`);
|
|
9937
|
-
}
|
|
9938
|
-
return numerator / denominator;
|
|
9939
|
-
}
|
|
9940
|
-
if (/^(NAN|NULL|NONE|UNDEFINED|ZERO|NO.*)$/.test(value)) {
|
|
9941
|
-
return 0;
|
|
9942
|
-
}
|
|
9943
|
-
if (value.includes('E')) {
|
|
9944
|
-
const [significand, exponent] = value.split('E');
|
|
9945
|
-
return parseNumber(significand) * 10 ** parseNumber(exponent);
|
|
9946
|
-
}
|
|
9947
|
-
if (!/^[0-9.]+$/.test(value) || value.split('.').length > 2) {
|
|
9948
|
-
throw new ParseError(`Unable to parse number from "${originalValue}"`);
|
|
9949
|
-
}
|
|
9950
|
-
const num = parseFloat(value);
|
|
9951
|
-
if (isNaN(num)) {
|
|
9952
|
-
throw new ParseError(`Unexpected NaN when parsing number from "${originalValue}"`);
|
|
9953
|
-
}
|
|
9954
|
-
return num;
|
|
9955
|
-
}
|
|
9956
|
-
/**
|
|
9957
|
-
* TODO: Maybe use sth. like safe-eval in fraction/calculation case @see https://www.npmjs.com/package/safe-eval
|
|
9958
|
-
* TODO: [🧠][🌻] Maybe export through `@promptbook/markdown-utils` not `@promptbook/utils`
|
|
9959
|
-
*/
|
|
9960
|
-
|
|
9961
10376
|
/**
|
|
9962
10377
|
import { WrappedError } from '../../errors/WrappedError';
|
|
9963
10378
|
import { assertsError } from '../../errors/assertsError';
|
|
@@ -10100,84 +10515,6 @@ const expectCommandParser = {
|
|
|
10100
10515
|
},
|
|
10101
10516
|
};
|
|
10102
10517
|
|
|
10103
|
-
/**
|
|
10104
|
-
* Normalizes a given text to camelCase format.
|
|
10105
|
-
*
|
|
10106
|
-
* Note: [🔂] This function is idempotent.
|
|
10107
|
-
*
|
|
10108
|
-
* @param text The text to be normalized.
|
|
10109
|
-
* @param _isFirstLetterCapital Whether the first letter should be capitalized.
|
|
10110
|
-
* @returns The camelCase formatted string.
|
|
10111
|
-
* @example 'helloWorld'
|
|
10112
|
-
* @example 'iLovePromptbook'
|
|
10113
|
-
* @public exported from `@promptbook/utils`
|
|
10114
|
-
*/
|
|
10115
|
-
function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
|
|
10116
|
-
let charType;
|
|
10117
|
-
let lastCharType = null;
|
|
10118
|
-
let normalizedName = '';
|
|
10119
|
-
for (const char of text) {
|
|
10120
|
-
let normalizedChar;
|
|
10121
|
-
if (/^[a-z]$/.test(char)) {
|
|
10122
|
-
charType = 'LOWERCASE';
|
|
10123
|
-
normalizedChar = char;
|
|
10124
|
-
}
|
|
10125
|
-
else if (/^[A-Z]$/.test(char)) {
|
|
10126
|
-
charType = 'UPPERCASE';
|
|
10127
|
-
normalizedChar = char.toLowerCase();
|
|
10128
|
-
}
|
|
10129
|
-
else if (/^[0-9]$/.test(char)) {
|
|
10130
|
-
charType = 'NUMBER';
|
|
10131
|
-
normalizedChar = char;
|
|
10132
|
-
}
|
|
10133
|
-
else {
|
|
10134
|
-
charType = 'OTHER';
|
|
10135
|
-
normalizedChar = '';
|
|
10136
|
-
}
|
|
10137
|
-
if (!lastCharType) {
|
|
10138
|
-
if (_isFirstLetterCapital) {
|
|
10139
|
-
normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
|
|
10140
|
-
}
|
|
10141
|
-
}
|
|
10142
|
-
else if (charType !== lastCharType &&
|
|
10143
|
-
!(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
|
|
10144
|
-
!(lastCharType === 'NUMBER') &&
|
|
10145
|
-
!(charType === 'NUMBER')) {
|
|
10146
|
-
normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
|
|
10147
|
-
}
|
|
10148
|
-
normalizedName += normalizedChar;
|
|
10149
|
-
lastCharType = charType;
|
|
10150
|
-
}
|
|
10151
|
-
return normalizedName;
|
|
10152
|
-
}
|
|
10153
|
-
/**
|
|
10154
|
-
* TODO: [🌺] Use some intermediate util splitWords
|
|
10155
|
-
*/
|
|
10156
|
-
|
|
10157
|
-
/**
|
|
10158
|
-
* Removes quotes from a string
|
|
10159
|
-
*
|
|
10160
|
-
* Note: [🔂] This function is idempotent.
|
|
10161
|
-
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
10162
|
-
* Note: This function removes only the same quotes from the beginning and the end of the string
|
|
10163
|
-
* Note: There are two similar functions:
|
|
10164
|
-
* - `removeQuotes` which removes only bounding quotes
|
|
10165
|
-
* - `unwrapResult` which removes whole introduce sentence
|
|
10166
|
-
*
|
|
10167
|
-
* @param text optionally quoted text
|
|
10168
|
-
* @returns text without quotes
|
|
10169
|
-
* @public exported from `@promptbook/utils`
|
|
10170
|
-
*/
|
|
10171
|
-
function removeQuotes(text) {
|
|
10172
|
-
if (text.startsWith('"') && text.endsWith('"')) {
|
|
10173
|
-
return text.slice(1, -1);
|
|
10174
|
-
}
|
|
10175
|
-
if (text.startsWith("'") && text.endsWith("'")) {
|
|
10176
|
-
return text.slice(1, -1);
|
|
10177
|
-
}
|
|
10178
|
-
return text;
|
|
10179
|
-
}
|
|
10180
|
-
|
|
10181
10518
|
/**
|
|
10182
10519
|
* Function `validateParameterName` will normalize and validate a parameter name for use in pipelines.
|
|
10183
10520
|
* It removes diacritics, emojis, and quotes, normalizes to camelCase, and checks for reserved names and invalid characters.
|
|
@@ -11067,11 +11404,7 @@ const modelCommandParser = {
|
|
|
11067
11404
|
// TODO: [🚜] DRY
|
|
11068
11405
|
if ($taskJson.modelRequirements[command.key] !== undefined) {
|
|
11069
11406
|
if ($taskJson.modelRequirements[command.key] === command.value) {
|
|
11070
|
-
console.warn(`Multiple commands \`MODEL ${{
|
|
11071
|
-
modelName: 'NAME',
|
|
11072
|
-
modelVariant: 'VARIANT',
|
|
11073
|
-
maxTokens: '???',
|
|
11074
|
-
}[command.key]} ${command.value}\` in the task "${$taskJson.title || $taskJson.name}"`);
|
|
11407
|
+
console.warn(`Multiple commands \`MODEL ${command.key} ${command.value}\` in the task "${$taskJson.title || $taskJson.name}"`);
|
|
11075
11408
|
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
11076
11409
|
}
|
|
11077
11410
|
else {
|
|
@@ -11352,30 +11685,16 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
|
|
|
11352
11685
|
}
|
|
11353
11686
|
console.warn(spaceTrim$2(`
|
|
11354
11687
|
|
|
11355
|
-
Persona "${personaName}" is defined multiple times with different description:
|
|
11356
|
-
|
|
11357
|
-
First definition:
|
|
11358
|
-
${persona.description}
|
|
11359
|
-
|
|
11360
|
-
Second definition:
|
|
11361
|
-
${personaDescription}
|
|
11362
|
-
|
|
11363
|
-
`));
|
|
11364
|
-
persona.description += spaceTrim$2('\n\n' + personaDescription);
|
|
11365
|
-
}
|
|
11366
|
-
|
|
11367
|
-
/**
|
|
11368
|
-
* Checks if the given value is a valid JavaScript identifier name.
|
|
11369
|
-
*
|
|
11370
|
-
* @param javascriptName The value to check for JavaScript identifier validity.
|
|
11371
|
-
* @returns `true` if the value is a valid JavaScript name, false otherwise.
|
|
11372
|
-
* @public exported from `@promptbook/utils`
|
|
11373
|
-
*/
|
|
11374
|
-
function isValidJavascriptName(javascriptName) {
|
|
11375
|
-
if (typeof javascriptName !== 'string') {
|
|
11376
|
-
return false;
|
|
11377
|
-
}
|
|
11378
|
-
return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/i.test(javascriptName);
|
|
11688
|
+
Persona "${personaName}" is defined multiple times with different description:
|
|
11689
|
+
|
|
11690
|
+
First definition:
|
|
11691
|
+
${persona.description}
|
|
11692
|
+
|
|
11693
|
+
Second definition:
|
|
11694
|
+
${personaDescription}
|
|
11695
|
+
|
|
11696
|
+
`));
|
|
11697
|
+
persona.description += spaceTrim$2('\n\n' + personaDescription);
|
|
11379
11698
|
}
|
|
11380
11699
|
|
|
11381
11700
|
/**
|
|
@@ -12891,345 +13210,59 @@ function parsePipeline(pipelineString) {
|
|
|
12891
13210
|
if ($pipelineJson.formfactorName === undefined) {
|
|
12892
13211
|
$pipelineJson.formfactorName = 'GENERIC';
|
|
12893
13212
|
}
|
|
12894
|
-
// =============================================================
|
|
12895
|
-
return exportJson({
|
|
12896
|
-
name: 'pipelineJson',
|
|
12897
|
-
message: `Result of \`parsePipeline\``,
|
|
12898
|
-
order: ORDER_OF_PIPELINE_JSON,
|
|
12899
|
-
value: {
|
|
12900
|
-
formfactorName: 'GENERIC',
|
|
12901
|
-
// <- Note: [🔆] Setting `formfactorName` is redundant to satisfy the typescript
|
|
12902
|
-
...$pipelineJson,
|
|
12903
|
-
},
|
|
12904
|
-
});
|
|
12905
|
-
}
|
|
12906
|
-
/**
|
|
12907
|
-
* TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
|
|
12908
|
-
* TODO: [main] !!4 Warn if used only sync version
|
|
12909
|
-
* TODO: [🚞] Report here line/column of error
|
|
12910
|
-
* TODO: Use spaceTrim more effectively
|
|
12911
|
-
* TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
|
|
12912
|
-
* TODO: [🥞] Not optimal parsing because `splitMarkdownIntoSections` is executed twice with same string, once through `flattenMarkdown` and second directly here
|
|
12913
|
-
* TODO: [♈] Probably move expectations from tasks to parameters
|
|
12914
|
-
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
12915
|
-
* TODO: [🍙] Make some standard order of json properties
|
|
12916
|
-
*/
|
|
12917
|
-
|
|
12918
|
-
/**
|
|
12919
|
-
* Compile pipeline from string (markdown) format to JSON format
|
|
12920
|
-
*
|
|
12921
|
-
* @see https://github.com/webgptorg/promptbook/discussions/196
|
|
12922
|
-
*
|
|
12923
|
-
* Note: This function does not validate logic of the pipeline only the parsing
|
|
12924
|
-
* Note: This function acts as compilation process
|
|
12925
|
-
*
|
|
12926
|
-
* @param pipelineString {Promptbook} in string markdown format (.book.md)
|
|
12927
|
-
* @param tools - Tools for the preparation and scraping - if not provided together with `llm`, the preparation will be skipped
|
|
12928
|
-
* @param options - Options and tools for the compilation
|
|
12929
|
-
* @returns {Promptbook} compiled in JSON format (.bookc)
|
|
12930
|
-
* @throws {ParseError} if the promptbook string is not valid
|
|
12931
|
-
* @public exported from `@promptbook/core`
|
|
12932
|
-
*/
|
|
12933
|
-
async function compilePipeline(pipelineString, tools, options) {
|
|
12934
|
-
let pipelineJson = parsePipeline(pipelineString);
|
|
12935
|
-
if (tools !== undefined && tools.llm !== undefined) {
|
|
12936
|
-
pipelineJson = await preparePipeline(pipelineJson, tools, options || {
|
|
12937
|
-
rootDirname: null,
|
|
12938
|
-
});
|
|
12939
|
-
}
|
|
12940
|
-
// Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
|
|
12941
|
-
return pipelineJson;
|
|
12942
|
-
}
|
|
12943
|
-
/**
|
|
12944
|
-
* TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
|
|
12945
|
-
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
12946
|
-
* TODO: [🧠] Should be in generated JSON file GENERATOR_WARNING
|
|
12947
|
-
*/
|
|
12948
|
-
|
|
12949
|
-
/**
|
|
12950
|
-
* Creates a Mermaid graph based on the promptbook
|
|
12951
|
-
*
|
|
12952
|
-
* Note: The result is not wrapped in a Markdown code block
|
|
12953
|
-
*
|
|
12954
|
-
* @public exported from `@promptbook/utils`
|
|
12955
|
-
*/
|
|
12956
|
-
function renderPromptbookMermaid(pipelineJson, options) {
|
|
12957
|
-
const { linkTask = () => null } = options || {};
|
|
12958
|
-
const MERMAID_PREFIX = 'pipeline_';
|
|
12959
|
-
const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
|
|
12960
|
-
const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
|
|
12961
|
-
const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
|
|
12962
|
-
const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
|
|
12963
|
-
const parameterNameToTaskName = (parameterName) => {
|
|
12964
|
-
if (parameterName === 'knowledge') {
|
|
12965
|
-
return MERMAID_KNOWLEDGE_NAME;
|
|
12966
|
-
}
|
|
12967
|
-
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
12968
|
-
return MERMAID_RESERVED_NAME;
|
|
12969
|
-
}
|
|
12970
|
-
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
12971
|
-
if (!parameter) {
|
|
12972
|
-
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
12973
|
-
// <- TODO: This causes problems when {knowledge} and other reserved parameters are used
|
|
12974
|
-
}
|
|
12975
|
-
if (parameter.isInput) {
|
|
12976
|
-
return MERMAID_INPUT_NAME;
|
|
12977
|
-
}
|
|
12978
|
-
const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
|
|
12979
|
-
if (!task) {
|
|
12980
|
-
throw new Error(`Could not find task for {${parameterName}}`);
|
|
12981
|
-
}
|
|
12982
|
-
return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
|
|
12983
|
-
};
|
|
12984
|
-
const inputAndIntermediateParametersMermaid = pipelineJson.tasks
|
|
12985
|
-
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
12986
|
-
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
12987
|
-
...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
|
|
12988
|
-
])
|
|
12989
|
-
.join('\n');
|
|
12990
|
-
const outputParametersMermaid = pipelineJson.parameters
|
|
12991
|
-
.filter(({ isOutput }) => isOutput)
|
|
12992
|
-
.map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
|
|
12993
|
-
.join('\n');
|
|
12994
|
-
const linksMermaid = pipelineJson.tasks
|
|
12995
|
-
.map((task) => {
|
|
12996
|
-
const link = linkTask(task);
|
|
12997
|
-
if (link === null) {
|
|
12998
|
-
return '';
|
|
12999
|
-
}
|
|
13000
|
-
const { href, title } = link;
|
|
13001
|
-
const taskName = parameterNameToTaskName(task.resultingParameterName);
|
|
13002
|
-
return `click ${taskName} href "${href}" "${title}";`;
|
|
13003
|
-
})
|
|
13004
|
-
.filter((line) => line !== '')
|
|
13005
|
-
.join('\n');
|
|
13006
|
-
const interactionPointsMermaid = Object.entries({
|
|
13007
|
-
[MERMAID_INPUT_NAME]: 'Input',
|
|
13008
|
-
[MERMAID_OUTPUT_NAME]: 'Output',
|
|
13009
|
-
[MERMAID_RESERVED_NAME]: 'Other',
|
|
13010
|
-
[MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
|
|
13011
|
-
})
|
|
13012
|
-
.filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
|
|
13013
|
-
.map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
|
|
13014
|
-
.join('\n');
|
|
13015
|
-
const promptbookMermaid = spaceTrim$1((block) => `
|
|
13016
|
-
|
|
13017
|
-
%% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
|
|
13018
|
-
|
|
13019
|
-
flowchart LR
|
|
13020
|
-
subgraph "${pipelineJson.title}"
|
|
13021
|
-
|
|
13022
|
-
%% Basic configuration
|
|
13023
|
-
direction TB
|
|
13024
|
-
|
|
13025
|
-
%% Interaction points from pipeline to outside
|
|
13026
|
-
${block(interactionPointsMermaid)}
|
|
13027
|
-
|
|
13028
|
-
%% Input and intermediate parameters
|
|
13029
|
-
${block(inputAndIntermediateParametersMermaid)}
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
%% Output parameters
|
|
13033
|
-
${block(outputParametersMermaid)}
|
|
13034
|
-
|
|
13035
|
-
%% Links
|
|
13036
|
-
${block(linksMermaid)}
|
|
13037
|
-
|
|
13038
|
-
%% Styles
|
|
13039
|
-
classDef ${MERMAID_INPUT_NAME} color: grey;
|
|
13040
|
-
classDef ${MERMAID_OUTPUT_NAME} color: grey;
|
|
13041
|
-
classDef ${MERMAID_RESERVED_NAME} color: grey;
|
|
13042
|
-
classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
|
|
13043
|
-
|
|
13044
|
-
end;
|
|
13045
|
-
|
|
13046
|
-
`);
|
|
13047
|
-
return promptbookMermaid;
|
|
13048
|
-
}
|
|
13049
|
-
/**
|
|
13050
|
-
* TODO: [🧠] FOREACH in mermaid graph
|
|
13051
|
-
* TODO: [🧠] Knowledge in mermaid graph
|
|
13052
|
-
* TODO: [🧠] Personas in mermaid graph
|
|
13053
|
-
* TODO: Maybe use some Mermaid package instead of string templating
|
|
13054
|
-
* TODO: [🕌] When more than 2 functionalities, split into separate functions
|
|
13055
|
-
*/
|
|
13056
|
-
|
|
13057
|
-
/**
|
|
13058
|
-
* Computes SHA-256 hash of the given object
|
|
13059
|
-
*
|
|
13060
|
-
* @public exported from `@promptbook/utils`
|
|
13061
|
-
*/
|
|
13062
|
-
function computeHash(value) {
|
|
13063
|
-
return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
|
|
13064
|
-
}
|
|
13065
|
-
/**
|
|
13066
|
-
* TODO: [🥬][🥬] Use this ACRY
|
|
13067
|
-
*/
|
|
13068
|
-
|
|
13069
|
-
/**
|
|
13070
|
-
* Makes first letter of a string lowercase
|
|
13071
|
-
*
|
|
13072
|
-
* Note: [🔂] This function is idempotent.
|
|
13073
|
-
*
|
|
13074
|
-
* @public exported from `@promptbook/utils`
|
|
13075
|
-
*/
|
|
13076
|
-
function decapitalize(word) {
|
|
13077
|
-
return word.substring(0, 1).toLowerCase() + word.substring(1);
|
|
13078
|
-
}
|
|
13079
|
-
|
|
13080
|
-
/**
|
|
13081
|
-
* Parses keywords from a string
|
|
13082
|
-
*
|
|
13083
|
-
* @param {string} input
|
|
13084
|
-
* @returns {Set} of keywords without diacritics in lowercase
|
|
13085
|
-
* @public exported from `@promptbook/utils`
|
|
13086
|
-
*/
|
|
13087
|
-
function parseKeywordsFromString(input) {
|
|
13088
|
-
const keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
|
|
13089
|
-
.toLowerCase()
|
|
13090
|
-
.split(/[^a-z0-9]+/gs)
|
|
13091
|
-
.filter((value) => value);
|
|
13092
|
-
return new Set(keywords);
|
|
13093
|
-
}
|
|
13094
|
-
|
|
13095
|
-
/**
|
|
13096
|
-
* Converts a name string into a URI-compatible format.
|
|
13097
|
-
*
|
|
13098
|
-
* @param name The string to be converted to a URI-compatible format.
|
|
13099
|
-
* @returns A URI-compatible string derived from the input name.
|
|
13100
|
-
* @example 'Hello World' -> 'hello-world'
|
|
13101
|
-
* @public exported from `@promptbook/utils`
|
|
13102
|
-
*/
|
|
13103
|
-
function nameToUriPart(name) {
|
|
13104
|
-
let uriPart = name;
|
|
13105
|
-
uriPart = uriPart.toLowerCase();
|
|
13106
|
-
uriPart = removeDiacritics(uriPart);
|
|
13107
|
-
uriPart = uriPart.replace(/[^a-zA-Z0-9]+/g, '-');
|
|
13108
|
-
uriPart = uriPart.replace(/^-+/, '');
|
|
13109
|
-
uriPart = uriPart.replace(/-+$/, '');
|
|
13110
|
-
return uriPart;
|
|
13111
|
-
}
|
|
13112
|
-
|
|
13113
|
-
/**
|
|
13114
|
-
* Converts a given name into URI-compatible parts.
|
|
13115
|
-
*
|
|
13116
|
-
* @param name The name to be converted into URI parts.
|
|
13117
|
-
* @returns An array of URI-compatible parts derived from the name.
|
|
13118
|
-
* @example 'Example Name' -> ['example', 'name']
|
|
13119
|
-
* @public exported from `@promptbook/utils`
|
|
13120
|
-
*/
|
|
13121
|
-
function nameToUriParts(name) {
|
|
13122
|
-
return nameToUriPart(name)
|
|
13123
|
-
.split('-')
|
|
13124
|
-
.filter((value) => value !== '');
|
|
13125
|
-
}
|
|
13126
|
-
|
|
13127
|
-
/**
|
|
13128
|
-
* Normalizes a given text to PascalCase format.
|
|
13129
|
-
*
|
|
13130
|
-
* Note: [🔂] This function is idempotent.
|
|
13131
|
-
*
|
|
13132
|
-
* @param text @public exported from `@promptbook/utils`
|
|
13133
|
-
* @returns
|
|
13134
|
-
* @example 'HelloWorld'
|
|
13135
|
-
* @example 'ILovePromptbook'
|
|
13136
|
-
* @public exported from `@promptbook/utils`
|
|
13137
|
-
*/
|
|
13138
|
-
function normalizeTo_PascalCase(text) {
|
|
13139
|
-
return normalizeTo_camelCase(text, true);
|
|
13213
|
+
// =============================================================
|
|
13214
|
+
return exportJson({
|
|
13215
|
+
name: 'pipelineJson',
|
|
13216
|
+
message: `Result of \`parsePipeline\``,
|
|
13217
|
+
order: ORDER_OF_PIPELINE_JSON,
|
|
13218
|
+
value: {
|
|
13219
|
+
formfactorName: 'GENERIC',
|
|
13220
|
+
// <- Note: [🔆] Setting `formfactorName` is redundant to satisfy the typescript
|
|
13221
|
+
...$pipelineJson,
|
|
13222
|
+
},
|
|
13223
|
+
});
|
|
13140
13224
|
}
|
|
13141
|
-
|
|
13142
13225
|
/**
|
|
13143
|
-
*
|
|
13144
|
-
*
|
|
13145
|
-
*
|
|
13146
|
-
*
|
|
13147
|
-
*
|
|
13226
|
+
* TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
|
|
13227
|
+
* TODO: [main] !!4 Warn if used only sync version
|
|
13228
|
+
* TODO: [🚞] Report here line/column of error
|
|
13229
|
+
* TODO: Use spaceTrim more effectively
|
|
13230
|
+
* TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
|
|
13231
|
+
* TODO: [🥞] Not optimal parsing because `splitMarkdownIntoSections` is executed twice with same string, once through `flattenMarkdown` and second directly here
|
|
13232
|
+
* TODO: [♈] Probably move expectations from tasks to parameters
|
|
13233
|
+
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
13234
|
+
* TODO: [🍙] Make some standard order of json properties
|
|
13148
13235
|
*/
|
|
13149
|
-
function normalizeWhitespaces(sentence) {
|
|
13150
|
-
return sentence.replace(/\s+/gs, ' ').trim();
|
|
13151
|
-
}
|
|
13152
13236
|
|
|
13153
13237
|
/**
|
|
13154
|
-
*
|
|
13238
|
+
* Compile pipeline from string (markdown) format to JSON format
|
|
13155
13239
|
*
|
|
13156
|
-
* @
|
|
13157
|
-
*/
|
|
13158
|
-
function suffixUrl(value, suffix) {
|
|
13159
|
-
const baseUrl = value.href.endsWith('/') ? value.href.slice(0, -1) : value.href;
|
|
13160
|
-
const normalizedSuffix = suffix.replace(/\/+/g, '/');
|
|
13161
|
-
return (baseUrl + normalizedSuffix);
|
|
13162
|
-
}
|
|
13163
|
-
|
|
13164
|
-
/**
|
|
13165
|
-
* Removes quotes and optional introduce text from a string
|
|
13240
|
+
* @see https://github.com/webgptorg/promptbook/discussions/196
|
|
13166
13241
|
*
|
|
13167
|
-
*
|
|
13168
|
-
* Note: This function
|
|
13169
|
-
* Note: There are two similar functions:
|
|
13170
|
-
* - `removeQuotes` which removes only bounding quotes
|
|
13171
|
-
* - `unwrapResult` which removes whole introduce sentence
|
|
13242
|
+
* Note: This function does not validate logic of the pipeline only the parsing
|
|
13243
|
+
* Note: This function acts as compilation process
|
|
13172
13244
|
*
|
|
13173
|
-
* @param
|
|
13174
|
-
* @
|
|
13175
|
-
* @
|
|
13245
|
+
* @param pipelineString {Promptbook} in string markdown format (.book.md)
|
|
13246
|
+
* @param tools - Tools for the preparation and scraping - if not provided together with `llm`, the preparation will be skipped
|
|
13247
|
+
* @param options - Options and tools for the compilation
|
|
13248
|
+
* @returns {Promptbook} compiled in JSON format (.bookc)
|
|
13249
|
+
* @throws {ParseError} if the promptbook string is not valid
|
|
13250
|
+
* @public exported from `@promptbook/core`
|
|
13176
13251
|
*/
|
|
13177
|
-
function
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
}
|
|
13184
|
-
let processedText = trimmedText;
|
|
13185
|
-
if (isIntroduceSentenceRemoved) {
|
|
13186
|
-
const introduceSentenceRegex = /^[a-zěščřžýáíéúů:\s]*:\s*/i;
|
|
13187
|
-
if (introduceSentenceRegex.test(text)) {
|
|
13188
|
-
// Remove the introduce sentence and quotes by replacing it with an empty string
|
|
13189
|
-
processedText = processedText.replace(introduceSentenceRegex, '');
|
|
13190
|
-
}
|
|
13191
|
-
processedText = spaceTrim$1(processedText);
|
|
13192
|
-
}
|
|
13193
|
-
if (processedText.length < 3) {
|
|
13194
|
-
return trimmedText;
|
|
13195
|
-
}
|
|
13196
|
-
if (processedText.includes('\n')) {
|
|
13197
|
-
return trimmedText;
|
|
13198
|
-
}
|
|
13199
|
-
// Remove the quotes by extracting the substring without the first and last characters
|
|
13200
|
-
const unquotedText = processedText.slice(1, -1);
|
|
13201
|
-
// Check if the text starts and ends with quotes
|
|
13202
|
-
if ([
|
|
13203
|
-
['"', '"'],
|
|
13204
|
-
["'", "'"],
|
|
13205
|
-
['`', '`'],
|
|
13206
|
-
['*', '*'],
|
|
13207
|
-
['_', '_'],
|
|
13208
|
-
['„', '“'],
|
|
13209
|
-
['«', '»'] /* <- QUOTES to config */,
|
|
13210
|
-
].some(([startQuote, endQuote]) => {
|
|
13211
|
-
if (!processedText.startsWith(startQuote)) {
|
|
13212
|
-
return false;
|
|
13213
|
-
}
|
|
13214
|
-
if (!processedText.endsWith(endQuote)) {
|
|
13215
|
-
return false;
|
|
13216
|
-
}
|
|
13217
|
-
if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
|
|
13218
|
-
return false;
|
|
13219
|
-
}
|
|
13220
|
-
if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
|
|
13221
|
-
return false;
|
|
13222
|
-
}
|
|
13223
|
-
return true;
|
|
13224
|
-
})) {
|
|
13225
|
-
return unwrapResult(unquotedText, { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
13226
|
-
}
|
|
13227
|
-
else {
|
|
13228
|
-
return processedText;
|
|
13252
|
+
async function compilePipeline(pipelineString, tools, options) {
|
|
13253
|
+
let pipelineJson = parsePipeline(pipelineString);
|
|
13254
|
+
if (tools !== undefined && tools.llm !== undefined) {
|
|
13255
|
+
pipelineJson = await preparePipeline(pipelineJson, tools, options || {
|
|
13256
|
+
rootDirname: null,
|
|
13257
|
+
});
|
|
13229
13258
|
}
|
|
13259
|
+
// Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
|
|
13260
|
+
return pipelineJson;
|
|
13230
13261
|
}
|
|
13231
13262
|
/**
|
|
13232
|
-
* TODO: [
|
|
13263
|
+
* TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
|
|
13264
|
+
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
13265
|
+
* TODO: [🧠] Should be in generated JSON file GENERATOR_WARNING
|
|
13233
13266
|
*/
|
|
13234
13267
|
|
|
13235
13268
|
/**
|
|
@@ -19444,7 +19477,7 @@ class OpenAiCompatibleExecutionTools {
|
|
|
19444
19477
|
let threadMessages = [];
|
|
19445
19478
|
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
19446
19479
|
threadMessages = prompt.thread.map((msg) => ({
|
|
19447
|
-
role: msg.
|
|
19480
|
+
role: msg.sender === 'assistant' ? 'assistant' : 'user',
|
|
19448
19481
|
content: msg.content,
|
|
19449
19482
|
}));
|
|
19450
19483
|
}
|
|
@@ -19857,13 +19890,14 @@ class OpenAiCompatibleExecutionTools {
|
|
|
19857
19890
|
const modelName = currentModelRequirements.modelName || this.getDefaultImageGenerationModel().modelName;
|
|
19858
19891
|
const modelSettings = {
|
|
19859
19892
|
model: modelName,
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19893
|
+
size: currentModelRequirements.size,
|
|
19894
|
+
quality: currentModelRequirements.quality,
|
|
19895
|
+
style: currentModelRequirements.style,
|
|
19863
19896
|
};
|
|
19864
19897
|
const rawPromptContent = templateParameters(content, { ...parameters, modelName });
|
|
19865
19898
|
const rawRequest = {
|
|
19866
19899
|
...modelSettings,
|
|
19900
|
+
size: modelSettings.size || '1024x1024',
|
|
19867
19901
|
prompt: rawPromptContent,
|
|
19868
19902
|
user: (_a = this.options.userId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
19869
19903
|
response_format: 'url', // TODO: [🧠] Maybe allow b64_json
|
|
@@ -20400,10 +20434,10 @@ class OllamaExecutionTools extends OpenAiCompatibleExecutionTools {
|
|
|
20400
20434
|
// <- TODO: [🛄]
|
|
20401
20435
|
}
|
|
20402
20436
|
/**
|
|
20403
|
-
* Default model for
|
|
20437
|
+
* Default model for completion variant.
|
|
20404
20438
|
*/
|
|
20405
20439
|
getDefaultImageGenerationModel() {
|
|
20406
|
-
return this.getDefaultModel('
|
|
20440
|
+
return this.getDefaultModel('dall-e-3');
|
|
20407
20441
|
// <- TODO: [🛄]
|
|
20408
20442
|
}
|
|
20409
20443
|
}
|
|
@@ -21218,11 +21252,10 @@ class HardcodedOpenAiCompatibleExecutionTools extends OpenAiCompatibleExecutionT
|
|
|
21218
21252
|
throw new PipelineExecutionError(`${this.title} does not support EMBEDDING model variant`);
|
|
21219
21253
|
}
|
|
21220
21254
|
/**
|
|
21221
|
-
* Default model for
|
|
21255
|
+
* Default model for completion variant.
|
|
21222
21256
|
*/
|
|
21223
21257
|
getDefaultImageGenerationModel() {
|
|
21224
|
-
|
|
21225
|
-
// <- TODO: [🛄]
|
|
21258
|
+
throw new PipelineExecutionError(`${this.title} does not support IMAGE_GENERATION model variant`);
|
|
21226
21259
|
}
|
|
21227
21260
|
}
|
|
21228
21261
|
/**
|
|
@@ -23098,6 +23131,114 @@ class DeleteCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
23098
23131
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23099
23132
|
*/
|
|
23100
23133
|
|
|
23134
|
+
/**
|
|
23135
|
+
* DICTIONARY commitment definition
|
|
23136
|
+
*
|
|
23137
|
+
* The DICTIONARY commitment defines specific terms and their meanings that the agent should use correctly
|
|
23138
|
+
* in its reasoning and responses. This ensures consistent terminology usage.
|
|
23139
|
+
*
|
|
23140
|
+
* Key features:
|
|
23141
|
+
* - Multiple DICTIONARY commitments are automatically merged into one
|
|
23142
|
+
* - Content is placed in a dedicated section of the system message
|
|
23143
|
+
* - Terms and definitions are stored in metadata.DICTIONARY for debugging
|
|
23144
|
+
* - Agent should use the defined terms correctly in responses
|
|
23145
|
+
*
|
|
23146
|
+
* Example usage in agent source:
|
|
23147
|
+
*
|
|
23148
|
+
* ```book
|
|
23149
|
+
* Legal Assistant
|
|
23150
|
+
*
|
|
23151
|
+
* PERSONA You are a knowledgeable legal assistant
|
|
23152
|
+
* DICTIONARY Misdemeanor is a minor wrongdoing or criminal offense
|
|
23153
|
+
* DICTIONARY Felony is a serious crime usually punishable by imprisonment for more than one year
|
|
23154
|
+
* DICTIONARY Tort is a civil wrong that causes harm or loss to another person, leading to legal liability
|
|
23155
|
+
* ```
|
|
23156
|
+
*
|
|
23157
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
23158
|
+
*/
|
|
23159
|
+
class DictionaryCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23160
|
+
constructor() {
|
|
23161
|
+
super('DICTIONARY');
|
|
23162
|
+
}
|
|
23163
|
+
/**
|
|
23164
|
+
* Short one-line description of DICTIONARY.
|
|
23165
|
+
*/
|
|
23166
|
+
get description() {
|
|
23167
|
+
return 'Define terms and their meanings for consistent terminology usage.';
|
|
23168
|
+
}
|
|
23169
|
+
/**
|
|
23170
|
+
* Icon for this commitment.
|
|
23171
|
+
*/
|
|
23172
|
+
get icon() {
|
|
23173
|
+
return '📚';
|
|
23174
|
+
}
|
|
23175
|
+
/**
|
|
23176
|
+
* Markdown documentation for DICTIONARY commitment.
|
|
23177
|
+
*/
|
|
23178
|
+
get documentation() {
|
|
23179
|
+
return spaceTrim$1(`
|
|
23180
|
+
# DICTIONARY
|
|
23181
|
+
|
|
23182
|
+
Defines specific terms and their meanings that the agent should use correctly in reasoning and responses.
|
|
23183
|
+
|
|
23184
|
+
## Key aspects
|
|
23185
|
+
|
|
23186
|
+
- Multiple \`DICTIONARY\` commitments are merged together.
|
|
23187
|
+
- Terms are defined in the format: "Term is definition"
|
|
23188
|
+
- The agent should use these terms consistently in responses.
|
|
23189
|
+
- Definitions help ensure accurate and consistent terminology.
|
|
23190
|
+
|
|
23191
|
+
## Examples
|
|
23192
|
+
|
|
23193
|
+
\`\`\`book
|
|
23194
|
+
Legal Assistant
|
|
23195
|
+
|
|
23196
|
+
PERSONA You are a knowledgeable legal assistant specializing in criminal law
|
|
23197
|
+
DICTIONARY Misdemeanor is a minor wrongdoing or criminal offense
|
|
23198
|
+
DICTIONARY Felony is a serious crime usually punishable by imprisonment for more than one year
|
|
23199
|
+
DICTIONARY Tort is a civil wrong that causes harm or loss to another person, leading to legal liability
|
|
23200
|
+
\`\`\`
|
|
23201
|
+
|
|
23202
|
+
\`\`\`book
|
|
23203
|
+
Medical Assistant
|
|
23204
|
+
|
|
23205
|
+
PERSONA You are a helpful medical assistant
|
|
23206
|
+
DICTIONARY Hypertension is persistently high blood pressure
|
|
23207
|
+
DICTIONARY Diabetes is a chronic condition that affects how the body processes blood sugar
|
|
23208
|
+
DICTIONARY Vaccine is a biological preparation that provides active immunity to a particular disease
|
|
23209
|
+
\`\`\`
|
|
23210
|
+
`);
|
|
23211
|
+
}
|
|
23212
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
23213
|
+
var _a;
|
|
23214
|
+
const trimmedContent = content.trim();
|
|
23215
|
+
if (!trimmedContent) {
|
|
23216
|
+
return requirements;
|
|
23217
|
+
}
|
|
23218
|
+
// Get existing dictionary entries from metadata
|
|
23219
|
+
const existingDictionary = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.DICTIONARY) || '';
|
|
23220
|
+
// Merge the new dictionary entry with existing entries
|
|
23221
|
+
const mergedDictionary = existingDictionary
|
|
23222
|
+
? `${existingDictionary}\n${trimmedContent}`
|
|
23223
|
+
: trimmedContent;
|
|
23224
|
+
// Store the merged dictionary in metadata for debugging and inspection
|
|
23225
|
+
const updatedMetadata = {
|
|
23226
|
+
...requirements.metadata,
|
|
23227
|
+
DICTIONARY: mergedDictionary,
|
|
23228
|
+
};
|
|
23229
|
+
// Create the dictionary section for the system message
|
|
23230
|
+
// Format: "# DICTIONARY\nTerm: definition\nTerm: definition..."
|
|
23231
|
+
const dictionarySection = `# DICTIONARY\n${mergedDictionary}`;
|
|
23232
|
+
return {
|
|
23233
|
+
...this.appendToSystemMessage(requirements, dictionarySection),
|
|
23234
|
+
metadata: updatedMetadata,
|
|
23235
|
+
};
|
|
23236
|
+
}
|
|
23237
|
+
}
|
|
23238
|
+
/**
|
|
23239
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23240
|
+
*/
|
|
23241
|
+
|
|
23101
23242
|
/**
|
|
23102
23243
|
* FORMAT commitment definition
|
|
23103
23244
|
*
|
|
@@ -25918,6 +26059,7 @@ const COMMITMENT_REGISTRY = [
|
|
|
25918
26059
|
new DeleteCommitmentDefinition('CANCEL'),
|
|
25919
26060
|
new DeleteCommitmentDefinition('DISCARD'),
|
|
25920
26061
|
new DeleteCommitmentDefinition('REMOVE'),
|
|
26062
|
+
new DictionaryCommitmentDefinition(),
|
|
25921
26063
|
new OpenCommitmentDefinition(),
|
|
25922
26064
|
new ClosedCommitmentDefinition(),
|
|
25923
26065
|
new UseBrowserCommitmentDefinition(),
|
|
@@ -26002,17 +26144,64 @@ function parseAgentSourceWithCommitments(agentSource) {
|
|
|
26002
26144
|
};
|
|
26003
26145
|
}
|
|
26004
26146
|
const lines = agentSource.split('\n');
|
|
26005
|
-
|
|
26147
|
+
let agentName = null;
|
|
26148
|
+
let agentNameLineIndex = -1;
|
|
26149
|
+
// Find the agent name: first non-empty line that is not a commitment and not a horizontal line
|
|
26150
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26151
|
+
const line = lines[i];
|
|
26152
|
+
if (line === undefined) {
|
|
26153
|
+
continue;
|
|
26154
|
+
}
|
|
26155
|
+
const trimmed = line.trim();
|
|
26156
|
+
if (!trimmed) {
|
|
26157
|
+
continue;
|
|
26158
|
+
}
|
|
26159
|
+
const isHorizontal = HORIZONTAL_LINE_PATTERN.test(line);
|
|
26160
|
+
if (isHorizontal) {
|
|
26161
|
+
continue;
|
|
26162
|
+
}
|
|
26163
|
+
let isCommitment = false;
|
|
26164
|
+
for (const definition of COMMITMENT_REGISTRY) {
|
|
26165
|
+
const typeRegex = definition.createTypeRegex();
|
|
26166
|
+
const match = typeRegex.exec(trimmed);
|
|
26167
|
+
if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
26168
|
+
isCommitment = true;
|
|
26169
|
+
break;
|
|
26170
|
+
}
|
|
26171
|
+
}
|
|
26172
|
+
if (!isCommitment) {
|
|
26173
|
+
agentName = trimmed;
|
|
26174
|
+
agentNameLineIndex = i;
|
|
26175
|
+
break;
|
|
26176
|
+
}
|
|
26177
|
+
}
|
|
26006
26178
|
const commitments = [];
|
|
26007
26179
|
const nonCommitmentLines = [];
|
|
26008
|
-
//
|
|
26009
|
-
|
|
26010
|
-
|
|
26180
|
+
// Add lines before agentName that are horizontal lines (they are non-commitment)
|
|
26181
|
+
for (let i = 0; i < agentNameLineIndex; i++) {
|
|
26182
|
+
const line = lines[i];
|
|
26183
|
+
if (line === undefined) {
|
|
26184
|
+
continue;
|
|
26185
|
+
}
|
|
26186
|
+
const trimmed = line.trim();
|
|
26187
|
+
if (!trimmed) {
|
|
26188
|
+
continue;
|
|
26189
|
+
}
|
|
26190
|
+
const isHorizontal = HORIZONTAL_LINE_PATTERN.test(line);
|
|
26191
|
+
if (isHorizontal) {
|
|
26192
|
+
nonCommitmentLines.push(line);
|
|
26193
|
+
}
|
|
26194
|
+
// Note: Commitments before agentName are not added to nonCommitmentLines
|
|
26195
|
+
}
|
|
26196
|
+
// Add the agent name line to non-commitment lines
|
|
26197
|
+
if (agentNameLineIndex >= 0) {
|
|
26198
|
+
nonCommitmentLines.push(lines[agentNameLineIndex]);
|
|
26011
26199
|
}
|
|
26012
26200
|
// Parse commitments with multiline support
|
|
26013
26201
|
let currentCommitment = null;
|
|
26014
|
-
// Process lines starting from the
|
|
26015
|
-
|
|
26202
|
+
// Process lines starting from after the agent name line
|
|
26203
|
+
const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
|
|
26204
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
26016
26205
|
const line = lines[i];
|
|
26017
26206
|
if (line === undefined) {
|
|
26018
26207
|
continue;
|
|
@@ -26232,7 +26421,12 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
|
|
|
26232
26421
|
};
|
|
26233
26422
|
}
|
|
26234
26423
|
// Apply each commitment in order using reduce-like pattern
|
|
26235
|
-
for (
|
|
26424
|
+
for (let i = 0; i < filteredCommitments.length; i++) {
|
|
26425
|
+
const commitment = filteredCommitments[i];
|
|
26426
|
+
// CLOSED commitment should work only if its the last commitment in the book
|
|
26427
|
+
if (commitment.type === 'CLOSED' && i !== filteredCommitments.length - 1) {
|
|
26428
|
+
continue;
|
|
26429
|
+
}
|
|
26236
26430
|
const definition = getCommitmentDefinition(commitment.type);
|
|
26237
26431
|
if (definition) {
|
|
26238
26432
|
try {
|
|
@@ -26273,44 +26467,6 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
|
|
|
26273
26467
|
};
|
|
26274
26468
|
}
|
|
26275
26469
|
|
|
26276
|
-
/**
|
|
26277
|
-
* Generates a gravatar URL based on agent name for fallback avatar
|
|
26278
|
-
*
|
|
26279
|
-
* @param agentName The agent name to generate avatar for
|
|
26280
|
-
* @returns Gravatar URL
|
|
26281
|
-
*
|
|
26282
|
-
* @private - [🤹] The fact that profile image is Gravatar is just implementation detail which should be hidden for consumer
|
|
26283
|
-
*/
|
|
26284
|
-
function generateGravatarUrl(agentName) {
|
|
26285
|
-
// Use a default name if none provided
|
|
26286
|
-
const safeName = agentName || 'Anonymous Agent';
|
|
26287
|
-
// Create a simple hash from the name for consistent avatar
|
|
26288
|
-
let hash = 0;
|
|
26289
|
-
for (let i = 0; i < safeName.length; i++) {
|
|
26290
|
-
const char = safeName.charCodeAt(i);
|
|
26291
|
-
hash = (hash << 5) - hash + char;
|
|
26292
|
-
hash = hash & hash; // Convert to 32bit integer
|
|
26293
|
-
}
|
|
26294
|
-
const avatarId = Math.abs(hash).toString();
|
|
26295
|
-
return `https://www.gravatar.com/avatar/${avatarId}?default=robohash&size=200&rating=x`;
|
|
26296
|
-
}
|
|
26297
|
-
|
|
26298
|
-
/**
|
|
26299
|
-
* Generates an image for the agent to use as profile image
|
|
26300
|
-
*
|
|
26301
|
-
* @param agentName The agent name to generate avatar for
|
|
26302
|
-
* @returns The placeholder profile image URL for the agent
|
|
26303
|
-
*
|
|
26304
|
-
* @public exported from `@promptbook/core`
|
|
26305
|
-
*/
|
|
26306
|
-
function generatePlaceholderAgentProfileImageUrl(agentName) {
|
|
26307
|
-
// Note: [🤹] The fact that profile image is Gravatar is just implementation detail which should be hidden for consumer
|
|
26308
|
-
return generateGravatarUrl(agentName);
|
|
26309
|
-
}
|
|
26310
|
-
/**
|
|
26311
|
-
* TODO: [🤹] Figure out best placeholder image generator https://i.pravatar.cc/1000?u=568
|
|
26312
|
-
*/
|
|
26313
|
-
|
|
26314
26470
|
/**
|
|
26315
26471
|
* Computes SHA-256 hash of the agent source
|
|
26316
26472
|
*
|
|
@@ -26378,7 +26534,57 @@ function parseAgentSource(agentSource) {
|
|
|
26378
26534
|
}
|
|
26379
26535
|
const meta = {};
|
|
26380
26536
|
const links = [];
|
|
26537
|
+
const capabilities = [];
|
|
26381
26538
|
for (const commitment of parseResult.commitments) {
|
|
26539
|
+
if (commitment.type === 'USE BROWSER') {
|
|
26540
|
+
capabilities.push({
|
|
26541
|
+
type: 'browser',
|
|
26542
|
+
label: 'Browser',
|
|
26543
|
+
iconName: 'Globe',
|
|
26544
|
+
});
|
|
26545
|
+
continue;
|
|
26546
|
+
}
|
|
26547
|
+
if (commitment.type === 'USE SEARCH ENGINE') {
|
|
26548
|
+
capabilities.push({
|
|
26549
|
+
type: 'search-engine',
|
|
26550
|
+
label: 'Search Internet',
|
|
26551
|
+
iconName: 'Search',
|
|
26552
|
+
});
|
|
26553
|
+
continue;
|
|
26554
|
+
}
|
|
26555
|
+
if (commitment.type === 'KNOWLEDGE') {
|
|
26556
|
+
const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
|
|
26557
|
+
let label = content;
|
|
26558
|
+
let iconName = 'Book';
|
|
26559
|
+
if (content.startsWith('http://') || content.startsWith('https://')) {
|
|
26560
|
+
try {
|
|
26561
|
+
const url = new URL(content);
|
|
26562
|
+
if (url.pathname.endsWith('.pdf')) {
|
|
26563
|
+
label = url.pathname.split('/').pop() || 'Document.pdf';
|
|
26564
|
+
iconName = 'FileText';
|
|
26565
|
+
}
|
|
26566
|
+
else {
|
|
26567
|
+
label = url.hostname.replace(/^www\./, '');
|
|
26568
|
+
}
|
|
26569
|
+
}
|
|
26570
|
+
catch (e) {
|
|
26571
|
+
// Invalid URL, treat as text
|
|
26572
|
+
}
|
|
26573
|
+
}
|
|
26574
|
+
else {
|
|
26575
|
+
// Text content - take first few words
|
|
26576
|
+
const words = content.split(/\s+/);
|
|
26577
|
+
if (words.length > 4) {
|
|
26578
|
+
label = words.slice(0, 4).join(' ') + '...';
|
|
26579
|
+
}
|
|
26580
|
+
}
|
|
26581
|
+
capabilities.push({
|
|
26582
|
+
type: 'knowledge',
|
|
26583
|
+
label,
|
|
26584
|
+
iconName,
|
|
26585
|
+
});
|
|
26586
|
+
continue;
|
|
26587
|
+
}
|
|
26382
26588
|
if (commitment.type === 'META LINK') {
|
|
26383
26589
|
const linkValue = spaceTrim$2(commitment.content);
|
|
26384
26590
|
links.push(linkValue);
|
|
@@ -26408,10 +26614,6 @@ function parseAgentSource(agentSource) {
|
|
|
26408
26614
|
const metaType = normalizeTo_camelCase(metaTypeRaw);
|
|
26409
26615
|
meta[metaType] = spaceTrim$2(commitment.content.substring(metaTypeRaw.length));
|
|
26410
26616
|
}
|
|
26411
|
-
// Generate gravatar fallback if no meta image specified
|
|
26412
|
-
if (!meta.image) {
|
|
26413
|
-
meta.image = generatePlaceholderAgentProfileImageUrl(parseResult.agentName || '!!');
|
|
26414
|
-
}
|
|
26415
26617
|
// Generate fullname fallback if no meta fullname specified
|
|
26416
26618
|
if (!meta.fullname) {
|
|
26417
26619
|
meta.fullname = parseResult.agentName || createDefaultAgentName(agentSource);
|
|
@@ -26423,11 +26625,13 @@ function parseAgentSource(agentSource) {
|
|
|
26423
26625
|
return {
|
|
26424
26626
|
agentName: normalizeAgentName(parseResult.agentName || createDefaultAgentName(agentSource)),
|
|
26425
26627
|
agentHash,
|
|
26628
|
+
permanentId: meta.id,
|
|
26426
26629
|
personaDescription,
|
|
26427
26630
|
initialMessage,
|
|
26428
26631
|
meta,
|
|
26429
26632
|
links,
|
|
26430
26633
|
parameters,
|
|
26634
|
+
capabilities,
|
|
26431
26635
|
};
|
|
26432
26636
|
}
|
|
26433
26637
|
/**
|