@intlayer/backend 5.4.2 → 5.5.0-canary.0

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.
Files changed (105) hide show
  1. package/dist/cjs/controllers/ai.controller.cjs +60 -52
  2. package/dist/cjs/controllers/ai.controller.cjs.map +1 -1
  3. package/dist/cjs/controllers/dictionary.controller.cjs +5 -0
  4. package/dist/cjs/controllers/dictionary.controller.cjs.map +1 -1
  5. package/dist/cjs/export.cjs +4 -2
  6. package/dist/cjs/export.cjs.map +1 -1
  7. package/dist/cjs/routes/ai.routes.cjs +6 -0
  8. package/dist/cjs/routes/ai.routes.cjs.map +1 -1
  9. package/dist/cjs/services/dictionary.service.cjs +6 -1
  10. package/dist/cjs/services/dictionary.service.cjs.map +1 -1
  11. package/dist/cjs/utils/AI/aiSdk.cjs +140 -0
  12. package/dist/cjs/utils/AI/aiSdk.cjs.map +1 -0
  13. package/dist/cjs/utils/AI/askDocQuestion/PROMPT.md +2 -1
  14. package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs +32 -27
  15. package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs.map +1 -1
  16. package/dist/cjs/utils/AI/askDocQuestion/embeddings.json +7374 -0
  17. package/dist/cjs/utils/AI/auditDictionary/PROMPT.md +4 -0
  18. package/dist/cjs/utils/AI/auditDictionary/index.cjs +36 -43
  19. package/dist/cjs/utils/AI/auditDictionary/index.cjs.map +1 -1
  20. package/dist/cjs/utils/AI/auditDictionaryField/PROMPT.md +4 -0
  21. package/dist/cjs/utils/AI/auditDictionaryField/index.cjs +34 -28
  22. package/dist/cjs/utils/AI/auditDictionaryField/index.cjs.map +1 -1
  23. package/dist/cjs/utils/AI/auditDictionaryMetadata/PROMPT.md +4 -0
  24. package/dist/cjs/utils/AI/auditDictionaryMetadata/index.cjs +23 -23
  25. package/dist/cjs/utils/AI/auditDictionaryMetadata/index.cjs.map +1 -1
  26. package/dist/cjs/utils/{auditTag → AI/auditTag}/PROMPT.md +4 -0
  27. package/dist/cjs/utils/{auditTag → AI/auditTag}/index.cjs +27 -27
  28. package/dist/cjs/utils/AI/auditTag/index.cjs.map +1 -0
  29. package/dist/cjs/utils/AI/autocomplete/PROMPT.md +4 -0
  30. package/dist/cjs/utils/AI/autocomplete/index.cjs +25 -22
  31. package/dist/cjs/utils/AI/autocomplete/index.cjs.map +1 -1
  32. package/dist/cjs/utils/AI/translateJSON/PROMPT.md +53 -0
  33. package/dist/cjs/utils/AI/translateJSON/index.cjs +106 -0
  34. package/dist/cjs/utils/AI/translateJSON/index.cjs.map +1 -0
  35. package/dist/cjs/utils/extractJSON.cjs +52 -0
  36. package/dist/cjs/utils/extractJSON.cjs.map +1 -0
  37. package/dist/esm/controllers/ai.controller.mjs +58 -51
  38. package/dist/esm/controllers/ai.controller.mjs.map +1 -1
  39. package/dist/esm/controllers/dictionary.controller.mjs +5 -0
  40. package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
  41. package/dist/esm/export.mjs +3 -2
  42. package/dist/esm/export.mjs.map +1 -1
  43. package/dist/esm/routes/ai.routes.mjs +8 -1
  44. package/dist/esm/routes/ai.routes.mjs.map +1 -1
  45. package/dist/esm/services/dictionary.service.mjs +6 -1
  46. package/dist/esm/services/dictionary.service.mjs.map +1 -1
  47. package/dist/esm/utils/AI/aiSdk.mjs +115 -0
  48. package/dist/esm/utils/AI/aiSdk.mjs.map +1 -0
  49. package/dist/esm/utils/AI/askDocQuestion/PROMPT.md +2 -1
  50. package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs +32 -27
  51. package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs.map +1 -1
  52. package/dist/esm/utils/AI/askDocQuestion/embeddings.json +7374 -0
  53. package/dist/esm/utils/AI/auditDictionary/PROMPT.md +4 -0
  54. package/dist/esm/utils/AI/auditDictionary/index.mjs +36 -43
  55. package/dist/esm/utils/AI/auditDictionary/index.mjs.map +1 -1
  56. package/dist/esm/utils/AI/auditDictionaryField/PROMPT.md +4 -0
  57. package/dist/esm/utils/AI/auditDictionaryField/index.mjs +34 -28
  58. package/dist/esm/utils/AI/auditDictionaryField/index.mjs.map +1 -1
  59. package/dist/esm/utils/AI/auditDictionaryMetadata/PROMPT.md +4 -0
  60. package/dist/esm/utils/AI/auditDictionaryMetadata/index.mjs +23 -23
  61. package/dist/esm/utils/AI/auditDictionaryMetadata/index.mjs.map +1 -1
  62. package/dist/esm/utils/{auditTag → AI/auditTag}/PROMPT.md +4 -0
  63. package/dist/esm/utils/AI/auditTag/index.mjs +49 -0
  64. package/dist/esm/utils/AI/auditTag/index.mjs.map +1 -0
  65. package/dist/esm/utils/AI/autocomplete/PROMPT.md +4 -0
  66. package/dist/esm/utils/AI/autocomplete/index.mjs +25 -22
  67. package/dist/esm/utils/AI/autocomplete/index.mjs.map +1 -1
  68. package/dist/esm/utils/AI/translateJSON/PROMPT.md +53 -0
  69. package/dist/esm/utils/AI/translateJSON/index.mjs +81 -0
  70. package/dist/esm/utils/AI/translateJSON/index.mjs.map +1 -0
  71. package/dist/esm/utils/extractJSON.mjs +28 -0
  72. package/dist/esm/utils/extractJSON.mjs.map +1 -0
  73. package/dist/types/controllers/ai.controller.d.ts +12 -21
  74. package/dist/types/controllers/ai.controller.d.ts.map +1 -1
  75. package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
  76. package/dist/types/export.d.ts +12 -11
  77. package/dist/types/export.d.ts.map +1 -1
  78. package/dist/types/routes/ai.routes.d.ts +5 -0
  79. package/dist/types/routes/ai.routes.d.ts.map +1 -1
  80. package/dist/types/services/dictionary.service.d.ts +2 -2
  81. package/dist/types/services/dictionary.service.d.ts.map +1 -1
  82. package/dist/types/utils/AI/aiSdk.d.ts +41 -0
  83. package/dist/types/utils/AI/aiSdk.d.ts.map +1 -0
  84. package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts +1 -1
  85. package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts.map +1 -1
  86. package/dist/types/utils/AI/auditDictionary/index.d.ts +10 -15
  87. package/dist/types/utils/AI/auditDictionary/index.d.ts.map +1 -1
  88. package/dist/types/utils/AI/auditDictionaryField/index.d.ts +9 -14
  89. package/dist/types/utils/AI/auditDictionaryField/index.d.ts.map +1 -1
  90. package/dist/types/utils/AI/auditDictionaryMetadata/index.d.ts +7 -13
  91. package/dist/types/utils/AI/auditDictionaryMetadata/index.d.ts.map +1 -1
  92. package/dist/types/utils/AI/auditTag/index.d.ts +18 -0
  93. package/dist/types/utils/AI/auditTag/index.d.ts.map +1 -0
  94. package/dist/types/utils/AI/autocomplete/index.d.ts +6 -12
  95. package/dist/types/utils/AI/autocomplete/index.d.ts.map +1 -1
  96. package/dist/types/utils/AI/translateJSON/index.d.ts +24 -0
  97. package/dist/types/utils/AI/translateJSON/index.d.ts.map +1 -0
  98. package/dist/types/utils/extractJSON.d.ts +6 -0
  99. package/dist/types/utils/extractJSON.d.ts.map +1 -0
  100. package/package.json +13 -7
  101. package/dist/cjs/utils/auditTag/index.cjs.map +0 -1
  102. package/dist/esm/utils/auditTag/index.mjs +0 -49
  103. package/dist/esm/utils/auditTag/index.mjs.map +0 -1
  104. package/dist/types/utils/auditTag/index.d.ts +0 -30
  105. package/dist/types/utils/auditTag/index.d.ts.map +0 -1
