@promptbook/remote-server 0.100.0-5 → 0.100.0-61
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/README.md +2 -8
- package/esm/index.es.js +276 -59
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/color.index.d.ts +50 -0
- package/esm/typings/src/_packages/components.index.d.ts +36 -0
- package/esm/typings/src/_packages/core.index.d.ts +30 -0
- package/esm/typings/src/_packages/types.index.d.ts +38 -0
- package/esm/typings/src/book-2.0/agent-source/parseAgentSource.d.ts +30 -0
- package/esm/typings/src/book-2.0/agent-source/parseAgentSource.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/agent-source/string_book.d.ts +26 -0
- package/esm/typings/src/book-2.0/commitments/ACTION/ACTION.d.ts +38 -0
- package/esm/typings/src/book-2.0/commitments/FORMAT/FORMAT.d.ts +39 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +45 -0
- package/esm/typings/src/book-2.0/commitments/META_IMAGE/META_IMAGE.d.ts +44 -0
- package/esm/typings/src/book-2.0/commitments/META_LINK/META_LINK.d.ts +56 -0
- package/esm/typings/src/book-2.0/commitments/MODEL/MODEL.d.ts +39 -0
- package/esm/typings/src/book-2.0/commitments/NOTE/NOTE.d.ts +49 -0
- package/esm/typings/src/book-2.0/commitments/PERSONA/PERSONA.d.ts +46 -0
- package/esm/typings/src/book-2.0/commitments/RULE/RULE.d.ts +44 -0
- package/esm/typings/src/book-2.0/commitments/SAMPLE/SAMPLE.d.ts +44 -0
- package/esm/typings/src/book-2.0/commitments/STYLE/STYLE.d.ts +38 -0
- package/esm/typings/src/book-2.0/commitments/_base/BaseCommitmentDefinition.d.ts +52 -0
- package/esm/typings/src/book-2.0/commitments/_base/BookCommitment.d.ts +5 -0
- package/esm/typings/src/book-2.0/commitments/_base/CommitmentDefinition.d.ts +48 -0
- package/esm/typings/src/book-2.0/commitments/_base/NotYetImplementedCommitmentDefinition.d.ts +22 -0
- package/esm/typings/src/book-2.0/commitments/_base/createEmptyAgentModelRequirements.d.ts +19 -0
- package/esm/typings/src/book-2.0/commitments/_misc/AgentModelRequirements.d.ts +37 -0
- package/esm/typings/src/book-2.0/commitments/_misc/AgentSourceParseResult.d.ts +18 -0
- package/esm/typings/src/book-2.0/commitments/_misc/ParsedCommitment.d.ts +22 -0
- package/esm/typings/src/book-2.0/commitments/_misc/createAgentModelRequirements.d.ts +62 -0
- package/esm/typings/src/book-2.0/commitments/_misc/createAgentModelRequirementsWithCommitments.d.ts +36 -0
- package/esm/typings/src/book-2.0/commitments/_misc/createCommitmentRegex.d.ts +20 -0
- package/esm/typings/src/book-2.0/commitments/_misc/parseAgentSourceWithCommitments.d.ts +24 -0
- package/esm/typings/src/book-2.0/commitments/_misc/removeCommentsFromSystemMessage.d.ts +11 -0
- package/esm/typings/src/book-2.0/commitments/index.d.ts +56 -0
- package/esm/typings/src/book-2.0/utils/profileImageUtils.d.ts +39 -0
- package/esm/typings/src/book-components/AvatarProfile/AvatarChip/AvatarChip.d.ts +35 -0
- package/esm/typings/src/book-components/AvatarProfile/AvatarChip/AvatarChipFromSource.d.ts +21 -0
- package/esm/typings/src/book-components/AvatarProfile/AvatarChip/index.d.ts +2 -0
- package/esm/typings/src/book-components/AvatarProfile/AvatarProfile/AvatarProfile.d.ts +26 -0
- package/esm/typings/src/book-components/AvatarProfile/AvatarProfile/AvatarProfileFromSource.d.ts +19 -0
- package/esm/typings/src/book-components/BookEditor/BookEditor.d.ts +35 -0
- package/esm/typings/src/book-components/BookEditor/BookEditorInner.d.ts +15 -0
- package/esm/typings/src/book-components/BookEditor/config.d.ts +10 -0
- package/esm/typings/src/book-components/BookEditor/injectCssModuleIntoShadowRoot.d.ts +11 -0
- package/esm/typings/src/book-components/Chat/Chat/Chat.d.ts +20 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +110 -0
- package/esm/typings/src/book-components/Chat/LlmChat/LlmChat.d.ts +14 -0
- package/esm/typings/src/book-components/Chat/LlmChat/LlmChat.test.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +24 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +16 -0
- package/esm/typings/src/book-components/Chat/types/ChatParticipant.d.ts +32 -0
- package/esm/typings/src/book-components/Chat/utils/ChatPersistence.d.ts +25 -0
- package/esm/typings/src/book-components/Chat/utils/ExportFormat.d.ts +4 -0
- package/esm/typings/src/book-components/Chat/utils/addUtmParamsToUrl.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/utils/createShortLinkForChat.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/utils/downloadFile.d.ts +6 -0
- package/esm/typings/src/book-components/Chat/utils/exportChatHistory.d.ts +9 -0
- package/esm/typings/src/book-components/Chat/utils/generatePdfContent.d.ts +8 -0
- package/esm/typings/src/book-components/Chat/utils/generateQrDataUrl.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/utils/getPromptbookBranding.d.ts +6 -0
- package/esm/typings/src/book-components/Chat/utils/messagesToHtml.d.ts +8 -0
- package/esm/typings/src/book-components/Chat/utils/messagesToJson.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/utils/messagesToMarkdown.d.ts +8 -0
- package/esm/typings/src/book-components/Chat/utils/messagesToText.d.ts +8 -0
- package/esm/typings/src/book-components/_common/react-utils/classNames.d.ts +7 -0
- package/esm/typings/src/book-components/_common/react-utils/collectCssTextsForClass.d.ts +7 -0
- package/esm/typings/src/book-components/_common/react-utils/escapeHtml.d.ts +6 -0
- package/esm/typings/src/book-components/_common/react-utils/escapeRegex.d.ts +6 -0
- package/esm/typings/src/config.d.ts +19 -0
- package/esm/typings/src/execution/AvailableModel.d.ts +4 -0
- package/esm/typings/src/execution/ExecutionTask.d.ts +27 -1
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +8 -0
- package/esm/typings/src/execution/createPipelineExecutor/40-executeAttempts.d.ts +6 -1
- package/esm/typings/src/llm-providers/_common/filterModels.d.ts +0 -3
- package/esm/typings/src/llm-providers/_common/profiles/llmProviderProfiles.d.ts +81 -0
- package/esm/typings/src/llm-providers/_common/profiles/test/llmProviderProfiles.test.d.ts +1 -0
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +5 -0
- package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +5 -5
- package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
- package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +1 -1
- package/esm/typings/src/llm-providers/google/google-models.d.ts +1 -1
- package/esm/typings/src/llm-providers/mocked/MockedEchoLlmExecutionTools.d.ts +5 -0
- package/esm/typings/src/llm-providers/ollama/ollama-models.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +8 -0
- package/esm/typings/src/llm-providers/openai/OpenAiExecutionTools.d.ts +5 -0
- package/esm/typings/src/llm-providers/openai/openai-models.d.ts +1 -1
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +5 -0
- package/esm/typings/src/pipeline/book-notation.d.ts +2 -1
- package/esm/typings/src/playground/permanent/error-handling-playground.d.ts +5 -0
- package/esm/typings/src/types/ModelRequirements.d.ts +0 -2
- package/esm/typings/src/types/typeAliases.d.ts +6 -0
- package/esm/typings/src/utils/color/$randomColor.d.ts +11 -0
- package/esm/typings/src/utils/color/Color.d.ts +180 -0
- package/esm/typings/src/utils/color/css-colors.d.ts +159 -0
- package/esm/typings/src/utils/color/internal-utils/checkChannelValue.d.ts +14 -0
- package/esm/typings/src/utils/color/internal-utils/hslToRgb.d.ts +17 -0
- package/esm/typings/src/utils/color/internal-utils/rgbToHsl.d.ts +17 -0
- package/esm/typings/src/utils/color/operators/ColorTransformer.d.ts +5 -0
- package/esm/typings/src/utils/color/operators/darken.d.ts +9 -0
- package/esm/typings/src/utils/color/operators/furthest.d.ts +16 -0
- package/esm/typings/src/utils/color/operators/grayscale.d.ts +9 -0
- package/esm/typings/src/utils/color/operators/lighten.d.ts +12 -0
- package/esm/typings/src/utils/color/operators/mixWithColor.d.ts +11 -0
- package/esm/typings/src/utils/color/operators/nearest.d.ts +10 -0
- package/esm/typings/src/utils/color/operators/negative.d.ts +7 -0
- package/esm/typings/src/utils/color/operators/negativeLightness.d.ts +7 -0
- package/esm/typings/src/utils/color/operators/withAlpha.d.ts +9 -0
- package/esm/typings/src/utils/color/utils/areColorsEqual.d.ts +14 -0
- package/esm/typings/src/utils/color/utils/colorDistance.d.ts +21 -0
- package/esm/typings/src/utils/color/utils/colorHue.d.ts +11 -0
- package/esm/typings/src/utils/color/utils/colorHueDistance.d.ts +11 -0
- package/esm/typings/src/utils/color/utils/colorHueDistance.test.d.ts +1 -0
- package/esm/typings/src/utils/color/utils/colorLuminance.d.ts +9 -0
- package/esm/typings/src/utils/color/utils/colorSatulightion.d.ts +7 -0
- package/esm/typings/src/utils/color/utils/colorSaturation.d.ts +9 -0
- package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +10 -0
- package/esm/typings/src/utils/color/utils/mixColors.d.ts +11 -0
- package/esm/typings/src/utils/organization/preserve.d.ts +21 -0
- package/esm/typings/src/utils/take/classes/TakeChain.d.ts +11 -0
- package/esm/typings/src/utils/take/interfaces/ITakeChain.d.ts +12 -0
- package/esm/typings/src/utils/take/interfaces/Takeable.d.ts +7 -0
- package/esm/typings/src/utils/take/take.d.ts +12 -0
- package/esm/typings/src/utils/take/take.test.d.ts +1 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +2 -3
- package/umd/index.umd.js +280 -63
- package/umd/index.umd.js.map +1 -1
- package/esm/typings/src/scripting/javascript/utils/preserve.d.ts +0 -14
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Write AI applications using plain human language across multiple models and plat
|
|
|
18
18
|
|
|
19
19
|
## 🌟 New Features
|
|
20
20
|
|
|
21
|
+
- 🚀 **GPT-5 Support** - Now includes OpenAI's most advanced language model with unprecedented reasoning capabilities and 200K context window
|
|
21
22
|
- 💡 VS Code support for `.book` files with syntax highlighting and IntelliSense
|
|
22
23
|
- 🐳 Official Docker image (`hejny/promptbook`) for seamless containerized usage
|
|
23
24
|
- 🔥 Native support for OpenAI `o3-mini`, GPT-4 and other leading LLMs
|
|
@@ -60,8 +61,6 @@ Rest of the documentation is common for **entire promptbook ecosystem**:
|
|
|
60
61
|
|
|
61
62
|
During the computer revolution, we have seen [multiple generations of computer languages](https://github.com/webgptorg/promptbook/discussions/180), from the physical rewiring of the vacuum tubes through low-level machine code to the high-level languages like Python or JavaScript. And now, we're on the edge of the **next revolution**!
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
64
|
It's a revolution of writing software in **plain human language** that is understandable and executable by both humans and machines – and it's going to change everything!
|
|
66
65
|
|
|
67
66
|
The incredible growth in power of microprocessors and the Moore's Law have been the driving force behind the ever-more powerful languages, and it's been an amazing journey! Similarly, the large language models (like GPT or Claude) are the next big thing in language technology, and they're set to transform the way we interact with computers.
|
|
@@ -187,8 +186,6 @@ Join our growing community of developers and users:
|
|
|
187
186
|
|
|
188
187
|
_A concise, Markdown-based DSL for crafting AI workflows and automations._
|
|
189
188
|
|
|
190
|
-
|
|
191
|
-
|
|
192
189
|
### Introduction
|
|
193
190
|
|
|
194
191
|
Book is a Markdown-based language that simplifies the creation of AI applications, workflows, and automations. With human-readable commands, you can define inputs, outputs, personas, knowledge sources, and actions—without needing model-specific details.
|
|
@@ -238,8 +235,6 @@ Personas can have access to different knowledge, tools and actions. They can als
|
|
|
238
235
|
|
|
239
236
|
- [PERSONA](https://github.com/webgptorg/promptbook/blob/main/documents/commands/PERSONA.md)
|
|
240
237
|
|
|
241
|
-
|
|
242
|
-
|
|
243
238
|
### **3. How:** Knowledge, Instruments and Actions
|
|
244
239
|
|
|
245
240
|
The resources used by the personas are used to do the work.
|
|
@@ -314,6 +309,7 @@ Or you can install them separately:
|
|
|
314
309
|
- **[@promptbook/editable](https://www.npmjs.com/package/@promptbook/editable)** - Editable book as native javascript object with imperative object API
|
|
315
310
|
- **[@promptbook/templates](https://www.npmjs.com/package/@promptbook/templates)** - Useful templates and examples of books which can be used as a starting point
|
|
316
311
|
- **[@promptbook/types](https://www.npmjs.com/package/@promptbook/types)** - Just typescript types used in the library
|
|
312
|
+
- **[@promptbook/color](https://www.npmjs.com/package/@promptbook/color)** - Color manipulation library
|
|
317
313
|
- ⭐ **[@promptbook/cli](https://www.npmjs.com/package/@promptbook/cli)** - Command line interface utilities for promptbooks
|
|
318
314
|
- 🐋 **[Docker image](https://hub.docker.com/r/hejny/promptbook/)** - Promptbook server
|
|
319
315
|
|
|
@@ -339,8 +335,6 @@ The following glossary is used to clarify certain concepts:
|
|
|
339
335
|
|
|
340
336
|
_Note: This section is not a complete dictionary, more list of general AI / LLM terms that has connection with Promptbook_
|
|
341
337
|
|
|
342
|
-
|
|
343
|
-
|
|
344
338
|
### 💯 Core concepts
|
|
345
339
|
|
|
346
340
|
- [📚 Collection of pipelines](https://github.com/webgptorg/promptbook/discussions/65)
|
package/esm/index.es.js
CHANGED
|
@@ -11,8 +11,9 @@ import { spawn } from 'child_process';
|
|
|
11
11
|
import { stat, access, constants, readFile, writeFile, readdir, mkdir } from 'fs/promises';
|
|
12
12
|
import { join, basename, dirname } from 'path';
|
|
13
13
|
import { Subject } from 'rxjs';
|
|
14
|
-
import { format } from 'prettier';
|
|
15
14
|
import parserHtml from 'prettier/parser-html';
|
|
15
|
+
import parserMarkdown from 'prettier/parser-markdown';
|
|
16
|
+
import { format } from 'prettier/standalone';
|
|
16
17
|
import hexEncoder from 'crypto-js/enc-hex';
|
|
17
18
|
import sha256 from 'crypto-js/sha256';
|
|
18
19
|
import { SHA256 } from 'crypto-js';
|
|
@@ -33,7 +34,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
|
|
|
33
34
|
* @generated
|
|
34
35
|
* @see https://github.com/webgptorg/promptbook
|
|
35
36
|
*/
|
|
36
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.100.0-
|
|
37
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.100.0-61';
|
|
37
38
|
/**
|
|
38
39
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
39
40
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -216,6 +217,19 @@ let DEFAULT_IS_VERBOSE = false;
|
|
|
216
217
|
* @public exported from `@promptbook/core`
|
|
217
218
|
*/
|
|
218
219
|
const DEFAULT_IS_AUTO_INSTALLED = false;
|
|
220
|
+
/**
|
|
221
|
+
* Default simulated duration for a task in milliseconds (used for progress reporting)
|
|
222
|
+
*
|
|
223
|
+
* @public exported from `@promptbook/core`
|
|
224
|
+
*/
|
|
225
|
+
const DEFAULT_TASK_SIMULATED_DURATION_MS = 5 * 60 * 1000; // 5 minutes
|
|
226
|
+
/**
|
|
227
|
+
* API request timeout in milliseconds
|
|
228
|
+
* Can be overridden via API_REQUEST_TIMEOUT environment variable
|
|
229
|
+
*
|
|
230
|
+
* @public exported from `@promptbook/core`
|
|
231
|
+
*/
|
|
232
|
+
parseInt(process.env.API_REQUEST_TIMEOUT || '90000');
|
|
219
233
|
/**
|
|
220
234
|
* Indicates whether pipeline logic validation is enabled. When true, the pipeline logic is checked for consistency.
|
|
221
235
|
*
|
|
@@ -2010,6 +2024,7 @@ function createTask(options) {
|
|
|
2010
2024
|
const errors = [];
|
|
2011
2025
|
const warnings = [];
|
|
2012
2026
|
let currentValue = {};
|
|
2027
|
+
let customTldr = null;
|
|
2013
2028
|
const partialResultSubject = new Subject();
|
|
2014
2029
|
// <- Note: Not using `BehaviorSubject` because on error we can't access the last value
|
|
2015
2030
|
const finalResultPromise = /* not await */ taskProcessCallback((newOngoingResult) => {
|
|
@@ -2020,6 +2035,9 @@ function createTask(options) {
|
|
|
2020
2035
|
Object.assign(currentValue, newOngoingResult);
|
|
2021
2036
|
// <- TODO: assign deep
|
|
2022
2037
|
partialResultSubject.next(newOngoingResult);
|
|
2038
|
+
}, (tldrInfo) => {
|
|
2039
|
+
customTldr = tldrInfo;
|
|
2040
|
+
updatedAt = new Date();
|
|
2023
2041
|
});
|
|
2024
2042
|
finalResultPromise
|
|
2025
2043
|
.catch((error) => {
|
|
@@ -2073,6 +2091,78 @@ function createTask(options) {
|
|
|
2073
2091
|
return status;
|
|
2074
2092
|
// <- Note: [1] --||--
|
|
2075
2093
|
},
|
|
2094
|
+
get tldr() {
|
|
2095
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
2096
|
+
// Use custom tldr if available
|
|
2097
|
+
if (customTldr) {
|
|
2098
|
+
return customTldr;
|
|
2099
|
+
}
|
|
2100
|
+
// Fallback to default implementation
|
|
2101
|
+
const cv = currentValue;
|
|
2102
|
+
// If explicit percent is provided, use it
|
|
2103
|
+
let percentRaw = (_f = (_d = (_b = (_a = cv === null || cv === void 0 ? void 0 : cv.tldr) === null || _a === void 0 ? void 0 : _a.percent) !== null && _b !== void 0 ? _b : (_c = cv === null || cv === void 0 ? void 0 : cv.usage) === null || _c === void 0 ? void 0 : _c.percent) !== null && _d !== void 0 ? _d : (_e = cv === null || cv === void 0 ? void 0 : cv.progress) === null || _e === void 0 ? void 0 : _e.percent) !== null && _f !== void 0 ? _f : cv === null || cv === void 0 ? void 0 : cv.percent;
|
|
2104
|
+
// Simulate progress if not provided
|
|
2105
|
+
if (typeof percentRaw !== 'number') {
|
|
2106
|
+
// Simulate progress: evenly split across subtasks, based on elapsed time
|
|
2107
|
+
const now = new Date();
|
|
2108
|
+
const elapsedMs = now.getTime() - createdAt.getTime();
|
|
2109
|
+
const totalMs = DEFAULT_TASK_SIMULATED_DURATION_MS;
|
|
2110
|
+
// If subtasks are defined, split progress evenly
|
|
2111
|
+
const subtaskCount = Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks) ? cv.subtasks.length : 1;
|
|
2112
|
+
const completedSubtasks = Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks)
|
|
2113
|
+
? cv.subtasks.filter((s) => s.done || s.completed).length
|
|
2114
|
+
: 0;
|
|
2115
|
+
// Progress from completed subtasks
|
|
2116
|
+
const subtaskProgress = subtaskCount > 0 ? completedSubtasks / subtaskCount : 0;
|
|
2117
|
+
// Progress from elapsed time for current subtask
|
|
2118
|
+
const timeProgress = Math.min(elapsedMs / totalMs, 1);
|
|
2119
|
+
// Combine: completed subtasks + time progress for current subtask
|
|
2120
|
+
percentRaw = Math.min(subtaskProgress + (1 / subtaskCount) * timeProgress, 1);
|
|
2121
|
+
if (status === 'FINISHED')
|
|
2122
|
+
percentRaw = 1;
|
|
2123
|
+
if (status === 'ERROR')
|
|
2124
|
+
percentRaw = 0;
|
|
2125
|
+
}
|
|
2126
|
+
// Clamp to [0,1]
|
|
2127
|
+
let percent = Number(percentRaw) || 0;
|
|
2128
|
+
if (percent < 0)
|
|
2129
|
+
percent = 0;
|
|
2130
|
+
if (percent > 1)
|
|
2131
|
+
percent = 1;
|
|
2132
|
+
// Build a short message: prefer explicit tldr.message, then common summary/message fields, then errors/warnings, then status
|
|
2133
|
+
const messageFromResult = (_k = (_j = (_h = (_g = cv === null || cv === void 0 ? void 0 : cv.tldr) === null || _g === void 0 ? void 0 : _g.message) !== null && _h !== void 0 ? _h : cv === null || cv === void 0 ? void 0 : cv.message) !== null && _j !== void 0 ? _j : cv === null || cv === void 0 ? void 0 : cv.summary) !== null && _k !== void 0 ? _k : cv === null || cv === void 0 ? void 0 : cv.statusMessage;
|
|
2134
|
+
let message = messageFromResult;
|
|
2135
|
+
if (!message) {
|
|
2136
|
+
// If subtasks, show current subtask
|
|
2137
|
+
if (Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks) && cv.subtasks.length > 0) {
|
|
2138
|
+
const current = cv.subtasks.find((s) => !s.done && !s.completed);
|
|
2139
|
+
if (current && current.title) {
|
|
2140
|
+
message = `Working on ${current.title}`;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
if (!message) {
|
|
2144
|
+
if (errors.length) {
|
|
2145
|
+
message = errors[errors.length - 1].message || 'Error';
|
|
2146
|
+
}
|
|
2147
|
+
else if (warnings.length) {
|
|
2148
|
+
message = warnings[warnings.length - 1].message || 'Warning';
|
|
2149
|
+
}
|
|
2150
|
+
else if (status === 'FINISHED') {
|
|
2151
|
+
message = 'Finished';
|
|
2152
|
+
}
|
|
2153
|
+
else if (status === 'ERROR') {
|
|
2154
|
+
message = 'Error';
|
|
2155
|
+
}
|
|
2156
|
+
else {
|
|
2157
|
+
message = 'Running';
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
return {
|
|
2162
|
+
percent: percent,
|
|
2163
|
+
message,
|
|
2164
|
+
};
|
|
2165
|
+
},
|
|
2076
2166
|
get createdAt() {
|
|
2077
2167
|
return createdAt;
|
|
2078
2168
|
// <- Note: [1] --||--
|
|
@@ -2278,7 +2368,7 @@ function prettifyMarkdown(content) {
|
|
|
2278
2368
|
try {
|
|
2279
2369
|
return format(content, {
|
|
2280
2370
|
parser: 'markdown',
|
|
2281
|
-
plugins: [parserHtml],
|
|
2371
|
+
plugins: [parserMarkdown, parserHtml],
|
|
2282
2372
|
// TODO: DRY - make some import or auto-copy of .prettierrc
|
|
2283
2373
|
endOfLine: 'lf',
|
|
2284
2374
|
tabWidth: 4,
|
|
@@ -2769,6 +2859,76 @@ function countUsage(llmTools) {
|
|
|
2769
2859
|
* TODO: [👷♂️] @@@ Manual about construction of llmTools
|
|
2770
2860
|
*/
|
|
2771
2861
|
|
|
2862
|
+
/**
|
|
2863
|
+
* Predefined profiles for LLM providers to maintain consistency across the application
|
|
2864
|
+
* These profiles represent each provider as a virtual persona in chat interfaces
|
|
2865
|
+
*
|
|
2866
|
+
* @private !!!!
|
|
2867
|
+
*/
|
|
2868
|
+
const LLM_PROVIDER_PROFILES = {
|
|
2869
|
+
OPENAI: {
|
|
2870
|
+
name: 'OPENAI',
|
|
2871
|
+
fullname: 'OpenAI GPT',
|
|
2872
|
+
color: '#10a37f', // OpenAI's signature green
|
|
2873
|
+
// Note: avatarSrc could be added when we have provider logos available
|
|
2874
|
+
},
|
|
2875
|
+
ANTHROPIC: {
|
|
2876
|
+
name: 'ANTHROPIC',
|
|
2877
|
+
fullname: 'Anthropic Claude',
|
|
2878
|
+
color: '#d97706', // Anthropic's orange/amber color
|
|
2879
|
+
},
|
|
2880
|
+
AZURE_OPENAI: {
|
|
2881
|
+
name: 'AZURE_OPENAI',
|
|
2882
|
+
fullname: 'Azure OpenAI',
|
|
2883
|
+
color: '#0078d4', // Microsoft Azure blue
|
|
2884
|
+
},
|
|
2885
|
+
GOOGLE: {
|
|
2886
|
+
name: 'GOOGLE',
|
|
2887
|
+
fullname: 'Google Gemini',
|
|
2888
|
+
color: '#4285f4', // Google blue
|
|
2889
|
+
},
|
|
2890
|
+
DEEPSEEK: {
|
|
2891
|
+
name: 'DEEPSEEK',
|
|
2892
|
+
fullname: 'DeepSeek',
|
|
2893
|
+
color: '#7c3aed', // Purple color for DeepSeek
|
|
2894
|
+
},
|
|
2895
|
+
OLLAMA: {
|
|
2896
|
+
name: 'OLLAMA',
|
|
2897
|
+
fullname: 'Ollama',
|
|
2898
|
+
color: '#059669', // Emerald green for local models
|
|
2899
|
+
},
|
|
2900
|
+
REMOTE: {
|
|
2901
|
+
name: 'REMOTE',
|
|
2902
|
+
fullname: 'Remote Server',
|
|
2903
|
+
color: '#6b7280', // Gray for remote/proxy connections
|
|
2904
|
+
},
|
|
2905
|
+
MOCKED_ECHO: {
|
|
2906
|
+
name: 'MOCKED_ECHO',
|
|
2907
|
+
fullname: 'Echo (Test)',
|
|
2908
|
+
color: '#8b5cf6', // Purple for test/mock tools
|
|
2909
|
+
},
|
|
2910
|
+
MOCKED_FAKE: {
|
|
2911
|
+
name: 'MOCKED_FAKE',
|
|
2912
|
+
fullname: 'Fake LLM (Test)',
|
|
2913
|
+
color: '#ec4899', // Pink for fake/test tools
|
|
2914
|
+
},
|
|
2915
|
+
VERCEL: {
|
|
2916
|
+
name: 'VERCEL',
|
|
2917
|
+
fullname: 'Vercel AI',
|
|
2918
|
+
color: '#000000', // Vercel's black
|
|
2919
|
+
},
|
|
2920
|
+
MULTIPLE: {
|
|
2921
|
+
name: 'MULTIPLE',
|
|
2922
|
+
fullname: 'Multiple Providers',
|
|
2923
|
+
color: '#6366f1', // Indigo for combined/multiple providers
|
|
2924
|
+
},
|
|
2925
|
+
};
|
|
2926
|
+
/**
|
|
2927
|
+
* TODO: Refactor this - each profile must be alongside the provider definition
|
|
2928
|
+
* TODO: Unite `AvatarProfileProps` and `ChatParticipant`
|
|
2929
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
2930
|
+
*/
|
|
2931
|
+
|
|
2772
2932
|
/**
|
|
2773
2933
|
* Multiple LLM Execution Tools is a proxy server that uses multiple execution tools internally and exposes the executor interface externally.
|
|
2774
2934
|
*
|
|
@@ -2794,7 +2954,7 @@ class MultipleLlmExecutionTools {
|
|
|
2794
2954
|
}
|
|
2795
2955
|
return spaceTrim((block) => `
|
|
2796
2956
|
${headLine}
|
|
2797
|
-
|
|
2957
|
+
|
|
2798
2958
|
${ /* <- Note: Indenting the description: */block(description)}
|
|
2799
2959
|
`);
|
|
2800
2960
|
})
|
|
@@ -2805,6 +2965,9 @@ class MultipleLlmExecutionTools {
|
|
|
2805
2965
|
${block(innerModelsTitlesAndDescriptions)}
|
|
2806
2966
|
`);
|
|
2807
2967
|
}
|
|
2968
|
+
get profile() {
|
|
2969
|
+
return LLM_PROVIDER_PROFILES.MULTIPLE;
|
|
2970
|
+
}
|
|
2808
2971
|
/**
|
|
2809
2972
|
* Check the configuration of all execution tools
|
|
2810
2973
|
*/
|
|
@@ -2849,25 +3012,22 @@ class MultipleLlmExecutionTools {
|
|
|
2849
3012
|
const errors = [];
|
|
2850
3013
|
llm: for (const llmExecutionTools of this.llmExecutionTools) {
|
|
2851
3014
|
try {
|
|
2852
|
-
|
|
3015
|
+
switch (prompt.modelRequirements.modelVariant) {
|
|
2853
3016
|
case 'CHAT':
|
|
2854
3017
|
if (llmExecutionTools.callChatModel === undefined) {
|
|
2855
3018
|
continue llm;
|
|
2856
3019
|
}
|
|
2857
3020
|
return await llmExecutionTools.callChatModel(prompt);
|
|
2858
|
-
break variant;
|
|
2859
3021
|
case 'COMPLETION':
|
|
2860
3022
|
if (llmExecutionTools.callCompletionModel === undefined) {
|
|
2861
3023
|
continue llm;
|
|
2862
3024
|
}
|
|
2863
3025
|
return await llmExecutionTools.callCompletionModel(prompt);
|
|
2864
|
-
break variant;
|
|
2865
3026
|
case 'EMBEDDING':
|
|
2866
3027
|
if (llmExecutionTools.callEmbeddingModel === undefined) {
|
|
2867
3028
|
continue llm;
|
|
2868
3029
|
}
|
|
2869
3030
|
return await llmExecutionTools.callEmbeddingModel(prompt);
|
|
2870
|
-
break variant;
|
|
2871
3031
|
// <- case [🤖]:
|
|
2872
3032
|
default:
|
|
2873
3033
|
throw new UnexpectedError(`Unknown model variant "${prompt.modelRequirements.modelVariant}"`);
|
|
@@ -3020,7 +3180,7 @@ async function preparePersona(personaDescription, tools, options) {
|
|
|
3020
3180
|
const result = await preparePersonaExecutor({
|
|
3021
3181
|
availableModels /* <- Note: Passing as JSON */,
|
|
3022
3182
|
personaDescription,
|
|
3023
|
-
}).asPromise();
|
|
3183
|
+
}).asPromise({ isCrashedOnError: true });
|
|
3024
3184
|
const { outputParameters } = result;
|
|
3025
3185
|
const { modelsRequirements: modelsRequirementsJson } = outputParameters;
|
|
3026
3186
|
let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
|
|
@@ -4171,7 +4331,7 @@ async function preparePipeline(pipeline, tools, options) {
|
|
|
4171
4331
|
});
|
|
4172
4332
|
const result = await prepareTitleExecutor({
|
|
4173
4333
|
book: sources.map(({ content }) => content).join('\n\n'),
|
|
4174
|
-
}).asPromise();
|
|
4334
|
+
}).asPromise({ isCrashedOnError: true });
|
|
4175
4335
|
const { outputParameters } = result;
|
|
4176
4336
|
const { title: titleRaw } = outputParameters;
|
|
4177
4337
|
if (isVerbose) {
|
|
@@ -5282,7 +5442,7 @@ function validatePromptResult(options) {
|
|
|
5282
5442
|
*/
|
|
5283
5443
|
async function executeAttempts(options) {
|
|
5284
5444
|
const { jokerParameterNames, priority, maxAttempts, // <- Note: [💂]
|
|
5285
|
-
preparedContent, parameters, task, preparedPipeline, tools, $executionReport, pipelineIdentification, maxExecutionAttempts, } = options;
|
|
5445
|
+
preparedContent, parameters, task, preparedPipeline, tools, $executionReport, pipelineIdentification, maxExecutionAttempts, onProgress, } = options;
|
|
5286
5446
|
const $ongoingTaskResult = {
|
|
5287
5447
|
$result: null,
|
|
5288
5448
|
$resultString: null,
|
|
@@ -5526,6 +5686,10 @@ async function executeAttempts(options) {
|
|
|
5526
5686
|
result: $ongoingTaskResult.$resultString,
|
|
5527
5687
|
error: error,
|
|
5528
5688
|
});
|
|
5689
|
+
// Report failed attempt
|
|
5690
|
+
onProgress({
|
|
5691
|
+
errors: [error],
|
|
5692
|
+
});
|
|
5529
5693
|
}
|
|
5530
5694
|
finally {
|
|
5531
5695
|
if (!isJokerAttempt &&
|
|
@@ -6399,15 +6563,74 @@ function createPipelineExecutor(options) {
|
|
|
6399
6563
|
});
|
|
6400
6564
|
});
|
|
6401
6565
|
};
|
|
6402
|
-
const pipelineExecutor = (inputParameters) =>
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6566
|
+
const pipelineExecutor = (inputParameters) => {
|
|
6567
|
+
const startTime = new Date().getTime();
|
|
6568
|
+
return createTask({
|
|
6569
|
+
taskType: 'EXECUTION',
|
|
6570
|
+
title: pipeline.title,
|
|
6571
|
+
taskProcessCallback(updateOngoingResult, updateTldr) {
|
|
6572
|
+
return pipelineExecutorWithCallback(inputParameters, async (newOngoingResult) => {
|
|
6573
|
+
var _a, _b;
|
|
6574
|
+
updateOngoingResult(newOngoingResult);
|
|
6575
|
+
// Calculate and update tldr based on pipeline progress
|
|
6576
|
+
const cv = newOngoingResult;
|
|
6577
|
+
// Calculate progress based on parameters resolved vs total parameters
|
|
6578
|
+
const totalParameters = pipeline.parameters.filter(p => !p.isInput).length;
|
|
6579
|
+
let resolvedParameters = 0;
|
|
6580
|
+
let currentTaskTitle = '';
|
|
6581
|
+
// Get the resolved parameters from output parameters
|
|
6582
|
+
if (cv === null || cv === void 0 ? void 0 : cv.outputParameters) {
|
|
6583
|
+
// Count how many output parameters have non-empty values
|
|
6584
|
+
resolvedParameters = Object.values(cv.outputParameters).filter(value => value !== undefined && value !== null && String(value).trim() !== '').length;
|
|
6585
|
+
}
|
|
6586
|
+
// Try to determine current task from execution report
|
|
6587
|
+
if (((_a = cv === null || cv === void 0 ? void 0 : cv.executionReport) === null || _a === void 0 ? void 0 : _a.promptExecutions) && cv.executionReport.promptExecutions.length > 0) {
|
|
6588
|
+
const lastExecution = cv.executionReport.promptExecutions[cv.executionReport.promptExecutions.length - 1];
|
|
6589
|
+
if ((_b = lastExecution === null || lastExecution === void 0 ? void 0 : lastExecution.prompt) === null || _b === void 0 ? void 0 : _b.title) {
|
|
6590
|
+
currentTaskTitle = lastExecution.prompt.title;
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
// Calculate base progress percentage
|
|
6594
|
+
let percent = totalParameters > 0 ? resolvedParameters / totalParameters : 0;
|
|
6595
|
+
// Add time-based progress for current task if we haven't completed all parameters
|
|
6596
|
+
if (resolvedParameters < totalParameters) {
|
|
6597
|
+
const elapsedMs = new Date().getTime() - startTime;
|
|
6598
|
+
const estimatedTotalMs = totalParameters * 30 * 1000; // Estimate 30 seconds per parameter
|
|
6599
|
+
const timeProgress = Math.min(elapsedMs / estimatedTotalMs, 0.9); // Cap at 90% for time-based progress
|
|
6600
|
+
// If we have time progress but no parameter progress, show time progress
|
|
6601
|
+
if (percent === 0 && timeProgress > 0) {
|
|
6602
|
+
percent = Math.min(timeProgress, 0.1); // Show some progress but not more than 10%
|
|
6603
|
+
}
|
|
6604
|
+
else if (percent < 1) {
|
|
6605
|
+
// Add partial progress for current task
|
|
6606
|
+
const taskProgress = totalParameters > 0 ? (1 / totalParameters) * 0.5 : 0; // 50% of task progress
|
|
6607
|
+
percent = Math.min(percent + taskProgress, 0.95); // Cap at 95% until fully complete
|
|
6608
|
+
}
|
|
6609
|
+
}
|
|
6610
|
+
// Clamp to [0,1]
|
|
6611
|
+
percent = Math.min(Math.max(percent, 0), 1);
|
|
6612
|
+
// Generate message
|
|
6613
|
+
let message = '';
|
|
6614
|
+
if (currentTaskTitle) {
|
|
6615
|
+
message = `Executing: ${currentTaskTitle}`;
|
|
6616
|
+
}
|
|
6617
|
+
else if (resolvedParameters === 0) {
|
|
6618
|
+
message = 'Starting pipeline execution';
|
|
6619
|
+
}
|
|
6620
|
+
else if (resolvedParameters < totalParameters) {
|
|
6621
|
+
message = `Processing pipeline (${resolvedParameters}/${totalParameters} parameters resolved)`;
|
|
6622
|
+
}
|
|
6623
|
+
else {
|
|
6624
|
+
message = 'Completing pipeline execution';
|
|
6625
|
+
}
|
|
6626
|
+
updateTldr({
|
|
6627
|
+
percent: percent,
|
|
6628
|
+
message,
|
|
6629
|
+
});
|
|
6630
|
+
});
|
|
6631
|
+
},
|
|
6632
|
+
});
|
|
6633
|
+
};
|
|
6411
6634
|
// <- TODO: Make types such as there is no need to do `as` for `createTask`
|
|
6412
6635
|
return pipelineExecutor;
|
|
6413
6636
|
}
|
|
@@ -7014,31 +7237,23 @@ function extractBlock(markdown) {
|
|
|
7014
7237
|
return content;
|
|
7015
7238
|
}
|
|
7016
7239
|
|
|
7240
|
+
/**
|
|
7241
|
+
* @private internal for `preserve`
|
|
7242
|
+
*/
|
|
7243
|
+
const _preserved = [];
|
|
7017
7244
|
/**
|
|
7018
7245
|
* Does nothing, but preserves the function in the bundle
|
|
7019
7246
|
* Compiler is tricked into thinking the function is used
|
|
7020
7247
|
*
|
|
7021
7248
|
* @param value any function to preserve
|
|
7022
7249
|
* @returns nothing
|
|
7023
|
-
* @private
|
|
7024
|
-
*/
|
|
7025
|
-
function preserve(
|
|
7026
|
-
|
|
7027
|
-
(async () => {
|
|
7028
|
-
// TODO: [💩] Change to `await forEver` or `forTime(Infinity)`
|
|
7029
|
-
await forTime(100000000);
|
|
7030
|
-
// [1]
|
|
7031
|
-
try {
|
|
7032
|
-
await func();
|
|
7033
|
-
}
|
|
7034
|
-
finally {
|
|
7035
|
-
// do nothing
|
|
7036
|
-
}
|
|
7037
|
-
})();
|
|
7250
|
+
* @private within the repository
|
|
7251
|
+
*/
|
|
7252
|
+
function $preserve(...value) {
|
|
7253
|
+
_preserved.push(...value);
|
|
7038
7254
|
}
|
|
7039
7255
|
/**
|
|
7040
|
-
*
|
|
7041
|
-
* TODO: [1] This maybe does memory leak
|
|
7256
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
7042
7257
|
*/
|
|
7043
7258
|
|
|
7044
7259
|
// Note: [💎]
|
|
@@ -7066,25 +7281,25 @@ class JavascriptEvalExecutionTools {
|
|
|
7066
7281
|
// Note: [💎]
|
|
7067
7282
|
// Note: Using direct eval, following variables are in same scope as eval call so they are accessible from inside the evaluated script:
|
|
7068
7283
|
const spaceTrim$1 = (_) => spaceTrim(_);
|
|
7069
|
-
preserve(spaceTrim$1);
|
|
7284
|
+
$preserve(spaceTrim$1);
|
|
7070
7285
|
const removeQuotes$1 = removeQuotes;
|
|
7071
|
-
preserve(removeQuotes$1);
|
|
7286
|
+
$preserve(removeQuotes$1);
|
|
7072
7287
|
const unwrapResult$1 = unwrapResult;
|
|
7073
|
-
preserve(unwrapResult$1);
|
|
7288
|
+
$preserve(unwrapResult$1);
|
|
7074
7289
|
const trimEndOfCodeBlock$1 = trimEndOfCodeBlock;
|
|
7075
|
-
preserve(trimEndOfCodeBlock$1);
|
|
7290
|
+
$preserve(trimEndOfCodeBlock$1);
|
|
7076
7291
|
const trimCodeBlock$1 = trimCodeBlock;
|
|
7077
|
-
preserve(trimCodeBlock$1);
|
|
7292
|
+
$preserve(trimCodeBlock$1);
|
|
7078
7293
|
// TODO: DRY [🍯]
|
|
7079
7294
|
const trim = (str) => str.trim();
|
|
7080
|
-
preserve(trim);
|
|
7295
|
+
$preserve(trim);
|
|
7081
7296
|
// TODO: DRY [🍯]
|
|
7082
7297
|
const reverse = (str) => str.split('').reverse().join('');
|
|
7083
|
-
preserve(reverse);
|
|
7298
|
+
$preserve(reverse);
|
|
7084
7299
|
const removeEmojis$1 = removeEmojis;
|
|
7085
|
-
preserve(removeEmojis$1);
|
|
7300
|
+
$preserve(removeEmojis$1);
|
|
7086
7301
|
const prettifyMarkdown$1 = prettifyMarkdown;
|
|
7087
|
-
preserve(prettifyMarkdown$1);
|
|
7302
|
+
$preserve(prettifyMarkdown$1);
|
|
7088
7303
|
//-------[n12:]---
|
|
7089
7304
|
const capitalize$1 = capitalize;
|
|
7090
7305
|
const decapitalize$1 = decapitalize;
|
|
@@ -7100,18 +7315,18 @@ class JavascriptEvalExecutionTools {
|
|
|
7100
7315
|
// TODO: DRY [🍯]
|
|
7101
7316
|
Array.from(parseKeywordsFromString(input)).join(', '); /* <- TODO: [🧠] What is the best format comma list, bullet list,...? */
|
|
7102
7317
|
const normalizeTo_SCREAMING_CASE$1 = normalizeTo_SCREAMING_CASE;
|
|
7103
|
-
preserve(capitalize$1);
|
|
7104
|
-
preserve(decapitalize$1);
|
|
7105
|
-
preserve(nameToUriPart$1);
|
|
7106
|
-
preserve(nameToUriParts$1);
|
|
7107
|
-
preserve(removeDiacritics$1);
|
|
7108
|
-
preserve(normalizeWhitespaces$1);
|
|
7109
|
-
preserve(normalizeToKebabCase$1);
|
|
7110
|
-
preserve(normalizeTo_camelCase$1);
|
|
7111
|
-
preserve(normalizeTo_snake_case$1);
|
|
7112
|
-
preserve(normalizeTo_PascalCase$1);
|
|
7113
|
-
preserve(parseKeywords);
|
|
7114
|
-
preserve(normalizeTo_SCREAMING_CASE$1);
|
|
7318
|
+
$preserve(capitalize$1);
|
|
7319
|
+
$preserve(decapitalize$1);
|
|
7320
|
+
$preserve(nameToUriPart$1);
|
|
7321
|
+
$preserve(nameToUriParts$1);
|
|
7322
|
+
$preserve(removeDiacritics$1);
|
|
7323
|
+
$preserve(normalizeWhitespaces$1);
|
|
7324
|
+
$preserve(normalizeToKebabCase$1);
|
|
7325
|
+
$preserve(normalizeTo_camelCase$1);
|
|
7326
|
+
$preserve(normalizeTo_snake_case$1);
|
|
7327
|
+
$preserve(normalizeTo_PascalCase$1);
|
|
7328
|
+
$preserve(parseKeywords);
|
|
7329
|
+
$preserve(normalizeTo_SCREAMING_CASE$1);
|
|
7115
7330
|
//-------[/n12]---
|
|
7116
7331
|
if (!script.includes('return')) {
|
|
7117
7332
|
script = `return ${script}`;
|
|
@@ -8144,7 +8359,7 @@ function startRemoteServer(options) {
|
|
|
8144
8359
|
});
|
|
8145
8360
|
function exportExecutionTask(executionTask, isFull) {
|
|
8146
8361
|
// <- TODO: [🧠] This should be maybe method of `ExecutionTask` itself
|
|
8147
|
-
const { taskType, promptbookVersion, taskId, title, status, errors, warnings, createdAt, updatedAt, currentValue, } = executionTask;
|
|
8362
|
+
const { taskType, promptbookVersion, taskId, title, status, errors, tldr, warnings, createdAt, updatedAt, currentValue, } = executionTask;
|
|
8148
8363
|
if (isFull) {
|
|
8149
8364
|
return {
|
|
8150
8365
|
taskId,
|
|
@@ -8152,6 +8367,7 @@ function startRemoteServer(options) {
|
|
|
8152
8367
|
taskType,
|
|
8153
8368
|
promptbookVersion,
|
|
8154
8369
|
status,
|
|
8370
|
+
tldr,
|
|
8155
8371
|
errors: errors.map(serializeError),
|
|
8156
8372
|
warnings: warnings.map(serializeError),
|
|
8157
8373
|
createdAt,
|
|
@@ -8166,6 +8382,7 @@ function startRemoteServer(options) {
|
|
|
8166
8382
|
taskType,
|
|
8167
8383
|
promptbookVersion,
|
|
8168
8384
|
status,
|
|
8385
|
+
tldr,
|
|
8169
8386
|
createdAt,
|
|
8170
8387
|
updatedAt,
|
|
8171
8388
|
};
|