@promptbook/node 0.92.0-5 → 0.92.0-6

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/esm/index.es.js CHANGED
@@ -30,7 +30,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
30
30
  * @generated
31
31
  * @see https://github.com/webgptorg/promptbook
32
32
  */
33
- const PROMPTBOOK_ENGINE_VERSION = '0.92.0-5';
33
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-6';
34
34
  /**
35
35
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
36
36
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -299,6 +299,45 @@ class UnexpectedError extends Error {
299
299
  }
300
300
  }
301
301
 
302
+ /**
303
+ * Converts a JavaScript Object Notation (JSON) string into an object.
304
+ *
305
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
306
+ *
307
+ * @public exported from `@promptbook/utils`
308
+ */
309
+ function jsonParse(value) {
310
+ if (value === undefined) {
311
+ throw new Error(`Can not parse JSON from undefined value.`);
312
+ }
313
+ else if (typeof value !== 'string') {
314
+ console.error('Can not parse JSON from non-string value.', { text: value });
315
+ throw new Error(spaceTrim(`
316
+ Can not parse JSON from non-string value.
317
+
318
+ The value type: ${typeof value}
319
+ See more in console.
320
+ `));
321
+ }
322
+ try {
323
+ return JSON.parse(value);
324
+ }
325
+ catch (error) {
326
+ if (!(error instanceof Error)) {
327
+ throw error;
328
+ }
329
+ throw new Error(spaceTrim((block) => `
330
+ ${block(error.message)}
331
+
332
+ The JSON text:
333
+ ${block(value)}
334
+ `));
335
+ }
336
+ }
337
+ /**
338
+ * TODO: !!!! Use in Promptbook.studio
339
+ */
340
+
302
341
  /**
303
342
  * Orders JSON object by keys
304
343
  *
@@ -1088,7 +1127,7 @@ async function loadArchive(filePath, fs) {
1088
1127
  if (!indexFile) {
1089
1128
  throw new UnexpectedError(`Archive does not contain 'index.book.json' file`);
1090
1129
  }
1091
- const collectionJson = JSON.parse(await indexFile.async('text'));
1130
+ const collectionJson = jsonParse(await indexFile.async('text'));
1092
1131
  for (const pipeline of collectionJson) {
1093
1132
  validatePipeline(pipeline);
1094
1133
  }
@@ -1682,7 +1721,7 @@ function jsonStringsToJsons(object) {
1682
1721
  const newObject = { ...object };
1683
1722
  for (const [key, value] of Object.entries(object)) {
1684
1723
  if (typeof value === 'string' && isValidJsonString(value)) {
1685
- newObject[key] = JSON.parse(value);
1724
+ newObject[key] = jsonParse(value);
1686
1725
  }
1687
1726
  else {
1688
1727
  newObject[key] = jsonStringsToJsons(value);
@@ -4076,13 +4115,79 @@ async function getExamplesForTask(task) {
4076
4115
  /**
4077
4116
  * @@@
4078
4117
  *
4118
+ * Here is the place where RAG (retrieval-augmented generation) happens
4119
+ *
4079
4120
  * @private internal utility of `createPipelineExecutor`
4080
4121
  */