@@ -138,6 +138,10 @@ You are an expert in internationalization, copy writing and content management.
138
138
  - The import of the `t` function was imported from `react-intlayer` instead of `intlayer`.
139
139
  - A type `Dictionary` was added to the file to strengthen the content declaration.
140
140
 
141
+ **Application Context**
142
+
143
+ {{applicationContext}}
144
+
141
145
  **Tags Instructions:**
142
146
 
143
147
  {{tagsInstructions}}
@@ -1,69 +1,62 @@
1
+ import { getLocaleName } from "@intlayer/core";
2
+ import { logger } from "./../../../logger/index.mjs";
3
+ import { generateText } from "ai";
1
4
  import { readFileSync } from "fs";
2
5
  import { dirname, join } from "path";
3
6
  import { fileURLToPath } from "url";
4
- import { getLocaleName } from "@intlayer/core";
5
- import { logger } from "./../../../logger/index.mjs";
6
- import { OpenAI } from "openai";
7
+ import { AIProvider, getAIConfig } from "../aiSdk.mjs";
7
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
- const getFileContent = (relativeFilePath) => {
9
- const absolutePath = join(__dirname, relativeFilePath);
10
- const fileContent = readFileSync(absolutePath, "utf-8");
11
- return fileContent;
12
- };
13
- const FILE_TEMPLATE = {
14
- ts: getFileContent("./TS_FORMAT.md"),
15
- tsx: getFileContent("./TSX_FORMAT.md"),
16
- js: getFileContent("./MJS_FORMAT.md"),
17
- mjs: getFileContent("./MJS_FORMAT.md"),
18
- cjs: getFileContent("./CJS_FORMAT.md"),
19
- jsx: getFileContent("./JSX_FORMAT.md"),
20
- json: getFileContent("./JSON_FORMAT.md")
9
+ const getFileContent = (filePath) => {
10
+ return readFileSync(join(__dirname, filePath), { encoding: "utf-8" });
21
11
  };
22
12
  const CHAT_GPT_PROMPT = getFileContent("./PROMPT.md");
23
13
  const formatLocaleWithName = (locale) => {
24
- const localeName = getLocaleName(locale);
25
- return `${locale}: ${localeName}`;
14
+ return `${locale}: ${getLocaleName(locale)}`;
15
+ };
16
+ const formatTagInstructions = (tags) => {
17
+ if (!tags || tags.length === 0) {
18
+ return "";
19
+ }
20
+ return `Based on the dictionary content, identify specific tags from the list below that would be relevant:
21
+
22
+ ${tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n")}`;
26
23
  };
27
- const formatTagInstructions = (tags = []) => tags.map((tag) => `- ${tag.key}: ${tag.instructions}`).join("\n\n");
28
24
  const auditDictionary = async ({
29
25
  fileContent,
30
26
  filePath,
31
- model,
32
- openAiApiKey,
33
- customPrompt,
34
- temperature,
27
+ aiOptions,
35
28
  locales,
36
29
  defaultLocale,
37
30
  tags
38
31
  }) => {
39
32
  try {
40
- const openai = new OpenAI({
41
- apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY
33
+ const otherLocales = locales.filter((locale) => locale !== defaultLocale);
34
+ const aiConfig = await getAIConfig({
35
+ provider: AIProvider.OPENAI,
36
+ model: "gpt-4o-mini",
37
+ apiKey: process.env.OPENAI_API_KEY,
38
+ ...aiOptions
42
39
  });
43
- const splitted = (filePath ?? ".json").split(".");
44
- const fileExtension = splitted[splitted.length - 1];
45
- const prompt = customPrompt ?? CHAT_GPT_PROMPT.replace("{{filePath}}", filePath ?? "Not provided").replace(
40
+ const prompt = CHAT_GPT_PROMPT.replace(
46
41
  "{{defaultLocale}}",
47
- `{${formatLocaleWithName(defaultLocale)}}`
42
+ formatLocaleWithName(defaultLocale)
48
43
  ).replace(
49
44
  "{{otherLocales}}",
50
- `{${locales.map(formatLocaleWithName).join(", ")}}`
51
- ).replace(
52
- "{{declarationsContentTemplate}}",
53
- FILE_TEMPLATE[fileExtension]
54
- ).replace("{{fileContent}}", fileContent).replace("{{tagsInstructions}}", formatTagInstructions(tags));
55
- const chatCompletion = await openai.chat.completions.create({
56
- model: openAiApiKey ? model ?? "gpt-4o-2024-11-20" : "gpt-4o-2024-11-20",
57
- temperature: openAiApiKey ? temperature ?? 0.1 : 0.1,
45
+ `{${otherLocales.map(formatLocaleWithName).join(", ")}}`
46
+ ).replace("{{filePath}}", filePath ?? "").replace("{{fileContent}}", fileContent).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags));
47
+ if (!aiConfig) {
48
+ logger.error("Failed to configure AI model");
49
+ return void 0;
50
+ }
51
+ const { text: newContent, usage } = await generateText({
52
+ model: aiConfig.model,
53
+ temperature: aiConfig.temperature,
58
54
  messages: [{ role: "system", content: prompt }]
59
55
  });
60
- const newContent = chatCompletion.choices[0].message?.content;
61
- logger.info(
62
- `${chatCompletion.usage?.total_tokens} tokens used in the request`
63
- );
56
+ logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
64
57
  return {
65
- fileContent: newContent ?? "",
66
- tokenUsed: chatCompletion.usage?.total_tokens ?? 0
58
+ fileContent: newContent,
59
+ tokenUsed: usage?.totalTokens ?? 0
67
60
  };
68
61
  } catch (error) {
69
62
  console.error(error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { getLocaleName } from '@intlayer/core';\nimport { logger } from '@logger';\nimport type { Locales } from 'intlayer';\nimport { OpenAI } from 'openai';\nimport type { Tag } from '@/types/tag.types';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport type AIOptions = {\n model?: string;\n temperature?: number;\n openAiApiKey?: string;\n};\n\nexport type AuditOptions = {\n locales: Locales[];\n defaultLocale: Locales;\n fileContent: string;\n filePath?: string;\n customPrompt?: string;\n tags?: Tag[];\n} & AIOptions;\nexport type AuditFileResultData = { fileContent: string; tokenUsed: number };\n\n/**\n * Reads the content of a file synchronously.\n *\n * @function\n * @param relativeFilePath - The relative or absolute path to the target file.\n * @returns The entire contents of the specified file as a UTF-8 encoded string.\n */\nconst getFileContent = (relativeFilePath: string): string => {\n const absolutePath = join(__dirname, relativeFilePath);\n const fileContent = readFileSync(absolutePath, 'utf-8');\n return fileContent;\n};\n\nconst FILE_TEMPLATE: Record<string, string> = {\n ts: getFileContent('./TS_FORMAT.md'),\n tsx: getFileContent('./TSX_FORMAT.md'),\n js: getFileContent('./MJS_FORMAT.md'),\n mjs: getFileContent('./MJS_FORMAT.md'),\n cjs: getFileContent('./CJS_FORMAT.md'),\n jsx: getFileContent('./JSX_FORMAT.md'),\n json: getFileContent('./JSON_FORMAT.md'),\n};\n\n// The prompt template to send to ChatGPT, requesting an audit of content declaration files.\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Formats a locale with its full name and returns a string representation.\n *\n * @function\n * @param locale - A locale from the project's configuration (e.g., 'en-US', 'fr-FR').\n * @returns A formatted string combining the locale's name and code. Example: \"English (US): en-US\".\n */\nconst formatLocaleWithName = (locale: Locales): string => {\n // getLocaleName returns a human-readable name for the locale.\n const localeName = getLocaleName(locale);\n\n // Concatenate both the readable name and the locale code.\n return `${locale}: ${localeName}`;\n};\n\n/**\n * Formats an array of tags with their keys and instructions.\n *\n * @function\n * @param tags - An array of tags from the project's configuration.\n * @returns A string representation of the tags, with their keys and instructions.\n */\nconst formatTagInstructions = (tags: Tag[] = []) =>\n tags.map((tag) => `- ${tag.key}: ${tag.instructions}`).join('\\n\\n');\n\n/**\n * Audits a content declaration file by constructing a prompt for ChatGPT.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies. It prints the prompt for each file,\n * and could be adapted to send requests to the ChatGPT model.\n */\nexport const auditDictionary = async ({\n fileContent,\n filePath,\n model,\n openAiApiKey,\n customPrompt,\n temperature,\n locales,\n defaultLocale,\n tags,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n try {\n // Optionally, you could initialize and configure the OpenAI client here, if you intend to make API calls.\n // Uncomment and configure the following lines if you have `openai` installed and want to call the API:\n\n const openai = new OpenAI({\n apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY,\n });\n\n // Read the file's content.\n const splitted = (filePath ?? '.json').split('.');\n const fileExtension = splitted[splitted.length - 1];\n\n // Prepare the prompt for ChatGPT by replacing placeholders with actual values.\n const prompt =\n customPrompt ??\n CHAT_GPT_PROMPT.replace('{{filePath}}', filePath ?? 'Not provided')\n .replace(\n '{{defaultLocale}}',\n `{${formatLocaleWithName(defaultLocale)}}`\n )\n .replace(\n '{{otherLocales}}',\n `{${locales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace(\n '{{declarationsContentTemplate}}',\n FILE_TEMPLATE[fileExtension]\n )\n .replace('{{fileContent}}', fileContent)\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Example of how you might request a completion from ChatGPT:\n const chatCompletion = await openai.chat.completions.create({\n model: openAiApiKey\n ? (model ?? 'gpt-4o-2024-11-20')\n : 'gpt-4o-2024-11-20',\n temperature: openAiApiKey ? (temperature ?? 0.1) : 0.1,\n messages: [{ role: 'system', content: prompt }],\n });\n\n const newContent = chatCompletion.choices[0].message?.content;\n\n logger.info(\n `${chatCompletion.usage?.total_tokens} tokens used in the request`\n );\n\n return {\n fileContent: newContent ?? '',\n tokenUsed: chatCompletion.usage?.total_tokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,cAAc;AAEvB,SAAS,cAAc;AAGvB,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAyBxD,MAAM,iBAAiB,CAAC,qBAAqC;AAC3D,QAAM,eAAe,KAAK,WAAW,gBAAgB;AACrD,QAAM,cAAc,aAAa,cAAc,OAAO;AACtD,SAAO;AACT;AAEA,MAAM,gBAAwC;AAAA,EAC5C,IAAI,eAAe,gBAAgB;AAAA,EACnC,KAAK,eAAe,iBAAiB;AAAA,EACrC,IAAI,eAAe,iBAAiB;AAAA,EACpC,KAAK,eAAe,iBAAiB;AAAA,EACrC,KAAK,eAAe,iBAAiB;AAAA,EACrC,KAAK,eAAe,iBAAiB;AAAA,EACrC,MAAM,eAAe,kBAAkB;AACzC;AAGA,MAAM,kBAAkB,eAAe,aAAa;AASpD,MAAM,uBAAuB,CAAC,WAA4B;AAExD,QAAM,aAAa,cAAc,MAAM;AAGvC,SAAO,GAAG,MAAM,KAAK,UAAU;AACjC;AASA,MAAM,wBAAwB,CAAC,OAAc,CAAC,MAC5C,KAAK,IAAI,CAAC,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,EAAE,EAAE,KAAK,MAAM;AAQ7D,MAAM,kBAAkB,OAAO;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA8D;AAC5D,MAAI;AAIF,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,QAAQ,gBAAgB,QAAQ,IAAI;AAAA,IACtC,CAAC;AAGD,UAAM,YAAY,YAAY,SAAS,MAAM,GAAG;AAChD,UAAM,gBAAgB,SAAS,SAAS,SAAS,CAAC;AAGlD,UAAM,SACJ,gBACA,gBAAgB,QAAQ,gBAAgB,YAAY,cAAc,EAC/D;AAAA,MACC;AAAA,MACA,IAAI,qBAAqB,aAAa,CAAC;AAAA,IACzC,EACC;AAAA,MACC;AAAA,MACA,IAAI,QAAQ,IAAI,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,IAClD,EACC;AAAA,MACC;AAAA,MACA,cAAc,aAAa;AAAA,IAC7B,EACC,QAAQ,mBAAmB,WAAW,EACtC,QAAQ,wBAAwB,sBAAsB,IAAI,CAAC;AAGhE,UAAM,iBAAiB,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,MAC1D,OAAO,eACF,SAAS,sBACV;AAAA,MACJ,aAAa,eAAgB,eAAe,MAAO;AAAA,MACnD,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,UAAM,aAAa,eAAe,QAAQ,CAAC,EAAE,SAAS;AAEtD,WAAO;AAAA,MACL,GAAG,eAAe,OAAO,YAAY;AAAA,IACvC;AAEA,WAAO;AAAA,MACL,aAAa,cAAc;AAAA,MAC3B,WAAW,eAAe,OAAO,gBAAgB;AAAA,IACnD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import type { Tag } from '@/types/tag.types';\nimport { getLocaleName } from '@intlayer/core';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { readFileSync } from 'fs';\nimport type { Locales } from 'intlayer';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { AIOptions, AIProvider, getAIConfig } from '../aiSdk';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Get the content of a file at the specified path\nconst getFileContent = (filePath: string) => {\n return readFileSync(join(__dirname, filePath), { encoding: 'utf-8' });\n};\n\nexport type AuditOptions = {\n fileContent: string;\n filePath?: string;\n locales: Locales[];\n defaultLocale: Locales;\n tags: Tag[];\n aiOptions?: AIOptions;\n};\n\nexport type AuditFileResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locales): string => {\n return `${locale}: ${getLocaleName(locale)}`;\n};\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionary = async ({\n fileContent,\n filePath,\n aiOptions,\n locales,\n defaultLocale,\n tags,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n try {\n const otherLocales = locales.filter((locale) => locale !== defaultLocale);\n\n // Get the appropriate AI model configuration\n const aiConfig = await getAIConfig({\n provider: AIProvider.OPENAI,\n model: 'gpt-4o-mini',\n apiKey: process.env.OPENAI_API_KEY,\n ...aiOptions,\n });\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{defaultLocale}}',\n formatLocaleWithName(defaultLocale)\n )\n .replace(\n '{{otherLocales}}',\n `{${otherLocales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{filePath}}', filePath ?? '')\n .replace('{{fileContent}}', fileContent)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n model: aiConfig.model,\n temperature: aiConfig.temperature,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AACA,SAAS,qBAAqB;AAC9B,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAE7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAoB,YAAY,mBAAmB;AAEnD,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,MAAM,iBAAiB,CAAC,aAAqB;AAC3C,SAAO,aAAa,KAAK,WAAW,QAAQ,GAAG,EAAE,UAAU,QAAQ,CAAC;AACtE;AAiBA,MAAM,kBAAkB,eAAe,aAAa;AAQpD,MAAM,uBAAuB,CAAC,WAA4B;AACxD,SAAO,GAAG,MAAM,KAAK,cAAc,MAAM,CAAC;AAC5C;AASA,MAAM,wBAAwB,CAAC,SAAwB;AACrD,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,WAAO;AAAA,EACT;AAGA,SAAO;AAAA;AAAA,EAEP,KAAK,IAAI,CAAC,EAAE,KAAK,YAAY,MAAM,KAAK,GAAG,KAAK,WAAW,EAAE,EAAE,KAAK,MAAM,CAAC;AAC7E;AAOO,MAAM,kBAAkB,OAAO;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA8D;AAC5D,MAAI;AACF,UAAM,eAAe,QAAQ,OAAO,CAAC,WAAW,WAAW,aAAa;AAGxE,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,UAAU,WAAW;AAAA,MACrB,OAAO;AAAA,MACP,QAAQ,QAAQ,IAAI;AAAA,MACpB,GAAG;AAAA,IACL,CAAC;AAGD,UAAM,SAAS,gBAAgB;AAAA,MAC7B;AAAA,MACA,qBAAqB,aAAa;AAAA,IACpC,EACG;AAAA,MACC;AAAA,MACA,IAAI,aAAa,IAAI,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,IACvD,EACC,QAAQ,gBAAgB,YAAY,EAAE,EACtC,QAAQ,mBAAmB,WAAW,EACtC,QAAQ,0BAA0B,WAAW,sBAAsB,EAAE,EACrE,QAAQ,wBAAwB,sBAAsB,IAAI,CAAC;AAE9D,QAAI,CAAC,UAAU;AACb,aAAO,MAAM,8BAA8B;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,EAAE,MAAM,YAAY,MAAM,IAAI,MAAM,aAAa;AAAA,MACrD,OAAO,SAAS;AAAA,MAChB,aAAa,SAAS;AAAA,MACtB,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,WAAO,KAAK,GAAG,OAAO,eAAe,CAAC,6BAA6B;AAEnE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW,OAAO,eAAe;AAAA,IACnC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
@@ -108,6 +108,10 @@ You are an expert in internationalization, copy writing and content management.
108
108
 
109
109
  Développeurs, Responsables de contenu
110
110
 
111
+ **Application Context**
112
+
113
+ {{applicationContext}}
114
+
111
115
  **Tags Instructions:**
112
116
 
113
117
  {{tagsInstructions}}
@@ -1,51 +1,57 @@
1
+ import { getLocaleName } from "@intlayer/core";
2
+ import { logger } from "./../../../logger/index.mjs";
3
+ import { generateText } from "ai";
1
4
  import { readFileSync } from "fs";
2
5
  import { dirname, join } from "path";
3
6
  import { fileURLToPath } from "url";
4
- import { getLocaleName } from "@intlayer/core";
5
- import { logger } from "./../../../logger/index.mjs";
6
- import { OpenAI } from "openai";
7
+ import { AIProvider, getAIConfig } from "../aiSdk.mjs";
7
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
- const getFileContent = (relativeFilePath) => {
9
- const absolutePath = join(__dirname, relativeFilePath);
10
- const fileContent = readFileSync(absolutePath, "utf-8");
11
- return fileContent;
9
+ const getFileContent = (filePath) => {
10
+ return readFileSync(join(__dirname, filePath), { encoding: "utf-8" });
12
11
  };
13
12
  const CHAT_GPT_PROMPT = getFileContent("./PROMPT.md");
14
13
  const formatLocaleWithName = (locale) => {
15
- const localeName = getLocaleName(locale);
16
- return `${locale}: ${localeName}`;
14
+ return `${locale}: ${getLocaleName(locale)}`;
15
+ };
16
+ const formatTagInstructions = (tags) => {
17
+ if (!tags || tags.length === 0) {
18
+ return "";
19
+ }
20
+ return `Based on the dictionary content, identify specific tags from the list below that would be relevant:
21
+
22
+ ${tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n")}`;
17
23
  };
18
- const formatTagInstructions = (tags = []) => tags.map((tag) => `- ${tag.key}: ${tag.instructions}`).join("\n\n");
19
24
  const auditDictionaryField = async ({
20
25
  fileContent,
21
- model,
22
- openAiApiKey,
23
- temperature,
24
- customPrompt,
26
+ aiOptions,
25
27
  locales,
26
28
  keyPath,
27
29
  tags
28
30
  }) => {
29
31
  try {
30
- const openai = new OpenAI({
31
- apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY
32
- });
33
- const prompt = customPrompt ?? CHAT_GPT_PROMPT.replace(
32
+ const prompt = CHAT_GPT_PROMPT.replace(
34
33
  "{{otherLocales}}",
35
34
  `{${locales.map(formatLocaleWithName).join(", ")}}`
36
- ).replace("{{keyPath}}", JSON.stringify(keyPath)).replace("{{fileContent}}", fileContent).replace("{{tagsInstructions}}", formatTagInstructions(tags));
37
- const chatCompletion = await openai.chat.completions.create({
38
- model: openAiApiKey ? model ?? "gpt-4o-2024-11-20" : "gpt-4o-2024-11-20",
39
- temperature: openAiApiKey ? temperature ?? 0.1 : 0.1,
35
+ ).replace("{{keyPath}}", JSON.stringify(keyPath)).replace("{{fileContent}}", fileContent).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags));
36
+ const aiConfig = await getAIConfig({
37
+ provider: AIProvider.OPENAI,
38
+ model: "gpt-4o-mini",
39
+ apiKey: process.env.OPENAI_API_KEY,
40
+ ...aiOptions
41
+ });
42
+ if (!aiConfig) {
43
+ logger.error("Failed to configure AI model");
44
+ return void 0;
45
+ }
46
+ const { text: newContent, usage } = await generateText({
47
+ model: aiConfig.model,
48
+ temperature: aiConfig.temperature,
40
49
  messages: [{ role: "system", content: prompt }]
41
50
  });
42
- const newContent = chatCompletion.choices[0].message?.content;
43
- logger.info(
44
- `${chatCompletion.usage?.total_tokens} tokens used in the request`
45
- );
51
+ logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
46
52
  return {
47
- fileContent: newContent ?? "",
48
- tokenUsed: chatCompletion.usage?.total_tokens ?? 0
53
+ fileContent: newContent,
54
+ tokenUsed: usage?.totalTokens ?? 0
49
55
  };
50
56
  } catch (error) {
51
57
  console.error(error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { getLocaleName, type KeyPath } from '@intlayer/core';\nimport { logger } from '@logger';\nimport type { Locales } from 'intlayer';\nimport { OpenAI } from 'openai';\nimport type { Tag } from '@/types/tag.types';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport type AIOptions = {\n model?: string;\n temperature?: number;\n openAiApiKey?: string;\n};\n\nexport type AuditDictionaryFieldOptions = {\n locales: Locales[];\n fileContent: string;\n customPrompt?: string;\n keyPath: KeyPath[];\n tags?: Tag[];\n} & AIOptions;\nexport type AuditDictionaryFieldResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n/**\n * Reads the content of a file synchronously.\n *\n * @function\n * @param relativeFilePath - The relative or absolute path to the target file.\n * @returns The entire contents of the specified file as a UTF-8 encoded string.\n */\nconst getFileContent = (relativeFilePath: string): string => {\n const absolutePath = join(__dirname, relativeFilePath);\n const fileContent = readFileSync(absolutePath, 'utf-8');\n return fileContent;\n};\n\n// The prompt template to send to ChatGPT, requesting an audit of content declaration files.\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Formats a locale with its full name and returns a string representation.\n *\n * @function\n * @param locale - A locale from the project's configuration (e.g., 'en-US', 'fr-FR').\n * @returns A formatted string combining the locale's name and code. Example: \"English (US): en-US\".\n */\nconst formatLocaleWithName = (locale: Locales): string => {\n // getLocaleName returns a human-readable name for the locale.\n const localeName = getLocaleName(locale);\n\n // Concatenate both the readable name and the locale code.\n return `${locale}: ${localeName}`;\n};\n\n/**\n * Formats an array of tags with their keys and instructions.\n *\n * @function\n * @param tags - An array of tags from the project's configuration.\n * @returns A string representation of the tags, with their keys and instructions.\n */\nconst formatTagInstructions = (tags: Tag[] = []) =>\n tags.map((tag) => `- ${tag.key}: ${tag.instructions}`).join('\\n\\n');\n\n/**\n * Audits a content declaration file by constructing a prompt for ChatGPT.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies. It prints the prompt for each file,\n * and could be adapted to send requests to the ChatGPT model.\n */\nexport const auditDictionaryField = async ({\n fileContent,\n model,\n openAiApiKey,\n temperature,\n customPrompt,\n locales,\n keyPath,\n tags,\n}: AuditDictionaryFieldOptions): Promise<\n AuditDictionaryFieldResultData | undefined\n> => {\n try {\n // Optionally, you could initialize and configure the OpenAI client here, if you intend to make API calls.\n // Uncomment and configure the following lines if you have `openai` installed and want to call the API:\n\n const openai = new OpenAI({\n apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY,\n });\n\n // Prepare the prompt for ChatGPT by replacing placeholders with actual values.\n const prompt =\n customPrompt ??\n CHAT_GPT_PROMPT.replace(\n '{{otherLocales}}',\n `{${locales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{keyPath}}', JSON.stringify(keyPath))\n .replace('{{fileContent}}', fileContent)\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Example of how you might request a completion from ChatGPT:\n const chatCompletion = await openai.chat.completions.create({\n model: openAiApiKey\n ? (model ?? 'gpt-4o-2024-11-20')\n : 'gpt-4o-2024-11-20',\n temperature: openAiApiKey ? (temperature ?? 0.1) : 0.1,\n messages: [{ role: 'system', content: prompt }],\n });\n\n const newContent = chatCompletion.choices[0].message?.content;\n\n logger.info(\n `${chatCompletion.usage?.total_tokens} tokens used in the request`\n );\n\n return {\n fileContent: newContent ?? '',\n tokenUsed: chatCompletion.usage?.total_tokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,qBAAmC;AAC5C,SAAS,cAAc;AAEvB,SAAS,cAAc;AAGvB,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AA2BxD,MAAM,iBAAiB,CAAC,qBAAqC;AAC3D,QAAM,eAAe,KAAK,WAAW,gBAAgB;AACrD,QAAM,cAAc,aAAa,cAAc,OAAO;AACtD,SAAO;AACT;AAGA,MAAM,kBAAkB,eAAe,aAAa;AASpD,MAAM,uBAAuB,CAAC,WAA4B;AAExD,QAAM,aAAa,cAAc,MAAM;AAGvC,SAAO,GAAG,MAAM,KAAK,UAAU;AACjC;AASA,MAAM,wBAAwB,CAAC,OAAc,CAAC,MAC5C,KAAK,IAAI,CAAC,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,EAAE,EAAE,KAAK,MAAM;AAQ7D,MAAM,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAEK;AACH,MAAI;AAIF,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,QAAQ,gBAAgB,QAAQ,IAAI;AAAA,IACtC,CAAC;AAGD,UAAM,SACJ,gBACA,gBAAgB;AAAA,MACd;AAAA,MACA,IAAI,QAAQ,IAAI,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,IAClD,EACG,QAAQ,eAAe,KAAK,UAAU,OAAO,CAAC,EAC9C,QAAQ,mBAAmB,WAAW,EACtC,QAAQ,wBAAwB,sBAAsB,IAAI,CAAC;AAGhE,UAAM,iBAAiB,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,MAC1D,OAAO,eACF,SAAS,sBACV;AAAA,MACJ,aAAa,eAAgB,eAAe,MAAO;AAAA,MACnD,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,UAAM,aAAa,eAAe,QAAQ,CAAC,EAAE,SAAS;AAEtD,WAAO;AAAA,MACL,GAAG,eAAe,OAAO,YAAY;AAAA,IACvC;AAEA,WAAO;AAAA,MACL,aAAa,cAAc;AAAA,MAC3B,WAAW,eAAe,OAAO,gBAAgB;AAAA,IACnD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import type { Tag } from '@/types/tag.types';\nimport { getLocaleName, type KeyPath } from '@intlayer/core';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { readFileSync } from 'fs';\nimport type { Locales } from 'intlayer';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { AIOptions, AIProvider, getAIConfig } from '../aiSdk';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Get the content of a file at the specified path\nconst getFileContent = (filePath: string) => {\n return readFileSync(join(__dirname, filePath), { encoding: 'utf-8' });\n};\n\nexport type AuditDictionaryFieldOptions = {\n fileContent: string;\n locales: Locales[];\n keyPath: KeyPath[];\n tags: Tag[];\n aiOptions?: AIOptions;\n};\n\nexport type AuditDictionaryFieldResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locales): string => {\n return `${locale}: ${getLocaleName(locale)}`;\n};\n\n/**\n * Formats tag instructions for the AI prompt.\n *\n * @param tags - Array of tags to format\n * @returns A formatted string with tag instructions\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryField = async ({\n fileContent,\n aiOptions,\n locales,\n keyPath,\n tags,\n}: AuditDictionaryFieldOptions): Promise<\n AuditDictionaryFieldResultData | undefined\n> => {\n try {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{otherLocales}}',\n `{${locales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace('{{keyPath}}', JSON.stringify(keyPath))\n .replace('{{fileContent}}', fileContent)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Get the appropriate AI model configuration\n const aiConfig = await getAIConfig({\n provider: AIProvider.OPENAI,\n model: 'gpt-4o-mini',\n apiKey: process.env.OPENAI_API_KEY,\n ...aiOptions,\n });\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n model: aiConfig.model,\n temperature: aiConfig.temperature,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AACA,SAAS,qBAAmC;AAC5C,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAE7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAoB,YAAY,mBAAmB;AAEnD,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,MAAM,iBAAiB,CAAC,aAAqB;AAC3C,SAAO,aAAa,KAAK,WAAW,QAAQ,GAAG,EAAE,UAAU,QAAQ,CAAC;AACtE;AAgBA,MAAM,kBAAkB,eAAe,aAAa;AAQpD,MAAM,uBAAuB,CAAC,WAA4B;AACxD,SAAO,GAAG,MAAM,KAAK,cAAc,MAAM,CAAC;AAC5C;AAQA,MAAM,wBAAwB,CAAC,SAAwB;AACrD,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA;AAAA,EAEP,KAAK,IAAI,CAAC,EAAE,KAAK,YAAY,MAAM,KAAK,GAAG,KAAK,WAAW,EAAE,EAAE,KAAK,MAAM,CAAC;AAC7E;AAOO,MAAM,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAEK;AACH,MAAI;AAEF,UAAM,SAAS,gBAAgB;AAAA,MAC7B;AAAA,MACA,IAAI,QAAQ,IAAI,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,IAClD,EACG,QAAQ,eAAe,KAAK,UAAU,OAAO,CAAC,EAC9C,QAAQ,mBAAmB,WAAW,EACtC,QAAQ,0BAA0B,WAAW,sBAAsB,EAAE,EACrE,QAAQ,wBAAwB,sBAAsB,IAAI,CAAC;AAG9D,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,UAAU,WAAW;AAAA,MACrB,OAAO;AAAA,MACP,QAAQ,QAAQ,IAAI;AAAA,MACpB,GAAG;AAAA,IACL,CAAC;AAED,QAAI,CAAC,UAAU;AACb,aAAO,MAAM,8BAA8B;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,EAAE,MAAM,YAAY,MAAM,IAAI,MAAM,aAAa;AAAA,MACrD,OAAO,SAAS;AAAA,MAChB,aAAa,SAAS;AAAA,MACtB,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,WAAO,KAAK,GAAG,OAAO,eAAe,CAAC,6BAA6B;AAEnE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW,OAAO,eAAe;AAAA,IACnC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
@@ -65,6 +65,10 @@ export default metadataContent;
65
65
  }
66
66
  ```
67
67
 
68
+ **Application Context**
69
+
70
+ {{applicationContext}}
71
+
68
72
  **List of existing Tags:**
69
73
 
70
74
  Here the list of existing tags as a context to help you to pick related ones.
@@ -1,47 +1,47 @@
1
1
  import { logger } from "./../../../logger/index.mjs";
2
+ import { generateText } from "ai";
2
3
  import { readFileSync } from "fs";
3
- import { OpenAI } from "openai";
4
4
  import { dirname, join } from "path";
5
5
  import { fileURLToPath } from "url";
6
+ import { AIProvider, getAIConfig } from "../aiSdk.mjs";
6
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const getFileContent = (relativeFilePath) => {
8
- const absolutePath = join(__dirname, relativeFilePath);
9
- const fileContent = readFileSync(absolutePath, "utf-8");
10
- return fileContent;
8
+ const getFileContent = (filePath) => {
9
+ return readFileSync(join(__dirname, filePath), { encoding: "utf-8" });
11
10
  };
12
11
  const CHAT_GPT_PROMPT = getFileContent("./PROMPT.md");
13
12
  const auditDictionaryMetadata = async ({
14
- model,
15
- openAiApiKey,
16
- temperature,
17
- customPrompt,
13
+ aiOptions,
18
14
  tags,
19
15
  fileContent
20
16
  }) => {
21
17
  try {
22
- const openai = new OpenAI({
23
- apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY
24
- });
25
- const prompt = customPrompt ?? CHAT_GPT_PROMPT.replace(
18
+ const prompt = CHAT_GPT_PROMPT.replace(
26
19
  "{{tags}}",
27
20
  `${JSON.stringify(
28
21
  tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n"),
29
22
  null,
30
23
  2
31
24
  )}`
32
- ).replace("{{contentDeclaration}}", fileContent);
33
- const chatCompletion = await openai.chat.completions.create({
34
- model: openAiApiKey ? model ?? "gpt-4o-2024-11-20" : "gpt-4o-2024-11-20",
35
- temperature: openAiApiKey ? temperature ?? 0.1 : 0.1,
25
+ ).replace("{{contentDeclaration}}", fileContent).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "");
26
+ const aiConfig = await getAIConfig({
27
+ provider: AIProvider.OPENAI,
28
+ model: "gpt-4o-mini",
29
+ apiKey: process.env.OPENAI_API_KEY,
30
+ ...aiOptions
31
+ });
32
+ if (!aiConfig) {
33
+ logger.error("Failed to configure AI model");
34
+ return void 0;
35
+ }
36
+ const { text: newContent, usage } = await generateText({
37
+ model: aiConfig.model,
38
+ temperature: aiConfig.temperature,
36
39
  messages: [{ role: "system", content: prompt }]
37
40
  });
38
- const newContent = chatCompletion.choices[0].message?.content;
39
- logger.info(
40
- `${chatCompletion.usage?.total_tokens} tokens used in the request`
41
- );
41
+ logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
42
42
  return {
43
- fileContent: newContent ?? "",
44
- tokenUsed: chatCompletion.usage?.total_tokens ?? 0
43
+ fileContent: newContent,
44
+ tokenUsed: usage?.totalTokens ?? 0
45
45
  };
46
46
  } catch (error) {
47
47
  console.error(error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/utils/AI/auditDictionaryMetadata/index.ts"],"sourcesContent":["import type { Tag } from '@/types/tag.types';\nimport { logger } from '@logger';\nimport { readFileSync } from 'fs';\nimport { OpenAI } from 'openai';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport type AIOptions = {\n model?: string;\n temperature?: number;\n openAiApiKey?: string;\n};\n\nexport type AuditOptions = {\n tags: Tag[];\n fileContent: string;\n customPrompt?: string;\n} & AIOptions;\nexport type AuditFileResultData = { fileContent: string; tokenUsed: number };\n\n/**\n * Reads the content of a file synchronously.\n *\n * @function\n * @param relativeFilePath - The relative or absolute path to the target file.\n * @returns The entire contents of the specified file as a UTF-8 encoded string.\n */\nconst getFileContent = (relativeFilePath: string): string => {\n const absolutePath = join(__dirname, relativeFilePath);\n const fileContent = readFileSync(absolutePath, 'utf-8');\n return fileContent;\n};\n\n// The prompt template to send to ChatGPT, requesting an audit of content declaration files.\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Audits a content declaration file by constructing a prompt for ChatGPT.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies. It prints the prompt for each file,\n * and could be adapted to send requests to the ChatGPT model.\n *\n */\nexport const auditDictionaryMetadata = async ({\n model,\n openAiApiKey,\n temperature,\n customPrompt,\n tags,\n fileContent,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n try {\n // Optionally, you could initialize and configure the OpenAI client here, if you intend to make API calls.\n // Uncomment and configure the following lines if you have `openai` installed and want to call the API:\n\n const openai = new OpenAI({\n apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY,\n });\n\n // Prepare the prompt for ChatGPT by replacing placeholders with actual values.\n const prompt =\n customPrompt ??\n CHAT_GPT_PROMPT.replace(\n '{{tags}}',\n `${JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )}`\n ).replace('{{contentDeclaration}}', fileContent);\n\n // Example of how you might request a completion from ChatGPT:\n const chatCompletion = await openai.chat.completions.create({\n model: openAiApiKey\n ? (model ?? 'gpt-4o-2024-11-20')\n : 'gpt-4o-2024-11-20',\n temperature: openAiApiKey ? (temperature ?? 0.1) : 0.1,\n messages: [{ role: 'system', content: prompt }],\n });\n\n const newContent = chatCompletion.choices[0].message?.content;\n\n logger.info(\n `${chatCompletion.usage?.total_tokens} tokens used in the request`\n );\n\n return {\n fileContent: newContent ?? '',\n tokenUsed: chatCompletion.usage?.total_tokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AACA,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAE9B,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAsBxD,MAAM,iBAAiB,CAAC,qBAAqC;AAC3D,QAAM,eAAe,KAAK,WAAW,gBAAgB;AACrD,QAAM,cAAc,aAAa,cAAc,OAAO;AACtD,SAAO;AACT;AAGA,MAAM,kBAAkB,eAAe,aAAa;AAS7C,MAAM,0BAA0B,OAAO;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA8D;AAC5D,MAAI;AAIF,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,QAAQ,gBAAgB,QAAQ,IAAI;AAAA,IACtC,CAAC;AAGD,UAAM,SACJ,gBACA,gBAAgB;AAAA,MACd;AAAA,MACA,GAAG,KAAK;AAAA,QACN,KACG,IAAI,CAAC,EAAE,KAAK,YAAY,MAAM,KAAK,GAAG,KAAK,WAAW,EAAE,EACxD,KAAK,MAAM;AAAA,QACd;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EAAE,QAAQ,0BAA0B,WAAW;AAGjD,UAAM,iBAAiB,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,MAC1D,OAAO,eACF,SAAS,sBACV;AAAA,MACJ,aAAa,eAAgB,eAAe,MAAO;AAAA,MACnD,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,UAAM,aAAa,eAAe,QAAQ,CAAC,EAAE,SAAS;AAEtD,WAAO;AAAA,MACL,GAAG,eAAe,OAAO,YAAY;AAAA,IACvC;AAEA,WAAO;AAAA,MACL,aAAa,cAAc;AAAA,MAC3B,WAAW,eAAe,OAAO,gBAAgB;AAAA,IACnD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/utils/AI/auditDictionaryMetadata/index.ts"],"sourcesContent":["import type { Tag } from '@/types/tag.types';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { AIOptions, AIProvider, getAIConfig } from '../aiSdk';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Get the content of a file at the specified path\nconst getFileContent = (filePath: string) => {\n return readFileSync(join(__dirname, filePath), { encoding: 'utf-8' });\n};\n\nexport type AuditOptions = {\n fileContent: string;\n tags: Tag[];\n aiOptions?: AIOptions;\n};\n\nexport type AuditFileResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n aiOptions,\n tags,\n fileContent,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n try {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{tags}}',\n `${JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )}`\n )\n .replace('{{contentDeclaration}}', fileContent)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '');\n\n // Get the appropriate AI model configuration\n const aiConfig = await getAIConfig({\n provider: AIProvider.OPENAI,\n model: 'gpt-4o-mini',\n apiKey: process.env.OPENAI_API_KEY,\n ...aiOptions,\n });\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n model: aiConfig.model,\n temperature: aiConfig.temperature,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AACA,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAoB,YAAY,mBAAmB;AAEnD,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,MAAM,iBAAiB,CAAC,aAAqB;AAC3C,SAAO,aAAa,KAAK,WAAW,QAAQ,GAAG,EAAE,UAAU,QAAQ,CAAC;AACtE;AAcA,MAAM,kBAAkB,eAAe,aAAa;AAO7C,MAAM,0BAA0B,OAAO;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AACF,MAA8D;AAC5D,MAAI;AAEF,UAAM,SAAS,gBAAgB;AAAA,MAC7B;AAAA,MACA,GAAG,KAAK;AAAA,QACN,KACG,IAAI,CAAC,EAAE,KAAK,YAAY,MAAM,KAAK,GAAG,KAAK,WAAW,EAAE,EACxD,KAAK,MAAM;AAAA,QACd;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EACG,QAAQ,0BAA0B,WAAW,EAC7C,QAAQ,0BAA0B,WAAW,sBAAsB,EAAE;AAGxE,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,UAAU,WAAW;AAAA,MACrB,OAAO;AAAA,MACP,QAAQ,QAAQ,IAAI;AAAA,MACpB,GAAG;AAAA,IACL,CAAC;AAED,QAAI,CAAC,UAAU;AACb,aAAO,MAAM,8BAA8B;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,EAAE,MAAM,YAAY,MAAM,IAAI,MAAM,aAAa;AAAA,MACrD,OAAO,SAAS;AAAA,MAChB,aAAa,SAAS;AAAA,MACtB,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,WAAO,KAAK,GAAG,OAAO,eAAe,CAAC,6BAA6B;AAEnE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW,OAAO,eAAe;AAAA,IACnC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
@@ -21,6 +21,10 @@ Your role is to review a tag. A tag is attached to a content declaration and is
21
21
  - **Do Not Alter Structure:** If the file structure is correct, do not modify it. Only add, update, or remove content declarations as necessary.
22
22
  - **Return Only Final File Content:** Provide the updated file content without any additional comments or explanations.
23
23
 
24
+ **Application Context**
25
+
26
+ {{applicationContext}}
27
+
24
28
  **Tags to Audit:**
25
29
 
26
30
  {{tag}}
@@ -0,0 +1,49 @@
1
+ import { logger } from "./../../../logger/index.mjs";
2
+ import { generateText } from "ai";
3
+ import { readFileSync } from "fs";
4
+ import { dirname, join } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { AIProvider, getAIConfig } from "../aiSdk.mjs";
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const getFileContent = (filePath) => {
9
+ return readFileSync(join(__dirname, filePath), { encoding: "utf-8" });
10
+ };
11
+ const CHAT_GPT_PROMPT = getFileContent("./PROMPT.md");
12
+ const auditTag = async ({
13
+ aiOptions,
14
+ dictionaries,
15
+ tag
16
+ }) => {
17
+ try {
18
+ const prompt = CHAT_GPT_PROMPT.replace(
19
+ "{{tag.description}}",
20
+ tag.description ?? ""
21
+ ).replace("{{tag.key}}", tag.key).replace("{{dictionaries}}", JSON.stringify(dictionaries, null, 2)).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "");
22
+ const aiConfig = await getAIConfig({
23
+ provider: AIProvider.OPENAI,
24
+ model: "gpt-4o-mini",
25
+ apiKey: process.env.OPENAI_API_KEY,
26
+ ...aiOptions
27
+ });
28
+ if (!aiConfig) {
29
+ logger.error("Failed to configure AI model");
30
+ return void 0;
31
+ }
32
+ const { text: newContent, usage } = await generateText({
33
+ model: aiConfig.model,
34
+ temperature: aiConfig.temperature,
35
+ messages: [{ role: "system", content: prompt }]
36
+ });
37
+ logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
38
+ return {
39
+ fileContent: newContent,
40
+ tokenUsed: usage?.totalTokens ?? 0
41
+ };
42
+ } catch (error) {
43
+ console.error(error);
44
+ }
45
+ };
46
+ export {
47
+ auditTag
48
+ };
49
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import type { Dictionary } from '@/types/dictionary.types';\nimport type { Tag } from '@/types/tag.types';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { AIOptions, AIProvider, getAIConfig } from '../aiSdk';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Get the content of a file at the specified path\nconst getFileContent = (filePath: string) => {\n return readFileSync(join(__dirname, filePath), { encoding: 'utf-8' });\n};\n\nexport type AuditOptions = {\n dictionaries: Dictionary[];\n tag: Tag;\n aiOptions?: AIOptions;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Audits a tag by constructing a prompt for AI models.\n * The prompt includes details about the tag and related dictionaries.\n */\nexport const auditTag = async ({\n aiOptions,\n dictionaries,\n tag,\n}: AuditOptions): Promise<TranslateJSONResultData | undefined> => {\n try {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{tag.description}}',\n tag.description ?? ''\n )\n .replace('{{tag.key}}', tag.key)\n .replace('{{dictionaries}}', JSON.stringify(dictionaries, null, 2))\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '');\n\n // Get the appropriate AI model configuration\n const aiConfig = await getAIConfig({\n provider: AIProvider.OPENAI,\n model: 'gpt-4o-mini',\n apiKey: process.env.OPENAI_API_KEY,\n ...aiOptions,\n });\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n model: aiConfig.model,\n temperature: aiConfig.temperature,\n messages: [{ role: 'system', content: prompt }],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AAEA,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAoB,YAAY,mBAAmB;AAEnD,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,MAAM,iBAAiB,CAAC,aAAqB;AAC3C,SAAO,aAAa,KAAK,WAAW,QAAQ,GAAG,EAAE,UAAU,QAAQ,CAAC;AACtE;AAcA,MAAM,kBAAkB,eAAe,aAAa;AAM7C,MAAM,WAAW,OAAO;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,MAAkE;AAChE,MAAI;AAEF,UAAM,SAAS,gBAAgB;AAAA,MAC7B;AAAA,MACA,IAAI,eAAe;AAAA,IACrB,EACG,QAAQ,eAAe,IAAI,GAAG,EAC9B,QAAQ,oBAAoB,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC,EACjE,QAAQ,0BAA0B,WAAW,sBAAsB,EAAE;AAGxE,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,UAAU,WAAW;AAAA,MACrB,OAAO;AAAA,MACP,QAAQ,QAAQ,IAAI;AAAA,MACpB,GAAG;AAAA,IACL,CAAC;AAED,QAAI,CAAC,UAAU;AACb,aAAO,MAAM,8BAA8B;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,EAAE,MAAM,YAAY,MAAM,IAAI,MAAM,aAAa;AAAA,MACrD,OAAO,SAAS;AAAA,MAChB,aAAa,SAAS;AAAA,MACtB,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,WAAO,KAAK,GAAG,OAAO,eAAe,CAAC,6BAA6B;AAEnE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW,OAAO,eAAe;AAAA,IACnC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
@@ -11,3 +11,7 @@ Your completion should not exceed one sentence. Minimize the completion length i
11
11
  The user input will be provided in the next user message: { role: 'user', content: 'xxx' }
12
12
 
13
13
  You should return your autocompletion without any additional text or formatting.
14
+
15
+ **Application Context**
16
+
17
+ {{applicationContext}}
@@ -1,42 +1,45 @@
1
+ import { logger } from "./../../../logger/index.mjs";
2
+ import { generateText } from "ai";
1
3
  import { readFileSync } from "fs";
2
4
  import { dirname, join } from "path";
3
5
  import { fileURLToPath } from "url";
4
- import { logger } from "./../../../logger/index.mjs";
5
- import { OpenAI } from "openai";
6
+ import { AIProvider, getAIConfig } from "../aiSdk.mjs";
6
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const getFileContent = (relativeFilePath) => {
8
- const absolutePath = join(__dirname, relativeFilePath);
9
- const fileContent = readFileSync(absolutePath, "utf-8");
10
- return fileContent;
8
+ const getFileContent = (filePath) => {
9
+ return readFileSync(join(__dirname, filePath), { encoding: "utf-8" });
11
10
  };
12
11
  const CHAT_GPT_PROMPT = getFileContent("./PROMPT.md");
13
12
  const autocomplete = async ({
14
13
  text,
15
- model,
16
- openAiApiKey,
17
- temperature,
18
- customPrompt
14
+ aiOptions
19
15
  }) => {
20
16
  try {
21
- const openai = new OpenAI({
22
- apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY
17
+ const prompt = CHAT_GPT_PROMPT.replace(
18
+ "{{applicationContext}}",
19
+ aiOptions?.applicationContext ?? ""
20
+ );
21
+ const aiConfig = await getAIConfig({
22
+ model: "gpt-4o-mini",
23
+ provider: AIProvider.OPENAI,
24
+ apiKey: process.env.OPENAI_API_KEY,
25
+ ...aiOptions
23
26
  });
24
- const prompt = customPrompt ?? CHAT_GPT_PROMPT;
25
- const chatCompletion = await openai.chat.completions.create({
26
- model: openAiApiKey ? model ?? "gpt-4o-mini" : "gpt-4o-mini",
27
- temperature: openAiApiKey ? temperature ?? 0.1 : 0.1,
27
+ if (!aiConfig) {
28
+ logger.error("Failed to configure AI model");
29
+ return void 0;
30
+ }
31
+ const { text: newContent, usage } = await generateText({
32
+ model: aiConfig.model,
33
+ temperature: aiConfig.temperature,
28
34
  messages: [
29
35
  { role: "system", content: prompt },
30
36
  { role: "user", content: text }
31
37
  ]
32
38
  });
33
- const newContent = chatCompletion.choices[0].message?.content;
34
- logger.info(
35
- `${chatCompletion.usage?.total_tokens} tokens used in the request`
36
- );
39
+ logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
37
40
  return {
38
- autocompletion: newContent ?? "",
39
- tokenUsed: chatCompletion.usage?.total_tokens ?? 0
41
+ autocompletion: newContent,
42
+ tokenUsed: usage?.totalTokens ?? 0
40
43
  };
41
44
  } catch (error) {
42
45
  console.error(error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/utils/AI/autocomplete/index.ts"],"sourcesContent":["import { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { logger } from '@logger';\nimport { OpenAI } from 'openai';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport type AIOptions = {\n model?: string;\n temperature?: number;\n openAiApiKey?: string;\n};\n\nexport type AutocompleteOptions = {\n text: string;\n customPrompt?: string;\n} & AIOptions;\nexport type AutocompleteFileResultData = {\n autocompletion: string;\n tokenUsed: number;\n};\n\n/**\n * Reads the content of a file synchronously.\n *\n * @function\n * @param relativeFilePath - The relative or absolute path to the target file.\n * @returns The entire contents of the specified file as a UTF-8 encoded string.\n */\nconst getFileContent = (relativeFilePath: string): string => {\n const absolutePath = join(__dirname, relativeFilePath);\n const fileContent = readFileSync(absolutePath, 'utf-8');\n return fileContent;\n};\n\n// The prompt template to send to ChatGPT, requesting an audit of content declaration files.\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Autocompletes a content declaration file by constructing a prompt for ChatGPT.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies. It prints the prompt for each file,\n * and could be adapted to send requests to the ChatGPT model.\n *\n */\nexport const autocomplete = async ({\n text,\n model,\n openAiApiKey,\n temperature,\n customPrompt,\n}: AutocompleteOptions): Promise<AutocompleteFileResultData | undefined> => {\n try {\n // Optionally, you could initialize and configure the OpenAI client here, if you intend to make API calls.\n // Uncomment and configure the following lines if you have `openai` installed and want to call the API:\n\n const openai = new OpenAI({\n apiKey: openAiApiKey ?? process.env.OPENAI_API_KEY,\n });\n\n // Prepare the prompt for ChatGPT by replacing placeholders with actual values.\n const prompt = customPrompt ?? CHAT_GPT_PROMPT;\n\n // Example of how you might request a completion from ChatGPT:\n const chatCompletion = await openai.chat.completions.create({\n model: openAiApiKey ? (model ?? 'gpt-4o-mini') : 'gpt-4o-mini',\n temperature: openAiApiKey ? (temperature ?? 0.1) : 0.1,\n messages: [\n { role: 'system', content: prompt },\n { role: 'user', content: text },\n ],\n });\n\n const newContent = chatCompletion.choices[0].message?.content;\n\n logger.info(\n `${chatCompletion.usage?.total_tokens} tokens used in the request`\n );\n\n return {\n autocompletion: newContent ?? '',\n tokenUsed: chatCompletion.usage?.total_tokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,cAAc;AACvB,SAAS,cAAc;AAEvB,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAwBxD,MAAM,iBAAiB,CAAC,qBAAqC;AAC3D,QAAM,eAAe,KAAK,WAAW,gBAAgB;AACrD,QAAM,cAAc,aAAa,cAAc,OAAO;AACtD,SAAO;AACT;AAGA,MAAM,kBAAkB,eAAe,aAAa;AAS7C,MAAM,eAAe,OAAO;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA4E;AAC1E,MAAI;AAIF,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,QAAQ,gBAAgB,QAAQ,IAAI;AAAA,IACtC,CAAC;AAGD,UAAM,SAAS,gBAAgB;AAG/B,UAAM,iBAAiB,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,MAC1D,OAAO,eAAgB,SAAS,gBAAiB;AAAA,MACjD,aAAa,eAAgB,eAAe,MAAO;AAAA,MACnD,UAAU;AAAA,QACR,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,QAClC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,MAChC;AAAA,IACF,CAAC;AAED,UAAM,aAAa,eAAe,QAAQ,CAAC,EAAE,SAAS;AAEtD,WAAO;AAAA,MACL,GAAG,eAAe,OAAO,YAAY;AAAA,IACvC;AAEA,WAAO;AAAA,MACL,gBAAgB,cAAc;AAAA,MAC9B,WAAW,eAAe,OAAO,gBAAgB;AAAA,IACnD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/utils/AI/autocomplete/index.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { generateText } from 'ai';\nimport { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { AIOptions, AIProvider, getAIConfig } from '../aiSdk';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Get the content of a file at the specified path\nconst getFileContent = (filePath: string) => {\n return readFileSync(join(__dirname, filePath), { encoding: 'utf-8' });\n};\n\nexport type AutocompleteOptions = {\n text: string;\n aiOptions?: AIOptions;\n};\n\nexport type AutocompleteFileResultData = {\n autocompletion: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Autocompletes a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const autocomplete = async ({\n text,\n aiOptions,\n}: AutocompleteOptions): Promise<AutocompleteFileResultData | undefined> => {\n try {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n aiOptions?.applicationContext ?? ''\n );\n\n // Get the appropriate AI model configuration\n const aiConfig = await getAIConfig({\n model: 'gpt-4o-mini',\n provider: AIProvider.OPENAI,\n apiKey: process.env.OPENAI_API_KEY,\n ...aiOptions,\n });\n\n if (!aiConfig) {\n logger.error('Failed to configure AI model');\n return undefined;\n }\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n model: aiConfig.model,\n temperature: aiConfig.temperature,\n messages: [\n { role: 'system', content: prompt },\n { role: 'user', content: text },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n autocompletion: newContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAoB,YAAY,mBAAmB;AAEnD,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,MAAM,iBAAiB,CAAC,aAAqB;AAC3C,SAAO,aAAa,KAAK,WAAW,QAAQ,GAAG,EAAE,UAAU,QAAQ,CAAC;AACtE;AAaA,MAAM,kBAAkB,eAAe,aAAa;AAO7C,MAAM,eAAe,OAAO;AAAA,EACjC;AAAA,EACA;AACF,MAA4E;AAC1E,MAAI;AAEF,UAAM,SAAS,gBAAgB;AAAA,MAC7B;AAAA,MACA,WAAW,sBAAsB;AAAA,IACnC;AAGA,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,OAAO;AAAA,MACP,UAAU,WAAW;AAAA,MACrB,QAAQ,QAAQ,IAAI;AAAA,MACpB,GAAG;AAAA,IACL,CAAC;AAED,QAAI,CAAC,UAAU;AACb,aAAO,MAAM,8BAA8B;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,EAAE,MAAM,YAAY,MAAM,IAAI,MAAM,aAAa;AAAA,MACrD,OAAO,SAAS;AAAA,MAChB,aAAa,SAAS;AAAA,MACtB,UAAU;AAAA,QACR,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,QAClC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,MAChC;AAAA,IACF,CAAC;AAED,WAAO,KAAK,GAAG,OAAO,eAAe,CAAC,6BAA6B;AAEnE,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,WAAW,OAAO,eAAe;AAAA,IACnC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}