@promptbook/node 0.92.0-5 → 0.92.0-8

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/umd/index.umd.js CHANGED
@@ -46,7 +46,7 @@
46
46
  * @generated
47
47
  * @see https://github.com/webgptorg/promptbook
48
48
  */
49
- const PROMPTBOOK_ENGINE_VERSION = '0.92.0-5';
49
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-8';
50
50
  /**
51
51
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
52
52
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -315,6 +315,45 @@
315
315
  }
316
316
  }
317
317
 
318
+ /**
319
+ * Converts a JavaScript Object Notation (JSON) string into an object.
320
+ *
321
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
322
+ *
323
+ * @public exported from `@promptbook/utils`
324
+ */
325
+ function jsonParse(value) {
326
+ if (value === undefined) {
327
+ throw new Error(`Can not parse JSON from undefined value.`);
328
+ }
329
+ else if (typeof value !== 'string') {
330
+ console.error('Can not parse JSON from non-string value.', { text: value });
331
+ throw new Error(spaceTrim__default["default"](`
332
+ Can not parse JSON from non-string value.
333
+
334
+ The value type: ${typeof value}
335
+ See more in console.
336
+ `));
337
+ }
338
+ try {
339
+ return JSON.parse(value);
340
+ }
341
+ catch (error) {
342
+ if (!(error instanceof Error)) {
343
+ throw error;
344
+ }
345
+ throw new Error(spaceTrim__default["default"]((block) => `
346
+ ${block(error.message)}
347
+
348
+ The JSON text:
349
+ ${block(value)}
350
+ `));
351
+ }
352
+ }
353
+ /**
354
+ * TODO: !!!! Use in Promptbook.studio
355
+ */
356
+
318
357
  /**
319
358
  * Orders JSON object by keys
320
359
  *
@@ -1104,7 +1143,7 @@
1104
1143
  if (!indexFile) {
1105
1144
  throw new UnexpectedError(`Archive does not contain 'index.book.json' file`);
1106
1145
  }
1107
- const collectionJson = JSON.parse(await indexFile.async('text'));
1146
+ const collectionJson = jsonParse(await indexFile.async('text'));
1108
1147
  for (const pipeline of collectionJson) {
1109
1148
  validatePipeline(pipeline);
1110
1149
  }
@@ -1698,7 +1737,7 @@
1698
1737
  const newObject = { ...object };
1699
1738
  for (const [key, value] of Object.entries(object)) {
1700
1739
  if (typeof value === 'string' && isValidJsonString(value)) {
1701
- newObject[key] = JSON.parse(value);
1740
+ newObject[key] = jsonParse(value);
1702
1741
  }
1703
1742
  else {
1704
1743
  newObject[key] = jsonStringsToJsons(value);
@@ -4092,13 +4131,79 @@
4092
4131
  /**
4093
4132
  * @@@
4094
4133
  *
4134
+ * Here is the place where RAG (retrieval-augmented generation) happens
4135
+ *
4095
4136
  * @private internal utility of `createPipelineExecutor`
4096
4137
  */
4097
4138
  async function getKnowledgeForTask(options) {
4098
- const { preparedPipeline, task } = options;
4099
- return preparedPipeline.knowledgePieces.map(({ content }) => `- ${content}`).join('\n');
4139
+ const { tools, preparedPipeline, task } = options;
4140
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
4141
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
4142
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
4143
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
4144
+ return 'No knowledge pieces found';
4145
+ }
4146
+ // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
4147
+ const _llms = arrayableToArray(tools.llm);
4148
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
4149
+ const taskEmbeddingPrompt = {
4150
+ title: 'Knowledge Search',
4151
+ modelRequirements: {
4152
+ modelVariant: 'EMBEDDING',
4153
+ modelName: firstKnowlegeIndex.modelName,
4154
+ },
4155
+ content: task.content,
4156
+ parameters: {
4157
+ /* !!!!!!!! */
4158
+ },
4159
+ };
4160
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
4161
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
4162
+ const { index } = knowledgePiece;
4163
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
4164
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
4165
+ if (knowledgePieceIndex === undefined) {
4166
+ return {
4167
+ content: knowledgePiece.content,
4168
+ relevance: 0,
4169
+ };
4170
+ }
4171
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
4172
+ return {
4173
+ content: knowledgePiece.content,
4174
+ relevance,
4175
+ };
4176
+ });
4177
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
4178
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
4179
+ console.log('!!! Embedding', {
4180
+ task,
4181
+ taskEmbeddingPrompt,
4182
+ taskEmbeddingResult,
4183
+ firstKnowlegePiece,
4184
+ firstKnowlegeIndex,
4185
+ knowledgePiecesWithRelevance,
4186
+ knowledgePiecesSorted,
4187
+ knowledgePiecesLimited,
4188
+ });
4189
+ return knowledgePiecesLimited.map(({ content }) => `- ${content}`).join('\n');
4100
4190
  // <- TODO: [🧠] Some smart aggregation of knowledge pieces, single-line vs multi-line vs mixed