4081
4122
  async function getKnowledgeForTask(options) {
4082
- const { preparedPipeline, task } = options;
4083
- return preparedPipeline.knowledgePieces.map(({ content }) => `- ${content}`).join('\n');
4123
+ const { tools, preparedPipeline, task } = options;
4124
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
4125
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
4126
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
4127
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
4128
+ return 'No knowledge pieces found';
4129
+ }
4130
+ // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
4131
+ const _llms = arrayableToArray(tools.llm);
4132
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
4133
+ const taskEmbeddingPrompt = {
4134
+ title: 'Knowledge Search',
4135
+ modelRequirements: {
4136
+ modelVariant: 'EMBEDDING',
4137
+ modelName: firstKnowlegeIndex.modelName,
4138
+ },
4139
+ content: task.content,
4140
+ parameters: {
4141
+ /* !!!!!!!! */
4142
+ },
4143
+ };
4144
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
4145
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
4146
+ const { index } = knowledgePiece;
4147
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
4148
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
4149
+ if (knowledgePieceIndex === undefined) {
4150
+ return {
4151
+ content: knowledgePiece.content,
4152
+ relevance: 0,
4153
+ };
4154
+ }
4155
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
4156
+ return {
4157
+ content: knowledgePiece.content,
4158
+ relevance,
4159
+ };
4160
+ });
4161
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
4162
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
4163
+ console.log('!!! Embedding', {
4164
+ task,
4165
+ taskEmbeddingPrompt,
4166
+ taskEmbeddingResult,
4167
+ firstKnowlegePiece,
4168
+ firstKnowlegeIndex,
4169
+ knowledgePiecesWithRelevance,
4170
+ knowledgePiecesSorted,
4171
+ knowledgePiecesLimited,
4172
+ });
4173
+ return knowledgePiecesLimited.map(({ content }) => `- ${content}`).join('\n');
4084
4174
  // <- TODO: [🧠] Some smart aggregation of knowledge pieces, single-line vs multi-line vs mixed
4085
4175
  }
4176
+ // TODO: !!!!!! Annotate + to new file
4177
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
4178
+ if (embeddingVector1.length !== embeddingVector2.length) {
4179
+ throw new TypeError('Embedding vectors must have the same length');
4180
+ }
4181
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
4182
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
4183
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
4184
+ return 1 - dotProduct / (magnitude1 * magnitude2);
4185
+ }
4186
+ /**
4187
+ * TODO: !!!! Verify if this is working
4188
+ * TODO: [♨] Implement Better - use keyword search
4189
+ * TODO: [♨] Examples of values
4190
+ */
4086
4191
 
4087
4192
  /**
4088
4193
  * @@@
@@ -4090,9 +4195,9 @@ async function getKnowledgeForTask(options) {
4090
4195
  * @private internal utility of `createPipelineExecutor`
4091
4196
  */
4092
4197
  async function getReservedParametersForTask(options) {
4093
- const { preparedPipeline, task, pipelineIdentification } = options;
4198
+ const { tools, preparedPipeline, task, pipelineIdentification } = options;
4094
4199
  const context = await getContextForTask(); // <- [🏍]
4095
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
4200
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task });
4096
4201
  const examples = await getExamplesForTask();
4097
4202
  const currentDate = new Date().toISOString(); // <- TODO: [🧠][💩] Better
4098
4203
  const modelName = RESERVED_PARAMETER_MISSING_VALUE;
@@ -4154,6 +4259,7 @@ async function executeTask(options) {
4154
4259
  }
4155
4260
  const definedParameters = Object.freeze({
4156
4261
  ...(await getReservedParametersForTask({
4262
+ tools,
4157
4263
  preparedPipeline,
4158
4264
  task: currentTask,
4159
4265
  pipelineIdentification,
@@ -4725,7 +4831,7 @@ async function preparePersona(personaDescription, tools, options) {
4725
4831
  }).asPromise();
4726
4832
  const { outputParameters } = result;
4727
4833
  const { modelsRequirements: modelsRequirementsJson } = outputParameters;
4728
- const modelsRequirementsUnchecked = JSON.parse(modelsRequirementsJson);
4834
+ const modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
4729
4835
  if (isVerbose) {
4730
4836
  console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
4731
4837
  }
@@ -5306,7 +5412,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5306
5412
  > },
5307
5413
  */
5308
5414
  async asJson() {
5309
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
5415
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
5310
5416
  },
5311
5417
  async asText() {
5312
5418
  return await tools.fs.readFile(filename, 'utf-8');
@@ -10826,7 +10932,7 @@ class FileCacheStorage {
10826
10932
  return null;
10827
10933
  }
10828
10934
  const fileContent = await readFile(filename, 'utf-8');
10829
- const value = JSON.parse(fileContent);
10935
+ const value = jsonParse(fileContent);
10830
10936
  // TODO: [🌗]
10831
10937
  return value;
10832
10938
  }