@fragola-ai/prompt 1.0.3 → 1.0.4

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/dist/Prompt.js ADDED
@@ -0,0 +1,169 @@
1
+ import { readFileSync } from "fs";
2
+ import path from "path";
3
+ class MissingVariablesError extends Error {
4
+ constructor(variables, prompt) {
5
+ super(`Missing variables: ${variables.map((v) => `'${v}'`).join(", ")}. For prompt: ${prompt}`);
6
+ this.variables = variables;
7
+ this.name = "MissingVariablesError";
8
+ }
9
+ }
10
+ class NamespaceUndefinedError extends Error {
11
+ constructor(namespace) {
12
+ super(`Tried to acess undefined namespace when using load() function: '${namespace}' does not exist`);
13
+ this.namespace = namespace;
14
+ this.name = "NamespaceUndefinedError";
15
+ }
16
+ }
17
+ class LoadFileReadError extends Error {
18
+ constructor(filePath, originalError) {
19
+ super(`Failed to load prompt from ${filePath}: ${originalError.message}`);
20
+ this.filePath = filePath;
21
+ this.originalError = originalError;
22
+ this.name = "LoadFileReadError";
23
+ }
24
+ }
25
+ const load = (relativePath) => ({ load: true, relativePath });
26
+ class Prompt {
27
+ static defaultVariableRegex = /\{\{(\s*[\w.]+\s*)\}\}/g;
28
+ /**
29
+ * A map of aliases to file paths for use with the {@link load} function.
30
+ * Allows defining shortcuts for commonly used prompt file paths.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * Prompt.pathAlias = {
35
+ * browse: "prompts", // Maps "@browse" to "prompts/"
36
+ * refund: "prompts/refund" // Maps "@refund" to "prompts/refund/"
37
+ * }
38
+ *
39
+ * // Usage with load():
40
+ * load("@refund/shopRefund.md") // Resolves to "prompts/refund/shopRefund.md"
41
+ * ```
42
+ * @see {@link load}
43
+ */
44
+ static pathAlias = {};
45
+ #originalPrompt = "";
46
+ #value = "";
47
+ #variableRegex = Prompt.defaultVariableRegex;
48
+ #variables = {};
49
+ #basePath = process.env["PWD"];
50
+ constructor(prompt, variables = {}) {
51
+ if (!process.env["PWD"])
52
+ console.warn("PWD env variable not set");
53
+ if (typeof prompt == "string") {
54
+ this.#originalPrompt = prompt;
55
+ this.#value = prompt;
56
+ } else {
57
+ this.#originalPrompt = this.#load(prompt.relativePath);
58
+ this.#value = this.#originalPrompt;
59
+ }
60
+ this.setVariables(variables);
61
+ }
62
+ #stringify(value) {
63
+ switch (typeof value) {
64
+ case "object":
65
+ return JSON.stringify(value);
66
+ default:
67
+ return value;
68
+ }
69
+ }
70
+ #isInsideCodeBlock(index, text) {
71
+ const codeBlockRegex = /```[\s\S]*?```/g;
72
+ let match;
73
+ while ((match = codeBlockRegex.exec(text)) !== null) {
74
+ const start = match.index;
75
+ const end = start + match[0].length;
76
+ if (index >= start && index < end) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ /**
83
+ * Updates the variables used for string interpolation and re-processes the prompt template.
84
+ *
85
+ * @param variables - Your new variables
86
+ * @throws {MissingVariablesError} When required variables are not provided in the variables object
87
+ */
88
+ setVariables(variables) {
89
+ this.#variables = variables;
90
+ const allMatch = Array.from(this.#originalPrompt.matchAll(this.#variableRegex)).filter((match) => !this.#isInsideCodeBlock(match.index, this.#originalPrompt)).map((match) => {
91
+ return {
92
+ placeholder: match[0],
93
+ index: match.index
94
+ };
95
+ });
96
+ const getVariableNameFromPlaceholder = (placeholder) => {
97
+ return placeholder.replaceAll(/[{}]/g, "").trim();
98
+ };
99
+ let missingVariables = [];
100
+ let applyReplace = [];
101
+ allMatch.forEach((match) => {
102
+ const variableName = getVariableNameFromPlaceholder(match.placeholder);
103
+ if (variableName in variables)
104
+ applyReplace.push(() => this.#value = this.#value.replace(match.placeholder, this.#stringify(this.#variables[variableName])));
105
+ else
106
+ missingVariables.push(variableName);
107
+ });
108
+ if (missingVariables.length)
109
+ throw new MissingVariablesError(missingVariables, this.#originalPrompt);
110
+ applyReplace.forEach((replace) => replace());
111
+ }
112
+ #load(relativePath) {
113
+ let _relativePath = (() => {
114
+ const split = relativePath.split("/");
115
+ if (split.length > 1) {
116
+ const namespace = split[0].trim();
117
+ if (namespace[0] == "@") {
118
+ const name = split[0].slice(1);
119
+ if (name in Prompt.pathAlias) {
120
+ return path.join(Prompt.pathAlias[name], split[1]);
121
+ } else
122
+ throw new NamespaceUndefinedError(namespace);
123
+ }
124
+ }
125
+ return relativePath;
126
+ })();
127
+ let fullPath = path.join(this.#basePath || "./", _relativePath);
128
+ if (!fullPath.split("/").at(-1)?.includes("."))
129
+ fullPath = `${fullPath}.md`;
130
+ try {
131
+ const content = readFileSync(fullPath, "utf-8");
132
+ return content;
133
+ } catch (error) {
134
+ if (error instanceof Error) {
135
+ throw new LoadFileReadError(fullPath, error);
136
+ }
137
+ throw error;
138
+ }
139
+ }
140
+ /**
141
+ * Gets the original prompt string used for this instance. Before any processing
142
+ * @returns {string} The original prompt string
143
+ */
144
+ get promptString() {
145
+ return this.#originalPrompt;
146
+ }
147
+ /**
148
+ * Gets the variables currently in use for this prompt.
149
+ * @returns {Map<string, any>} A map containing the prompt variables and their values.
150
+ */
151
+ get variables() {
152
+ return this.#variables;
153
+ }
154
+ /**
155
+ * Gets the interpolated value of the prompt.
156
+ * @returns {string} The resulting string after all variable interpolation has been applied.
157
+ */
158
+ get value() {
159
+ return this.#value;
160
+ }
161
+ }
162
+ export {
163
+ LoadFileReadError,
164
+ MissingVariablesError,
165
+ NamespaceUndefinedError,
166
+ Prompt as default,
167
+ load
168
+ };
169
+ //# sourceMappingURL=Prompt.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../Prompt.ts"],
4
+ "sourcesContent": ["import { readFileSync } from \"fs\";\nimport path from \"path\";\n\n/**\n * Represents a record of variables used for string interpolation in prompts.\n * Each key in the record is a variable name to interpolate in the prompt template,\n * and its value can be any type (object, array, number, string, etc.) which will replace the placeholder.\n */\nexport type Variables = Record<string, any>;\n\nexport class MissingVariablesError extends Error {\n constructor(public variables: string[], prompt: string) {\n super(`Missing variables: ${variables.map(v => `'${v}'`).join(', ')}. For prompt: ${prompt}`);\n this.name = 'MissingVariablesError';\n }\n}\n\nexport class NamespaceUndefinedError extends Error {\n constructor(public namespace: string) {\n super(`Tried to acess undefined namespace when using load() function: '${namespace}' does not exist`)\n this.name = \"NamespaceUndefinedError\";\n }\n}\n\nexport class LoadFileReadError extends Error {\n constructor(public filePath: string, public originalError: Error) {\n super(`Failed to load prompt from ${filePath}: ${originalError.message}`);\n this.name = \"LoadFileReadError\";\n }\n}\n\nexport type Load = { load: true, relativePath: string };\n\n/**\n * Creates a Load object to specify a prompt file location.\n * This function is used when the prompt is located in a file system.\n * The process working directory (PWD) is used as the base path by default.\n * Path aliases can be used to reduce path overhead.\n * \n * @param {string} relativePath - The relative path to the prompt file. Can include path aliases.\n * @returns {Load} An object containing the load flag and the relative path to the prompt file\n * \n * @example\n * // Using relative path\n * load('./prompts/myPrompt.txt')\n * \n * @example\n * // Using path alias\n * load('prompts:myPrompt.txt')\n */\nexport const load = (relativePath: string): Load => ({ load: true, relativePath });\n\nexport default class Prompt {\n private static defaultVariableRegex = /\\{\\{(\\s*[\\w.]+\\s*)\\}\\}/g;\n /**\n * A map of aliases to file paths for use with the {@link load} function.\n * Allows defining shortcuts for commonly used prompt file paths.\n * \n * @example\n * ```typescript\n * Prompt.pathAlias = {\n * browse: \"prompts\", // Maps \"@browse\" to \"prompts/\"\n * refund: \"prompts/refund\" // Maps \"@refund\" to \"prompts/refund/\"\n * }\n * \n * // Usage with load():\n * load(\"@refund/shopRefund.md\") // Resolves to \"prompts/refund/shopRefund.md\"\n * ```\n * @see {@link load}\n */\n public static pathAlias: Record<string, string> = {};\n\n #originalPrompt: string = \"\";\n #value: string = \"\";\n #variableRegex: RegExp = Prompt.defaultVariableRegex;\n #variables: Variables = {};\n #basePath = process.env[\"PWD\"];\n\n constructor(prompt: string | Load, variables: Variables = {}) {\n if (!process.env[\"PWD\"])\n console.warn(\"PWD env variable not set\");\n if (typeof prompt == 'string') {\n this.#originalPrompt = prompt;\n this.#value = prompt;\n } else {\n this.#originalPrompt = this.#load(prompt.relativePath);\n this.#value = this.#originalPrompt;\n }\n this.setVariables(variables);\n }\n\n #stringify(value: any) {\n switch (typeof value) {\n case \"object\":\n return JSON.stringify(value);\n default:\n return value;\n }\n }\n\n #isInsideCodeBlock(index: number, text: string): boolean {\n const codeBlockRegex = /```[\\s\\S]*?```/g;\n let match;\n\n while ((match = codeBlockRegex.exec(text)) !== null) {\n const start = match.index;\n const end = start + match[0].length;\n\n if (index >= start && index < end) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Updates the variables used for string interpolation and re-processes the prompt template.\n * \n * @param variables - Your new variables\n * @throws {MissingVariablesError} When required variables are not provided in the variables object\n */\n setVariables(variables: Variables) {\n this.#variables = variables;\n const allMatch = Array.from(this.#originalPrompt.matchAll(this.#variableRegex))\n .filter(match => !this.#isInsideCodeBlock(match.index!, this.#originalPrompt))\n .map(match => {\n return {\n placeholder: match[0],\n index: match.index\n }\n });\n\n const getVariableNameFromPlaceholder = (placeholder: string) => {\n return placeholder.replaceAll(/[{}]/g, \"\").trim();\n }\n\n let missingVariables: string[] = [];\n let applyReplace: (() => {})[] = [];\n\n allMatch.forEach(match => {\n const variableName = getVariableNameFromPlaceholder(match.placeholder);\n if (variableName in variables)\n applyReplace.push(() => this.#value = this.#value.replace(match.placeholder, this.#stringify(this.#variables[variableName])));\n else\n missingVariables.push(variableName);\n });\n\n if (missingVariables.length)\n throw new MissingVariablesError(missingVariables, this.#originalPrompt);\n applyReplace.forEach(replace => replace());\n }\n\n #load(relativePath: string): string {\n let _relativePath = ((): string => {\n const split = relativePath.split(\"/\");\n if (split.length > 1) {\n const namespace = split[0].trim();\n if (namespace[0] == \"@\") {\n const name = split[0].slice(1);\n if (name in Prompt.pathAlias) {\n return path.join(Prompt.pathAlias[name], split[1]);\n } else\n throw new NamespaceUndefinedError(namespace);\n }\n }\n return relativePath;\n })();\n\n let fullPath = path.join(this.#basePath || \"./\", _relativePath);\n if (!fullPath.split(\"/\").at(-1)?.includes(\".\"))\n fullPath = `${fullPath}.md`;\n try {\n const content = readFileSync(fullPath, 'utf-8');\n return content;\n } catch (error) {\n if (error instanceof Error) {\n throw new LoadFileReadError(fullPath, error);\n }\n throw error;\n }\n }\n\n /**\n * Gets the original prompt string used for this instance. Before any processing\n * @returns {string} The original prompt string\n */\n get promptString() {\n return this.#originalPrompt;\n }\n\n /**\n * Gets the variables currently in use for this prompt.\n * @returns {Map<string, any>} A map containing the prompt variables and their values.\n */\n get variables() {\n return this.#variables;\n }\n\n /**\n * Gets the interpolated value of the prompt.\n * @returns {string} The resulting string after all variable interpolation has been applied.\n */\n get value() {\n return this.#value;\n }\n}"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,OAAO,UAAU;AASV,MAAM,8BAA8B,MAAM;AAAA,EAC7C,YAAmB,WAAqB,QAAgB;AACpD,UAAM,sBAAsB,UAAU,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,iBAAiB,MAAM,EAAE;AAD7E;AAEf,SAAK,OAAO;AAAA,EAChB;AACJ;AAEO,MAAM,gCAAgC,MAAM;AAAA,EAC/C,YAAmB,WAAmB;AAClC,UAAM,mEAAmE,SAAS,kBAAkB;AADrF;AAEf,SAAK,OAAO;AAAA,EAChB;AACJ;AAEO,MAAM,0BAA0B,MAAM;AAAA,EACzC,YAAmB,UAAyB,eAAsB;AAC9D,UAAM,8BAA8B,QAAQ,KAAK,cAAc,OAAO,EAAE;AADzD;AAAyB;AAExC,SAAK,OAAO;AAAA,EAChB;AACJ;AAqBO,MAAM,OAAO,CAAC,kBAAgC,EAAE,MAAM,MAAM,aAAa;AAEhF,MAAO,OAAqB;AAAA,EACxB,OAAe,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBtC,OAAc,YAAoC,CAAC;AAAA,EAEnD,kBAA0B;AAAA,EAC1B,SAAiB;AAAA,EACjB,iBAAyB,OAAO;AAAA,EAChC,aAAwB,CAAC;AAAA,EACzB,YAAY,QAAQ,IAAI,KAAK;AAAA,EAE7B,YAAY,QAAuB,YAAuB,CAAC,GAAG;AAC1D,QAAI,CAAC,QAAQ,IAAI,KAAK;AAClB,cAAQ,KAAK,0BAA0B;AAC3C,QAAI,OAAO,UAAU,UAAU;AAC3B,WAAK,kBAAkB;AACvB,WAAK,SAAS;AAAA,IAClB,OAAO;AACH,WAAK,kBAAkB,KAAK,MAAM,OAAO,YAAY;AACrD,WAAK,SAAS,KAAK;AAAA,IACvB;AACA,SAAK,aAAa,SAAS;AAAA,EAC/B;AAAA,EAEA,WAAW,OAAY;AACnB,YAAQ,OAAO,OAAO;AAAA,MAClB,KAAK;AACD,eAAO,KAAK,UAAU,KAAK;AAAA,MAC/B;AACI,eAAO;AAAA,IACf;AAAA,EACJ;AAAA,EAEA,mBAAmB,OAAe,MAAuB;AACrD,UAAM,iBAAiB;AACvB,QAAI;AAEJ,YAAQ,QAAQ,eAAe,KAAK,IAAI,OAAO,MAAM;AACjD,YAAM,QAAQ,MAAM;AACpB,YAAM,MAAM,QAAQ,MAAM,CAAC,EAAE;AAE7B,UAAI,SAAS,SAAS,QAAQ,KAAK;AAC/B,eAAO;AAAA,MACX;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,WAAsB;AAC/B,SAAK,aAAa;AAClB,UAAM,WAAW,MAAM,KAAK,KAAK,gBAAgB,SAAS,KAAK,cAAc,CAAC,EACzE,OAAO,WAAS,CAAC,KAAK,mBAAmB,MAAM,OAAQ,KAAK,eAAe,CAAC,EAC5E,IAAI,WAAS;AACV,aAAO;AAAA,QACH,aAAa,MAAM,CAAC;AAAA,QACpB,OAAO,MAAM;AAAA,MACjB;AAAA,IACJ,CAAC;AAEL,UAAM,iCAAiC,CAAC,gBAAwB;AAC5D,aAAO,YAAY,WAAW,SAAS,EAAE,EAAE,KAAK;AAAA,IACpD;AAEA,QAAI,mBAA6B,CAAC;AAClC,QAAI,eAA6B,CAAC;AAElC,aAAS,QAAQ,WAAS;AACtB,YAAM,eAAe,+BAA+B,MAAM,WAAW;AACrE,UAAI,gBAAgB;AAChB,qBAAa,KAAK,MAAM,KAAK,SAAS,KAAK,OAAO,QAAQ,MAAM,aAAa,KAAK,WAAW,KAAK,WAAW,YAAY,CAAC,CAAC,CAAC;AAAA;AAE5H,yBAAiB,KAAK,YAAY;AAAA,IAC1C,CAAC;AAED,QAAI,iBAAiB;AACjB,YAAM,IAAI,sBAAsB,kBAAkB,KAAK,eAAe;AAC1E,iBAAa,QAAQ,aAAW,QAAQ,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,cAA8B;AAChC,QAAI,iBAAiB,MAAc;AAC/B,YAAM,QAAQ,aAAa,MAAM,GAAG;AACpC,UAAI,MAAM,SAAS,GAAG;AAClB,cAAM,YAAY,MAAM,CAAC,EAAE,KAAK;AAChC,YAAI,UAAU,CAAC,KAAK,KAAK;AACrB,gBAAM,OAAO,MAAM,CAAC,EAAE,MAAM,CAAC;AAC7B,cAAI,QAAQ,OAAO,WAAW;AAC1B,mBAAO,KAAK,KAAK,OAAO,UAAU,IAAI,GAAG,MAAM,CAAC,CAAC;AAAA,UACrD;AACI,kBAAM,IAAI,wBAAwB,SAAS;AAAA,QACnD;AAAA,MACJ;AACA,aAAO;AAAA,IACX,GAAG;AAEH,QAAI,WAAW,KAAK,KAAK,KAAK,aAAa,MAAM,aAAa;AAC9D,QAAI,CAAC,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,SAAS,GAAG;AACzC,iBAAW,GAAG,QAAQ;AAC1B,QAAI;AACA,YAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,aAAO;AAAA,IACX,SAAS,OAAO;AACZ,UAAI,iBAAiB,OAAO;AACxB,cAAM,IAAI,kBAAkB,UAAU,KAAK;AAAA,MAC/C;AACA,YAAM;AAAA,IACV;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,eAAe;AACf,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,YAAY;AACZ,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,QAAQ;AACR,WAAO,KAAK;AAAA,EAChB;AACJ;",
6
+ "names": []
7
+ }
package/dist/utils.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fragola-ai/prompt",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Prompt interpolation and loading library for Node.js/TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",