4101
4191
  }
4192
+ // TODO: !!!!!! Annotate + to new file
4193
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
4194
+ if (embeddingVector1.length !== embeddingVector2.length) {
4195
+ throw new TypeError('Embedding vectors must have the same length');
4196
+ }
4197
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
4198
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
4199
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
4200
+ return 1 - dotProduct / (magnitude1 * magnitude2);
4201
+ }
4202
+ /**
4203
+ * TODO: !!!! Verify if this is working
4204
+ * TODO: [♨] Implement Better - use keyword search
4205
+ * TODO: [♨] Examples of values
4206
+ */
4102
4207
 
4103
4208
  /**
4104
4209
  * @@@
@@ -4106,9 +4211,9 @@
4106
4211
  * @private internal utility of `createPipelineExecutor`
4107
4212
  */
4108
4213
  async function getReservedParametersForTask(options) {
4109
- const { preparedPipeline, task, pipelineIdentification } = options;
4214
+ const { tools, preparedPipeline, task, pipelineIdentification } = options;
4110
4215
  const context = await getContextForTask(); // <- [🏍]
4111
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
4216
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task });
4112
4217
  const examples = await getExamplesForTask();
4113
4218
  const currentDate = new Date().toISOString(); // <- TODO: [🧠][💩] Better
4114
4219
  const modelName = RESERVED_PARAMETER_MISSING_VALUE;
@@ -4170,6 +4275,7 @@
4170
4275
  }
4171
4276
  const definedParameters = Object.freeze({
4172
4277
  ...(await getReservedParametersForTask({
4278
+ tools,
4173
4279
  preparedPipeline,
4174
4280
  task: currentTask,
4175
4281
  pipelineIdentification,
@@ -4741,18 +4847,26 @@
4741
4847
  }).asPromise();
4742
4848
  const { outputParameters } = result;
4743
4849
  const { modelsRequirements: modelsRequirementsJson } = outputParameters;
4744
- const modelsRequirementsUnchecked = JSON.parse(modelsRequirementsJson);
4850
+ let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
4745
4851
  if (isVerbose) {
4746
4852
  console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
4747
4853
  }
4748
4854
  if (!Array.isArray(modelsRequirementsUnchecked)) {
4749
- throw new UnexpectedError(spaceTrim__default["default"]((block) => `
4750
- Invalid \`modelsRequirements\`:
4855
+ // <- TODO: Book should have syntax and system to enforce shape of JSON
4856
+ modelsRequirementsUnchecked = [modelsRequirementsUnchecked];
4857
+ /*
4858
+ throw new UnexpectedError(
4859
+ spaceTrim(
4860
+ (block) => `
4861
+ Invalid \`modelsRequirements\`:
4751
4862
 
4752
- \`\`\`json
4753
- ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
4754
- \`\`\`
4755
- `));
4863
+ \`\`\`json
4864
+ ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
4865
+ \`\`\`
4866
+ `,
4867
+ ),
4868
+ );
4869
+ */
4756
4870
  }
4757
4871
  const modelsRequirements = modelsRequirementsUnchecked.map((modelRequirements) => ({
4758
4872
  modelVariant: 'CHAT',
@@ -5322,7 +5436,7 @@
5322
5436
  > },
5323
5437
  */
5324
5438
  async asJson() {
5325
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
5439
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
5326
5440
  },
5327
5441
  async asText() {
5328
5442
  return await tools.fs.readFile(filename, 'utf-8');
@@ -10842,7 +10956,7 @@
10842
10956
  return null;
10843
10957
  }
10844
10958
  const fileContent = await promises.readFile(filename, 'utf-8');
10845
- const value = JSON.parse(fileContent);
10959
+ const value = jsonParse(fileContent);
10846
10960
  // TODO: [🌗]
10847
10961
  return value;
10848
10962
  }