@intlayer/backend 7.2.0 → 7.2.1-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.
- package/dist/assets/utils/AI/auditDictionary/PROMPT.md +0 -12
- package/dist/assets/utils/AI/auditDictionaryField/PROMPT.md +0 -13
- package/dist/assets/utils/AI/auditDictionaryMetadata/PROMPT.md +0 -6
- package/dist/assets/utils/AI/auditTag/PROMPT.md +0 -10
- package/dist/assets/utils/AI/translateJSON/PROMPT.md +0 -8
- package/dist/cjs/controllers/ai.controller.cjs +0 -1
- package/dist/cjs/controllers/ai.controller.cjs.map +1 -1
- package/dist/cjs/utils/AI/aiSdk.cjs +12 -21
- package/dist/cjs/utils/AI/aiSdk.cjs.map +1 -1
- package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs +0 -9
- package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs.map +1 -1
- package/dist/cjs/utils/AI/askDocQuestion/indexMarkdownFiles.cjs +0 -9
- package/dist/cjs/utils/AI/askDocQuestion/indexMarkdownFiles.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditDictionary/index.cjs +4 -1
- package/dist/cjs/utils/AI/auditDictionary/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditDictionaryField/index.cjs +4 -1
- package/dist/cjs/utils/AI/auditDictionaryField/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditDictionaryMetadata/index.cjs +8 -1
- package/dist/cjs/utils/AI/auditDictionaryMetadata/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/auditTag/index.cjs +8 -1
- package/dist/cjs/utils/AI/auditTag/index.cjs.map +1 -1
- package/dist/cjs/utils/AI/translateJSON/index.cjs +8 -1
- package/dist/cjs/utils/AI/translateJSON/index.cjs.map +1 -1
- package/dist/esm/controllers/ai.controller.mjs +15 -16
- package/dist/esm/controllers/ai.controller.mjs.map +1 -1
- package/dist/esm/utils/AI/aiSdk.mjs +12 -21
- package/dist/esm/utils/AI/aiSdk.mjs.map +1 -1
- package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs +1 -9
- package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs.map +1 -1
- package/dist/esm/utils/AI/askDocQuestion/indexMarkdownFiles.mjs +1 -9
- package/dist/esm/utils/AI/askDocQuestion/indexMarkdownFiles.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionary/index.mjs +4 -1
- package/dist/esm/utils/AI/auditDictionary/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionaryField/index.mjs +4 -1
- package/dist/esm/utils/AI/auditDictionaryField/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditDictionaryMetadata/index.mjs +8 -1
- package/dist/esm/utils/AI/auditDictionaryMetadata/index.mjs.map +1 -1
- package/dist/esm/utils/AI/auditTag/index.mjs +8 -1
- package/dist/esm/utils/AI/auditTag/index.mjs.map +1 -1
- package/dist/esm/utils/AI/translateJSON/index.mjs +8 -1
- package/dist/esm/utils/AI/translateJSON/index.mjs.map +1 -1
- package/dist/types/controllers/ai.controller.d.ts.map +1 -1
- package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
- package/dist/types/emails/InviteUserEmail.d.ts +4 -4
- package/dist/types/emails/InviteUserEmail.d.ts.map +1 -1
- package/dist/types/emails/MagicLinkEmail.d.ts +4 -4
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +4 -4
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +4 -4
- package/dist/types/emails/PasswordChangeConfirmation.d.ts.map +1 -1
- package/dist/types/emails/ResetUserPassword.d.ts +4 -4
- package/dist/types/emails/ResetUserPassword.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentError.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts.map +1 -1
- package/dist/types/emails/ValidateUserEmail.d.ts +4 -4
- package/dist/types/emails/Welcome.d.ts +4 -4
- package/dist/types/models/dictionary.model.d.ts +4 -4
- package/dist/types/models/dictionary.model.d.ts.map +1 -1
- package/dist/types/models/discussion.model.d.ts +2 -2
- package/dist/types/models/discussion.model.d.ts.map +1 -1
- package/dist/types/models/oAuth2.model.d.ts +3 -3
- package/dist/types/models/oAuth2.model.d.ts.map +1 -1
- package/dist/types/routes/search.routes.d.ts.map +1 -1
- package/dist/types/routes/stripe.routes.d.ts.map +1 -1
- package/dist/types/schemas/dictionary.schema.d.ts +6 -6
- package/dist/types/schemas/dictionary.schema.d.ts.map +1 -1
- package/dist/types/schemas/discussion.schema.d.ts +6 -6
- package/dist/types/schemas/oAuth2.schema.d.ts +5 -5
- package/dist/types/schemas/oAuth2.schema.d.ts.map +1 -1
- package/dist/types/schemas/organization.schema.d.ts +6 -6
- package/dist/types/schemas/plans.schema.d.ts +6 -6
- package/dist/types/schemas/project.schema.d.ts +6 -6
- package/dist/types/schemas/session.schema.d.ts +6 -6
- package/dist/types/schemas/tag.schema.d.ts +6 -6
- package/dist/types/schemas/tag.schema.d.ts.map +1 -1
- package/dist/types/schemas/user.schema.d.ts +6 -6
- package/dist/types/services/email.service.d.ts +11 -11
- package/dist/types/utils/AI/aiSdk.d.ts.map +1 -1
- package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts +2 -3
- package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts.map +1 -1
- package/dist/types/utils/AI/askDocQuestion/indexMarkdownFiles.d.ts +1 -4
- package/dist/types/utils/AI/askDocQuestion/indexMarkdownFiles.d.ts.map +1 -1
- package/dist/types/utils/AI/translateJSON/index.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts +2 -2
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +2 -2
- package/package.json +1 -1
|
@@ -36,12 +36,15 @@ const formatTagInstructions = (tags) => {
|
|
|
36
36
|
*/
|
|
37
37
|
const auditDictionary = async ({ fileContent, filePath, locales, defaultLocale, tags, aiConfig, applicationContext }) => {
|
|
38
38
|
const otherLocales = locales.filter((locale) => locale !== defaultLocale);
|
|
39
|
-
const prompt = CHAT_GPT_PROMPT.replace("{{defaultLocale}}", formatLocaleWithName(defaultLocale)).replace("{{otherLocales}}", `{${otherLocales.map(formatLocaleWithName).join(", ")}}`).replace("{{filePath}}", filePath ?? "").replace("{{
|
|
39
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{defaultLocale}}", formatLocaleWithName(defaultLocale)).replace("{{otherLocales}}", `{${otherLocales.map(formatLocaleWithName).join(", ")}}`).replace("{{filePath}}", filePath ?? "").replace("{{applicationContext}}", applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags));
|
|
40
40
|
const { text: newContent, usage } = await (0, ai.generateText)({
|
|
41
41
|
...aiConfig,
|
|
42
42
|
messages: [{
|
|
43
43
|
role: "system",
|
|
44
44
|
content: prompt
|
|
45
|
+
}, {
|
|
46
|
+
role: "user",
|
|
47
|
+
content: ["**File to Audit:**", fileContent].join("\n")
|
|
45
48
|
}]
|
|
46
49
|
});
|
|
47
50
|
require_logger_index.logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n filePath?: string;\n locales: Locale[];\n defaultLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\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: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\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) return '';\n\n // Prepare the tag instructions.\n return [\n `Based on the dictionary content, identify specific tags from the list below that would be relevant:`,\n tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n'),\n ].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 locales,\n defaultLocale,\n tags,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n const otherLocales = locales.filter((locale) => locale !== defaultLocale);\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('{{
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionary/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n filePath?: string;\n locales: Locale[];\n defaultLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\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: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\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) return '';\n\n // Prepare the tag instructions.\n return [\n `Based on the dictionary content, identify specific tags from the list below that would be relevant:`,\n tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n'),\n ].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 locales,\n defaultLocale,\n tags,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n const otherLocales = locales.filter((locale) => locale !== defaultLocale);\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('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n content: ['**File to Audit:**', fileContent].join('\\n'),\n },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;;AA6BA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;;;AAQD,MAAM,wBAAwB,WAA2B;AACvD,QAAO,GAAG,OAAO,uCAAkB,QAAQC,yBAAQ,QAAQ;;;;;;;;;AAU7D,MAAM,yBAAyB,SAAwB;AACrD,KAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AAGvC,QAAO,CACL,uGACA,KAAK,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CAAC,KAAK,OAAO,CAC5E,CAAC,KAAK,OAAO;;;;;;;AAQhB,MAAa,kBAAkB,OAAO,EACpC,aACA,UACA,SACA,eACA,MACA,UACA,yBAC4D;CAC5D,MAAM,eAAe,QAAQ,QAAQ,WAAW,WAAW,cAAc;CAGzE,MAAM,SAAS,gBAAgB,QAC7B,qBACA,qBAAqB,cAAc,CACpC,CACE,QACC,oBACA,IAAI,aAAa,IAAI,qBAAqB,CAAC,KAAK,KAAK,CAAC,GACvD,CACA,QAAQ,gBAAgB,YAAY,GAAG,CACvC,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC;CAG/D,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GACN,SAAS,CAAC,sBAAsB,YAAY,CAAC,KAAK,KAAK;GACxD,CACF;EACF,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -35,7 +35,7 @@ ${tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n")}`;
|
|
|
35
35
|
* and requests for identifying issues or inconsistencies.
|
|
36
36
|
*/
|
|
37
37
|
const auditDictionaryField = async ({ fileContent, applicationContext, locales, keyPath, tags, aiConfig }) => {
|
|
38
|
-
const prompt = CHAT_GPT_PROMPT.replace("{{otherLocales}}", `{${locales.map(formatLocaleWithName).join(", ")}}`).replace("{{keyPath}}", JSON.stringify(keyPath)).replace("{{
|
|
38
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{otherLocales}}", `{${locales.map(formatLocaleWithName).join(", ")}}`).replace("{{keyPath}}", JSON.stringify(keyPath)).replace("{{applicationContext}}", applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags));
|
|
39
39
|
if (!aiConfig) {
|
|
40
40
|
require_logger_index.logger.error("Failed to configure AI model");
|
|
41
41
|
return;
|
|
@@ -45,6 +45,9 @@ const auditDictionaryField = async ({ fileContent, applicationContext, locales,
|
|
|
45
45
|
messages: [{
|
|
46
46
|
role: "system",
|
|
47
47
|
content: prompt
|
|
48
|
+
}, {
|
|
49
|
+
role: "user",
|
|
50
|
+
content: ["**File to Audit:**", fileContent].join("\n")
|
|
48
51
|
}]
|
|
49
52
|
});
|
|
50
53
|
require_logger_index.logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales"],"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport type { KeyPath } from '@intlayer/types';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditDictionaryFieldOptions = {\n fileContent: string;\n locales: Locale[];\n keyPath: KeyPath[];\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\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 = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\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: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\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 applicationContext,\n locales,\n keyPath,\n tags,\n aiConfig,\n}: AuditDictionaryFieldOptions): Promise<\n AuditDictionaryFieldResultData | undefined\n> => {\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('{{
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","Locales"],"sources":["../../../../../src/utils/AI/auditDictionaryField/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport type { KeyPath } from '@intlayer/types';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditDictionaryFieldOptions = {\n fileContent: string;\n locales: Locale[];\n keyPath: KeyPath[];\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\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 = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\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: Locale): string => {\n return `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\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 applicationContext,\n locales,\n keyPath,\n tags,\n aiConfig,\n}: AuditDictionaryFieldOptions): Promise<\n AuditDictionaryFieldResultData | undefined\n> => {\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('{{applicationContext}}', 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 ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n content: ['**File to Audit:**', fileContent].join('\\n'),\n },\n ],\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};\n"],"mappings":";;;;;;;;AAwBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;;;AAQD,MAAM,wBAAwB,WAA2B;AACvD,QAAO,GAAG,OAAO,uCAAkB,QAAQC,yBAAQ,QAAQ;;;;;;;;AAS7D,MAAM,yBAAyB,SAAwB;AACrD,KAAI,CAAC,QAAQ,KAAK,WAAW,EAC3B,QAAO;AAGT,QAAO;;EAEP,KAAK,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CAAC,KAAK,OAAO;;;;;;;AAQ7E,MAAa,uBAAuB,OAAO,EACzC,aACA,oBACA,SACA,SACA,MACA,eAGG;CAEH,MAAM,SAAS,gBAAgB,QAC7B,oBACA,IAAI,QAAQ,IAAI,qBAAqB,CAAC,KAAK,KAAK,CAAC,GAClD,CACE,QAAQ,eAAe,KAAK,UAAU,QAAQ,CAAC,CAC/C,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC;AAE/D,KAAI,CAAC,UAAU;AACb,8BAAO,MAAM,+BAA+B;AAC5C;;CAIF,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GACN,SAAS,CAAC,sBAAsB,YAAY,CAAC,KAAK,KAAK;GACxD,CACF;EACF,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -13,7 +13,7 @@ const aiDefaultOptions = {};
|
|
|
13
13
|
* and requests for identifying issues or inconsistencies.
|
|
14
14
|
*/
|
|
15
15
|
const auditDictionaryMetadata = async ({ tags, fileContent, applicationContext, aiConfig }) => {
|
|
16
|
-
const prompt = CHAT_GPT_PROMPT.replace("{{tags}}", `${JSON.stringify(tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n"), null, 2)}`).replace("{{
|
|
16
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{tags}}", `${JSON.stringify(tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n"), null, 2)}`).replace("{{applicationContext}}", applicationContext ?? "");
|
|
17
17
|
if (!aiConfig) {
|
|
18
18
|
require_logger_index.logger.error("Failed to configure AI model");
|
|
19
19
|
return;
|
|
@@ -23,6 +23,13 @@ const auditDictionaryMetadata = async ({ tags, fileContent, applicationContext,
|
|
|
23
23
|
messages: [{
|
|
24
24
|
role: "system",
|
|
25
25
|
content: prompt
|
|
26
|
+
}, {
|
|
27
|
+
role: "user",
|
|
28
|
+
content: [
|
|
29
|
+
"**Content declaration to describe:**",
|
|
30
|
+
"This is the content declaration that you should consider to describe:",
|
|
31
|
+
fileContent
|
|
32
|
+
].join("\n")
|
|
26
33
|
}]
|
|
27
34
|
});
|
|
28
35
|
require_logger_index.logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\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 auditDictionaryMetadata = async ({\n tags,\n fileContent,\n applicationContext,\n aiConfig,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\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 )
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n fileContent: string;\n tags: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\n// The prompt template to send to AI models\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\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 auditDictionaryMetadata = async ({\n tags,\n fileContent,\n applicationContext,\n aiConfig,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\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 ).replace('{{applicationContext}}', applicationContext ?? '');\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 ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n content: [\n '**Content declaration to describe:**',\n 'This is the content declaration that you should consider to describe:',\n fileContent,\n ].join('\\n'),\n },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;AAwBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;;AAOD,MAAa,0BAA0B,OAAO,EAC5C,MACA,aACA,oBACA,eAC4D;CAE5D,MAAM,SAAS,gBAAgB,QAC7B,YACA,GAAG,KAAK,UACN,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CACzD,KAAK,OAAO,EACf,MACA,EACD,GACF,CAAC,QAAQ,0BAA0B,sBAAsB,GAAG;AAE7D,KAAI,CAAC,UAAU;AACb,8BAAO,MAAM,+BAA+B;AAC5C;;CAIF,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GACN,SAAS;IACP;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACb,CACF;EACF,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -12,12 +12,19 @@ const aiDefaultOptions = {};
|
|
|
12
12
|
* The prompt includes details about the tag and related dictionaries.
|
|
13
13
|
*/
|
|
14
14
|
const auditTag = async ({ dictionaries, tag, aiConfig, applicationContext }) => {
|
|
15
|
-
const prompt = CHAT_GPT_PROMPT.replace("{{tag.description}}", tag.description ?? "").replace("{{tag.key}}", tag.key).replace("{{
|
|
15
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{tag.description}}", tag.description ?? "").replace("{{tag.key}}", tag.key).replace("{{applicationContext}}", applicationContext ?? "");
|
|
16
16
|
const { text: newContent, usage } = await (0, ai.generateText)({
|
|
17
17
|
...aiConfig,
|
|
18
18
|
messages: [{
|
|
19
19
|
role: "system",
|
|
20
20
|
content: prompt
|
|
21
|
+
}, {
|
|
22
|
+
role: "user",
|
|
23
|
+
content: [
|
|
24
|
+
"**Tag to audit:**",
|
|
25
|
+
"This is the tag that you should consider to audit:",
|
|
26
|
+
JSON.stringify(tag)
|
|
27
|
+
].join("\n")
|
|
21
28
|
}]
|
|
22
29
|
});
|
|
23
30
|
require_logger_index.logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { TagAPI } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n dictionaries: Dictionary[];\n tag: TagAPI;\n aiConfig: AIConfig;\n applicationContext?: string;\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 = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\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 dictionaries,\n tag,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<TranslateJSONResultData | undefined> => {\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('{{
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","extractJson"],"sources":["../../../../../src/utils/AI/auditTag/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { TagAPI } from '@/types/tag.types';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\nexport type AuditOptions = {\n dictionaries: Dictionary[];\n tag: TagAPI;\n aiConfig: AIConfig;\n applicationContext?: string;\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 = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\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 dictionaries,\n tag,\n aiConfig,\n applicationContext,\n}: AuditOptions): Promise<TranslateJSONResultData | undefined> => {\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('{{applicationContext}}', applicationContext ?? '');\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n content: [\n '**Tag to audit:**',\n 'This is the tag that you should consider to audit:',\n JSON.stringify(tag),\n ].join('\\n'),\n },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;AAqBA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B,EAE1C;;;;;AAMD,MAAa,WAAW,OAAO,EAC7B,cACA,KACA,UACA,yBACgE;CAEhE,MAAM,SAAS,gBAAgB,QAC7B,uBACA,IAAI,eAAe,GACpB,CACE,QAAQ,eAAe,IAAI,IAAI,CAC/B,QAAQ,0BAA0B,sBAAsB,GAAG;CAG9D,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GACN,SAAS;IACP;IACA;IACA,KAAK,UAAU,IAAI;IACpB,CAAC,KAAK,KAAK;GACb,CACF;EACF,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -43,12 +43,19 @@ const getModeInstructions = (mode) => {
|
|
|
43
43
|
* and requests for identifying issues or inconsistencies.
|
|
44
44
|
*/
|
|
45
45
|
const translateJSON = async ({ entryFileContent, presetOutputContent, dictionaryDescription, aiConfig, entryLocale, outputLocale, tags, mode, applicationContext }) => {
|
|
46
|
-
const prompt = CHAT_GPT_PROMPT.replace("{{entryLocale}}", formatLocaleWithName(entryLocale)).replace("{{outputLocale}}", formatLocaleWithName(outputLocale)).replace("{{
|
|
46
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{entryLocale}}", formatLocaleWithName(entryLocale)).replace("{{outputLocale}}", formatLocaleWithName(outputLocale)).replace("{{presetOutputContent}}", JSON.stringify(presetOutputContent)).replace("{{dictionaryDescription}}", dictionaryDescription ?? "").replace("{{applicationContext}}", applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags)).replace("{{modeInstructions}}", getModeInstructions(mode));
|
|
47
47
|
const { text: newContent, usage } = await (0, ai.generateText)({
|
|
48
48
|
...aiConfig,
|
|
49
49
|
messages: [{
|
|
50
50
|
role: "system",
|
|
51
51
|
content: prompt
|
|
52
|
+
}, {
|
|
53
|
+
role: "user",
|
|
54
|
+
content: [
|
|
55
|
+
"**Entry Content to Translate:**",
|
|
56
|
+
"- Given Language: {{entryLocale}}",
|
|
57
|
+
JSON.stringify(entryFileContent)
|
|
58
|
+
].join("\n")
|
|
52
59
|
}]
|
|
53
60
|
});
|
|
54
61
|
require_logger_index.logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","AIProvider","Locales","extractJson"],"sources":["../../../../../src/utils/AI/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\nexport type TranslateJSONOptions = {\n entryFileContent: JSON;\n presetOutputContent: JSON;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\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: Locale): string =>\n `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\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\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\n/**\n * TranslateJSONs 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 translateJSON = async ({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions): Promise<TranslateJSONResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{entryLocale}}',\n formatLocaleWithName(entryLocale)\n )\n .replace('{{outputLocale}}', formatLocaleWithName(outputLocale))\n .replace('{{
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["readAsset","aiDefaultOptions: AIOptions","AIProvider","Locales","extractJson"],"sources":["../../../../../src/utils/AI/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { extractJson } from '@utils/extractJSON';\nimport { generateText } from 'ai';\nimport type { Tag } from '@/types/tag.types';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\nexport type TranslateJSONOptions = {\n entryFileContent: JSON;\n presetOutputContent: JSON;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData = {\n fileContent: string;\n tokenUsed: number;\n};\n\n// The prompt template to send to the AI model\nconst CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\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: Locale): string =>\n `${locale}: ${getLocaleName(locale, Locales.ENGLISH)}`;\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\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\n/**\n * TranslateJSONs 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 translateJSON = async ({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions): Promise<TranslateJSONResultData | undefined> => {\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{entryLocale}}',\n formatLocaleWithName(entryLocale)\n )\n .replace('{{outputLocale}}', formatLocaleWithName(outputLocale))\n .replace('{{presetOutputContent}}', JSON.stringify(presetOutputContent))\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n // Use the AI SDK to generate the completion\n const { text: newContent, usage } = await generateText({\n ...aiConfig,\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n content: [\n '**Entry Content to Translate:**',\n '- Given Language: {{entryLocale}}',\n JSON.stringify(entryFileContent),\n ].join('\\n'),\n },\n ],\n });\n\n logger.info(`${usage?.totalTokens ?? 0} tokens used in the request`);\n\n return {\n fileContent: extractJson(newContent),\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;;;AA2BA,MAAM,kBAAkBA,+BAAU,cAAc;AAEhD,MAAaC,mBAA8B;CACzC,UAAUC,kCAAW;CACrB,OAAO;CACR;;;;;;;AAQD,MAAM,wBAAwB,WAC5B,GAAG,OAAO,uCAAkB,QAAQC,yBAAQ,QAAQ;;;;;;;;AAStD,MAAM,yBAAyB,SAAwB;AACrD,KAAI,CAAC,QAAQ,KAAK,WAAW,EAC3B,QAAO;AAIT,QAAO;;EAEP,KAAK,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CAAC,KAAK,OAAO;;AAG7E,MAAM,uBAAuB,SAAwC;AACnE,KAAI,SAAS,WACX,QAAO;AAGT,QAAO;;;;;;;AAQT,MAAa,gBAAgB,OAAO,EAClC,kBACA,qBACA,uBACA,UACA,aACA,cACA,MACA,MACA,yBACwE;CAExE,MAAM,SAAS,gBAAgB,QAC7B,mBACA,qBAAqB,YAAY,CAClC,CACE,QAAQ,oBAAoB,qBAAqB,aAAa,CAAC,CAC/D,QAAQ,2BAA2B,KAAK,UAAU,oBAAoB,CAAC,CACvE,QAAQ,6BAA6B,yBAAyB,GAAG,CACjE,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,KAAK,CAAC,CAC5D,QAAQ,wBAAwB,oBAAoB,KAAK,CAAC;CAG7D,MAAM,EAAE,MAAM,YAAY,UAAU,2BAAmB;EACrD,GAAG;EACH,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GACN,SAAS;IACP;IACA;IACA,KAAK,UAAU,iBAAiB;IACjC,CAAC,KAAK,KAAK;GACb,CACF;EACF,CAAC;AAEF,6BAAO,KAAK,GAAG,OAAO,eAAe,EAAE,6BAA6B;AAEpE,QAAO;EACL,aAAaC,sCAAY,WAAW;EACpC,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -3,14 +3,14 @@ import { ErrorHandler } from "../utils/errors/ErrorHandler.mjs";
|
|
|
3
3
|
import { getDictionariesByTags } from "../services/dictionary.service.mjs";
|
|
4
4
|
import { findTags, getTagsByKeys } from "../services/tag.service.mjs";
|
|
5
5
|
import { getAIConfig } from "../utils/AI/aiSdk.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { aiDefaultOptions
|
|
8
|
-
import { aiDefaultOptions as aiDefaultOptions$
|
|
9
|
-
import { aiDefaultOptions as aiDefaultOptions$
|
|
10
|
-
import { aiDefaultOptions as aiDefaultOptions$
|
|
11
|
-
import { aiDefaultOptions as aiDefaultOptions$
|
|
12
|
-
import { aiDefaultOptions as aiDefaultOptions$
|
|
13
|
-
import { aiDefaultOptions as aiDefaultOptions$
|
|
6
|
+
import { askDocQuestion as askDocQuestion$1 } from "../utils/AI/askDocQuestion/askDocQuestion.mjs";
|
|
7
|
+
import { aiDefaultOptions, auditDictionary } from "../utils/AI/auditDictionary/index.mjs";
|
|
8
|
+
import { aiDefaultOptions as aiDefaultOptions$1, auditDictionaryField } from "../utils/AI/auditDictionaryField/index.mjs";
|
|
9
|
+
import { aiDefaultOptions as aiDefaultOptions$2, auditDictionaryMetadata } from "../utils/AI/auditDictionaryMetadata/index.mjs";
|
|
10
|
+
import { aiDefaultOptions as aiDefaultOptions$3, auditTag as auditTag$1 } from "../utils/AI/auditTag/index.mjs";
|
|
11
|
+
import { aiDefaultOptions as aiDefaultOptions$4, autocomplete as autocomplete$1 } from "../utils/AI/autocomplete/index.mjs";
|
|
12
|
+
import { aiDefaultOptions as aiDefaultOptions$5, customQuery as customQuery$1 } from "../utils/AI/customQuery/index.mjs";
|
|
13
|
+
import { aiDefaultOptions as aiDefaultOptions$6, translateJSON as translateJSON$1 } from "../utils/AI/translateJSON/index.mjs";
|
|
14
14
|
import { getDiscussionFiltersAndPagination } from "../utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs";
|
|
15
15
|
import { DiscussionModel } from "../models/discussion.model.mjs";
|
|
16
16
|
|
|
@@ -21,7 +21,7 @@ const customQuery = async (req, res, _next) => {
|
|
|
21
21
|
try {
|
|
22
22
|
aiConfig = await getAIConfig(res, {
|
|
23
23
|
userOptions: aiOptions,
|
|
24
|
-
defaultOptions: aiDefaultOptions$
|
|
24
|
+
defaultOptions: aiDefaultOptions$5,
|
|
25
25
|
accessType: ["registered_user", "apiKey"]
|
|
26
26
|
});
|
|
27
27
|
} catch (_error) {
|
|
@@ -53,7 +53,7 @@ const translateJSON = async (req, res, _next) => {
|
|
|
53
53
|
try {
|
|
54
54
|
aiConfig = await getAIConfig(res, {
|
|
55
55
|
userOptions: aiOptions,
|
|
56
|
-
defaultOptions: aiDefaultOptions$
|
|
56
|
+
defaultOptions: aiDefaultOptions$6,
|
|
57
57
|
accessType: ["registered_user", "apiKey"]
|
|
58
58
|
});
|
|
59
59
|
} catch (_error) {
|
|
@@ -91,7 +91,7 @@ const auditContentDeclaration = async (req, res, _next) => {
|
|
|
91
91
|
try {
|
|
92
92
|
aiConfig = await getAIConfig(res, {
|
|
93
93
|
userOptions: aiOptions,
|
|
94
|
-
defaultOptions: aiDefaultOptions
|
|
94
|
+
defaultOptions: aiDefaultOptions,
|
|
95
95
|
accessType: ["registered_user", "apiKey"]
|
|
96
96
|
});
|
|
97
97
|
} catch (_error) {
|
|
@@ -132,7 +132,7 @@ const auditContentDeclarationField = async (req, res, _next) => {
|
|
|
132
132
|
try {
|
|
133
133
|
aiConfig = await getAIConfig(res, {
|
|
134
134
|
userOptions: aiOptions,
|
|
135
|
-
defaultOptions: aiDefaultOptions$
|
|
135
|
+
defaultOptions: aiDefaultOptions$1,
|
|
136
136
|
accessType: ["registered_user", "apiKey"]
|
|
137
137
|
});
|
|
138
138
|
} catch (_error) {
|
|
@@ -172,7 +172,7 @@ const auditContentDeclarationMetadata = async (req, res, _next) => {
|
|
|
172
172
|
try {
|
|
173
173
|
aiConfig = await getAIConfig(res, {
|
|
174
174
|
userOptions: aiOptions,
|
|
175
|
-
defaultOptions: aiDefaultOptions$
|
|
175
|
+
defaultOptions: aiDefaultOptions$2,
|
|
176
176
|
accessType: ["registered_user", "apiKey"]
|
|
177
177
|
});
|
|
178
178
|
} catch (_error) {
|
|
@@ -209,7 +209,7 @@ const auditTag = async (req, res, _next) => {
|
|
|
209
209
|
try {
|
|
210
210
|
aiConfig = await getAIConfig(res, {
|
|
211
211
|
userOptions: aiOptions,
|
|
212
|
-
defaultOptions: aiDefaultOptions$
|
|
212
|
+
defaultOptions: aiDefaultOptions$3,
|
|
213
213
|
accessType: ["registered_user", "apiKey"]
|
|
214
214
|
});
|
|
215
215
|
} catch (_error) {
|
|
@@ -244,7 +244,6 @@ const askDocQuestion = async (req, res, _next) => {
|
|
|
244
244
|
try {
|
|
245
245
|
aiConfig = await getAIConfig(res, {
|
|
246
246
|
userOptions: {},
|
|
247
|
-
defaultOptions: aiDefaultOptions,
|
|
248
247
|
accessType: ["public"]
|
|
249
248
|
});
|
|
250
249
|
} catch (_error) {
|
|
@@ -299,7 +298,7 @@ const autocomplete = async (req, res, _next) => {
|
|
|
299
298
|
try {
|
|
300
299
|
aiConfig = await getAIConfig(res, {
|
|
301
300
|
userOptions: aiOptions,
|
|
302
|
-
defaultOptions: aiDefaultOptions$
|
|
301
|
+
defaultOptions: aiDefaultOptions$4,
|
|
303
302
|
accessType: ["public"]
|
|
304
303
|
});
|
|
305
304
|
} catch (_error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.controller.mjs","names":["aiConfig: AIConfig","customQueryUtil.aiDefaultOptions","customQueryUtil.customQuery","translateJSONUtil.aiDefaultOptions","tags: Tag[]","translateJSONUtil.translateJSON","auditContentDeclarationUtil.aiDefaultOptions","auditContentDeclarationUtil.auditDictionary","auditContentDeclarationFieldUtil.aiDefaultOptions","auditContentDeclarationFieldUtil.auditDictionaryField","auditContentDeclarationMetadataUtil.aiDefaultOptions","tagService.findTags","auditContentDeclarationMetadataUtil.auditDictionaryMetadata","auditTagUtil.aiDefaultOptions","dictionaries: Dictionary[]","auditTagUtil.auditTag","askDocQuestionUtil.aiDefaultOptions","autocompleteUtil.aiDefaultOptions","autocompleteUtil.autocomplete","numberOfMessagesById: Record<string, number>"],"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import type { KeyPath, Locale } from '@intlayer/types';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport {\n type AIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n getAIConfig,\n} from '@utils/AI/aiSdk';\nimport * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';\nimport * as auditContentDeclarationUtil from '@utils/AI/auditDictionary';\nimport * as auditContentDeclarationFieldUtil from '@utils/AI/auditDictionaryField';\nimport * as auditContentDeclarationMetadataUtil from '@utils/AI/auditDictionaryMetadata';\nimport * as auditTagUtil from '@utils/AI/auditTag';\nimport * as autocompleteUtil from '@utils/AI/autocomplete';\nimport * as customQueryUtil from '@utils/AI/customQuery';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DiscussionFiltersParams,\n getDiscussionFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDiscussionFiltersAndPagination';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { DiscussionModel } from '@/models/discussion.model';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { DiscussionAPI } from '@/types/discussion.types';\nimport type { Tag, TagAPI } from '@/types/tag.types';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions> & {\n tagsKeys?: string[];\n applicationContext?: string;\n };\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n req: Request<CustomQueryBody>,\n res: ResponseWithSession<CustomQueryResult>,\n _next: NextFunction\n): Promise<void> => {\n // biome-ignore lint/correctness/noUnusedVariables: Just filter out tagsKeys\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'QUERY_FAILED');\n return;\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult =\n ResponseData<translateJSONUtil.TranslateJSONResultData>;\n\nexport const translateJSON = async (\n req: Request<TranslateJSONBody>,\n res: ResponseWithSession<TranslateJSONResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId && tagsKeys) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await translateJSONUtil.translateJSON({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<translateJSONUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n defaultLocale: Locale;\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n};\nexport type AuditContentDeclarationResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclaration = async (\n req: Request<AuditContentDeclarationBody>,\n res: ResponseWithSession<AuditContentDeclarationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await auditContentDeclarationUtil.auditDictionary({\n fileContent,\n filePath,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n defaultLocale,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n req: Request<AuditContentDeclarationFieldBody>,\n res: ResponseWithSession<AuditContentDeclarationFieldResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse =\n await auditContentDeclarationFieldUtil.auditDictionaryField({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n tags,\n keyPath,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>(\n {\n data: auditResponse,\n }\n );\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\n\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationMetadataUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n req: Request<AuditContentDeclarationMetadataBody>,\n res: ResponseWithSession<AuditContentDeclarationMetadataResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization } = res.locals;\n const { fileContent, aiOptions } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n const tags: Tag[] = await tagService.findTags(\n {\n organizationId: organization?.id,\n },\n 0,\n 1000\n );\n\n const auditResponse =\n await auditContentDeclarationMetadataUtil.auditDictionaryMetadata({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationMetadataUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditTagBody = {\n aiOptions?: AIOptions;\n tag: TagAPI;\n};\nexport type AuditTagResult = ResponseData<auditTagUtil.TranslateJSONResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n req: Request<undefined, undefined, AuditTagBody>,\n res: ResponseWithSession<AuditTagResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tag } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let dictionaries: Dictionary[] = [];\n if (project?.organizationId) {\n dictionaries = await getDictionariesByTags([tag.key], project.id);\n }\n\n const auditResponse = await auditTagUtil.auditTag({\n aiConfig,\n dictionaries,\n tag,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData = formatResponse<auditTagUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n req: Request<undefined, undefined, AskDocQuestionBody>,\n res: ResponseWithSession<AskDocQuestionResult>,\n _next: NextFunction\n): Promise<void> => {\n const { messages = [], discussionId } = req.body;\n const { user, project, organization } = res.locals;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: {},\n defaultOptions: askDocQuestionUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n // 1. Prepare SSE headers and flush them NOW\n res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache, no-transform');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no'); // disable nginx buffering\n res.flushHeaders?.();\n res.write(': connected\\n\\n'); // initial comment keeps some browsers happy\n res.flush?.();\n\n // 2. Kick off the upstream stream WITHOUT awaiting it\n askDocQuestionUtil\n .askDocQuestion(messages, aiConfig, {\n onMessage: (chunk) => {\n res.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n res.flush?.();\n },\n })\n .then(async (fullResponse) => {\n const lastUserMessageContent = messages.findLast(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords = lastUserMessageContent\n ? lastUserMessageContent.split(' ').length\n : 0;\n if (lastUserMessageNbWords > 2) {\n // If the last user message is less than 3 words, don't persist the discussion\n // Example: \"Hello\", \"Hi\", \"Hey\", \"test\", etc.\n\n // 3. Persist discussion while the client already has all chunks\n await DiscussionModel.findOneAndUpdate(\n { discussionId },\n {\n $set: {\n discussionId,\n userId: user?.id,\n projectId: project?.id,\n organizationId: organization?.id,\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp,\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n },\n },\n { upsert: true, new: true }\n );\n }\n\n // 4. Tell the client we're done and close the stream\n res.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n res.end();\n })\n .catch((err) => {\n // propagate error as an SSE event so the client knows why it closed\n res.write(\n `event: error\\ndata: ${JSON.stringify({ message: err.message })}\\n\\n`\n );\n res.end();\n });\n};\n\nexport type AutocompleteBody = {\n text: string;\n aiOptions?: AIOptions;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteResponse = ResponseData<{\n autocompletion: string;\n}>;\n\nexport const autocomplete = async (\n req: Request<AutocompleteBody>,\n res: ResponseWithSession<AutocompleteResponse>,\n _next: NextFunction\n): Promise<void> => {\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n const response = (await autocompleteUtil.autocomplete({\n text,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n })) ?? {\n autocompletion: '',\n tokenUsed: 0,\n };\n\n const responseData =\n formatResponse<autocompleteUtil.AutocompleteFileResultData>({\n data: response,\n });\n\n res.json(responseData);\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetDiscussionsParams =\n | ({\n page?: string | number;\n pageSize?: string | number;\n includeMessages?: 'true' | 'false';\n } & DiscussionFiltersParams)\n | undefined;\n\nexport type GetDiscussionsResult = PaginatedResponse<DiscussionAPI>;\n\n/**\n * Retrieves a list of discussions with filters and pagination.\n * Only the owner or admins can access. By default, users only see their own.\n */\nexport const getDiscussions = async (\n req: Request<GetDiscussionsParams>,\n res: ResponseWithSession<GetDiscussionsResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, roles } = res.locals;\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getDiscussionFiltersAndPagination(req, res);\n const includeMessagesParam = (req.query as any)?.includeMessages as\n | 'true'\n | 'false'\n | undefined;\n const includeMessages = includeMessagesParam !== 'false';\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const projection = includeMessages ? {} : { messages: 0 };\n const discussions = await DiscussionModel.find(filters, projection)\n .sort(sortOptions)\n .skip(skip)\n .limit(pageSize)\n .lean();\n\n // Compute number of messages for each discussion\n const numberOfMessagesById: Record<string, number> = {};\n if (!includeMessages && discussions.length > 0) {\n const ids = discussions.map((d: any) => d._id);\n const counts = await DiscussionModel.aggregate([\n { $match: { _id: { $in: ids } } },\n {\n $project: {\n numberOfMessages: { $size: { $ifNull: ['$messages', []] } },\n },\n },\n ]);\n for (const c of counts as any[]) {\n numberOfMessagesById[String(c._id)] = c.numberOfMessages ?? 0;\n }\n }\n\n // Permission: allow admin, or the owner for all returned entries\n const allOwnedByUser = discussions.every(\n (d) => String(d.userId) === String(user.id)\n );\n const isAllowed = roles.includes('admin') || allOwnedByUser;\n\n if (!isAllowed) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await DiscussionModel.countDocuments(filters);\n\n const responseData = formatPaginatedResponse({\n data: discussions.map((d: any) => ({\n ...d,\n id: String(d._id ?? d.id),\n numberOfMessages: includeMessages\n ? Array.isArray(d.messages)\n ? d.messages.length\n : 0\n : (numberOfMessagesById[String(d._id ?? d.id)] ?? 0),\n })),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData as any);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai.controller.mjs","names":["aiConfig: AIConfig","customQueryUtil.aiDefaultOptions","customQueryUtil.customQuery","translateJSONUtil.aiDefaultOptions","tags: Tag[]","translateJSONUtil.translateJSON","auditContentDeclarationUtil.aiDefaultOptions","auditContentDeclarationUtil.auditDictionary","auditContentDeclarationFieldUtil.aiDefaultOptions","auditContentDeclarationFieldUtil.auditDictionaryField","auditContentDeclarationMetadataUtil.aiDefaultOptions","tagService.findTags","auditContentDeclarationMetadataUtil.auditDictionaryMetadata","auditTagUtil.aiDefaultOptions","dictionaries: Dictionary[]","auditTagUtil.auditTag","autocompleteUtil.aiDefaultOptions","autocompleteUtil.autocomplete","numberOfMessagesById: Record<string, number>"],"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import type { KeyPath, Locale } from '@intlayer/types';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport {\n type AIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n getAIConfig,\n} from '@utils/AI/aiSdk';\nimport * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';\nimport * as auditContentDeclarationUtil from '@utils/AI/auditDictionary';\nimport * as auditContentDeclarationFieldUtil from '@utils/AI/auditDictionaryField';\nimport * as auditContentDeclarationMetadataUtil from '@utils/AI/auditDictionaryMetadata';\nimport * as auditTagUtil from '@utils/AI/auditTag';\nimport * as autocompleteUtil from '@utils/AI/autocomplete';\nimport * as customQueryUtil from '@utils/AI/customQuery';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DiscussionFiltersParams,\n getDiscussionFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDiscussionFiltersAndPagination';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { DiscussionModel } from '@/models/discussion.model';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { DiscussionAPI } from '@/types/discussion.types';\nimport type { Tag, TagAPI } from '@/types/tag.types';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions> & {\n tagsKeys?: string[];\n applicationContext?: string;\n };\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n req: Request<CustomQueryBody>,\n res: ResponseWithSession<CustomQueryResult>,\n _next: NextFunction\n): Promise<void> => {\n // biome-ignore lint/correctness/noUnusedVariables: Just filter out tagsKeys\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'QUERY_FAILED');\n return;\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult =\n ResponseData<translateJSONUtil.TranslateJSONResultData>;\n\nexport const translateJSON = async (\n req: Request<TranslateJSONBody>,\n res: ResponseWithSession<TranslateJSONResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId && tagsKeys) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await translateJSONUtil.translateJSON({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<translateJSONUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n defaultLocale: Locale;\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n};\nexport type AuditContentDeclarationResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclaration = async (\n req: Request<AuditContentDeclarationBody>,\n res: ResponseWithSession<AuditContentDeclarationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await auditContentDeclarationUtil.auditDictionary({\n fileContent,\n filePath,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n defaultLocale,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n req: Request<AuditContentDeclarationFieldBody>,\n res: ResponseWithSession<AuditContentDeclarationFieldResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse =\n await auditContentDeclarationFieldUtil.auditDictionaryField({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n tags,\n keyPath,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>(\n {\n data: auditResponse,\n }\n );\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\n\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationMetadataUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n req: Request<AuditContentDeclarationMetadataBody>,\n res: ResponseWithSession<AuditContentDeclarationMetadataResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization } = res.locals;\n const { fileContent, aiOptions } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n const tags: Tag[] = await tagService.findTags(\n {\n organizationId: organization?.id,\n },\n 0,\n 1000\n );\n\n const auditResponse =\n await auditContentDeclarationMetadataUtil.auditDictionaryMetadata({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationMetadataUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AuditTagBody = {\n aiOptions?: AIOptions;\n tag: TagAPI;\n};\nexport type AuditTagResult = ResponseData<auditTagUtil.TranslateJSONResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n req: Request<undefined, undefined, AuditTagBody>,\n res: ResponseWithSession<AuditTagResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tag } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n let dictionaries: Dictionary[] = [];\n if (project?.organizationId) {\n dictionaries = await getDictionariesByTags([tag.key], project.id);\n }\n\n const auditResponse = await auditTagUtil.auditTag({\n aiConfig,\n dictionaries,\n tag,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData = formatResponse<auditTagUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n req: Request<undefined, undefined, AskDocQuestionBody>,\n res: ResponseWithSession<AskDocQuestionResult>,\n _next: NextFunction\n): Promise<void> => {\n const { messages = [], discussionId } = req.body;\n const { user, project, organization } = res.locals;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: {},\n accessType: ['public'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n // 1. Prepare SSE headers and flush them NOW\n res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache, no-transform');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no'); // disable nginx buffering\n res.flushHeaders?.();\n res.write(': connected\\n\\n'); // initial comment keeps some browsers happy\n res.flush?.();\n\n // 2. Kick off the upstream stream WITHOUT awaiting it\n askDocQuestionUtil\n .askDocQuestion(messages, aiConfig, {\n onMessage: (chunk) => {\n res.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n res.flush?.();\n },\n })\n .then(async (fullResponse) => {\n const lastUserMessageContent = messages.findLast(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords = lastUserMessageContent\n ? lastUserMessageContent.split(' ').length\n : 0;\n if (lastUserMessageNbWords > 2) {\n // If the last user message is less than 3 words, don't persist the discussion\n // Example: \"Hello\", \"Hi\", \"Hey\", \"test\", etc.\n\n // 3. Persist discussion while the client already has all chunks\n await DiscussionModel.findOneAndUpdate(\n { discussionId },\n {\n $set: {\n discussionId,\n userId: user?.id,\n projectId: project?.id,\n organizationId: organization?.id,\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp,\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n },\n },\n { upsert: true, new: true }\n );\n }\n\n // 4. Tell the client we're done and close the stream\n res.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n res.end();\n })\n .catch((err) => {\n // propagate error as an SSE event so the client knows why it closed\n res.write(\n `event: error\\ndata: ${JSON.stringify({ message: err.message })}\\n\\n`\n );\n res.end();\n });\n};\n\nexport type AutocompleteBody = {\n text: string;\n aiOptions?: AIOptions;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteResponse = ResponseData<{\n autocompletion: string;\n}>;\n\nexport const autocomplete = async (\n req: Request<AutocompleteBody>,\n res: ResponseWithSession<AutocompleteResponse>,\n _next: NextFunction\n): Promise<void> => {\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (_error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n const response = (await autocompleteUtil.autocomplete({\n text,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n })) ?? {\n autocompletion: '',\n tokenUsed: 0,\n };\n\n const responseData =\n formatResponse<autocompleteUtil.AutocompleteFileResultData>({\n data: response,\n });\n\n res.json(responseData);\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetDiscussionsParams =\n | ({\n page?: string | number;\n pageSize?: string | number;\n includeMessages?: 'true' | 'false';\n } & DiscussionFiltersParams)\n | undefined;\n\nexport type GetDiscussionsResult = PaginatedResponse<DiscussionAPI>;\n\n/**\n * Retrieves a list of discussions with filters and pagination.\n * Only the owner or admins can access. By default, users only see their own.\n */\nexport const getDiscussions = async (\n req: Request<GetDiscussionsParams>,\n res: ResponseWithSession<GetDiscussionsResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, roles } = res.locals;\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getDiscussionFiltersAndPagination(req, res);\n const includeMessagesParam = (req.query as any)?.includeMessages as\n | 'true'\n | 'false'\n | undefined;\n const includeMessages = includeMessagesParam !== 'false';\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const projection = includeMessages ? {} : { messages: 0 };\n const discussions = await DiscussionModel.find(filters, projection)\n .sort(sortOptions)\n .skip(skip)\n .limit(pageSize)\n .lean();\n\n // Compute number of messages for each discussion\n const numberOfMessagesById: Record<string, number> = {};\n if (!includeMessages && discussions.length > 0) {\n const ids = discussions.map((d: any) => d._id);\n const counts = await DiscussionModel.aggregate([\n { $match: { _id: { $in: ids } } },\n {\n $project: {\n numberOfMessages: { $size: { $ifNull: ['$messages', []] } },\n },\n },\n ]);\n for (const c of counts as any[]) {\n numberOfMessagesById[String(c._id)] = c.numberOfMessages ?? 0;\n }\n }\n\n // Permission: allow admin, or the owner for all returned entries\n const allOwnedByUser = discussions.every(\n (d) => String(d.userId) === String(user.id)\n );\n const isAllowed = roles.includes('admin') || allOwnedByUser;\n\n if (!isAllowed) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await DiscussionModel.countDocuments(filters);\n\n const responseData = formatPaginatedResponse({\n data: discussions.map((d: any) => ({\n ...d,\n id: String(d._id ?? d.id),\n numberOfMessages: includeMessages\n ? Array.isArray(d.messages)\n ? d.messages.length\n : 0\n : (numberOfMessagesById[String(d._id ?? d.id)] ?? 0),\n })),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData as any);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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"}
|
|
@@ -23,28 +23,19 @@ const getAPIKey = (res, accessType, aiOptions) => {
|
|
|
23
23
|
if (accessType.includes("registered_user") && res.locals.user) return aiOptions?.apiKey ?? defaultApiKey;
|
|
24
24
|
if (accessType.includes("premium_user") && res.locals.user) return aiOptions?.apiKey ?? defaultApiKey;
|
|
25
25
|
};
|
|
26
|
-
const getModel = (provider, userApiKey, userModel, defaultModel) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
defaultModel = "mistral-large-latest";
|
|
37
|
-
break;
|
|
38
|
-
case AIProvider.DEEPSEEK:
|
|
39
|
-
defaultModel = "deepseek-coder";
|
|
40
|
-
break;
|
|
41
|
-
case AIProvider.GEMINI:
|
|
42
|
-
defaultModel = "gemini-2.5-flash";
|
|
43
|
-
break;
|
|
26
|
+
const getModel = (provider, userApiKey, userModel, defaultModel = "gpt-5-mini") => {
|
|
27
|
+
if (userApiKey) {
|
|
28
|
+
if (provider && provider === AIProvider.OPENAI) return userModel;
|
|
29
|
+
switch (provider) {
|
|
30
|
+
case AIProvider.ANTHROPIC: return "claude-sonnet-4-5-20250929";
|
|
31
|
+
case AIProvider.MISTRAL: return "mistral-large-latest";
|
|
32
|
+
case AIProvider.DEEPSEEK: return "deepseek-coder";
|
|
33
|
+
case AIProvider.GEMINI: return "gemini-2.5-flash";
|
|
34
|
+
default: return defaultModel;
|
|
35
|
+
}
|
|
44
36
|
}
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
return fallBackModel;
|
|
37
|
+
if (userModel || provider) throw new Error("The user should use his own API key to use a custom model");
|
|
38
|
+
return defaultModel;
|
|
48
39
|
};
|
|
49
40
|
const DEFAULT_PROVIDER = AIProvider.OPENAI;
|
|
50
41
|
const DEFAULT_TEMPERATURE = 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiSdk.mjs","names":["
|
|
1
|
+
{"version":3,"file":"aiSdk.mjs","names":["DEFAULT_PROVIDER: AIProvider","DEFAULT_TEMPERATURE: number","languageModel: AIConfig['model']"],"sources":["../../../../src/utils/AI/aiSdk.ts"],"sourcesContent":["import { type anthropic, createAnthropic } from '@ai-sdk/anthropic';\nimport { createDeepSeek, type deepseek } from '@ai-sdk/deepseek';\nimport { createGoogleGenerativeAI, type google } from '@ai-sdk/google';\nimport { createMistral, type mistral } from '@ai-sdk/mistral';\nimport { createOpenAI, type openai } from '@ai-sdk/openai';\nimport type {\n AssistantModelMessage,\n generateText,\n SystemModelMessage,\n ToolModelMessage,\n UserModelMessage,\n} from 'ai';\nimport type { Response } from 'express';\n\ntype AnthropicModel = Parameters<typeof anthropic>[0];\ntype DeepSeekModel = Parameters<typeof deepseek>[0];\ntype MistralModel = Parameters<typeof mistral>[0];\ntype OpenAIModel = Parameters<typeof openai>[0];\ntype GoogleModel = Parameters<typeof google>[0];\n\nexport type Messages = (\n | SystemModelMessage\n | UserModelMessage\n | AssistantModelMessage\n | ToolModelMessage\n)[];\n\n/**\n * Supported AI models\n */\nexport type Model =\n | AnthropicModel\n | DeepSeekModel\n | MistralModel\n | OpenAIModel\n | GoogleModel\n | (string & {});\n\n/**\n * Supported AI SDK providers\n */\nexport enum AIProvider {\n OPENAI = 'openai',\n ANTHROPIC = 'anthropic',\n MISTRAL = 'mistral',\n DEEPSEEK = 'deepseek',\n GEMINI = 'gemini',\n}\n\n/**\n * Common options for all AI providers\n */\nexport type AIOptions = {\n provider?: AIProvider;\n model?: Model;\n temperature?: number;\n apiKey?: string;\n applicationContext?: string;\n};\n\n// Define the structure of messages used in chat completions\nexport type ChatCompletionRequestMessage = {\n role: 'system' | 'user' | 'assistant'; // The role of the message sender\n content: string; // The text content of the message\n timestamp?: Date; // The timestamp of the message\n};\n\ntype AccessType = 'apiKey' | 'registered_user' | 'premium_user' | 'public';\n\nconst getAPIKey = (\n res: Response,\n accessType: AccessType[],\n aiOptions?: AIOptions\n) => {\n const defaultApiKey = process.env.OPENAI_API_KEY;\n\n if (accessType.includes('public')) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n if (accessType.includes('apiKey') && aiOptions?.apiKey) {\n return aiOptions?.apiKey;\n }\n\n if (accessType.includes('registered_user') && res.locals.user) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n // TODO: Implement premium user access\n if (accessType.includes('premium_user') && res.locals.user) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n return undefined;\n};\n\nconst getModel = (\n provider: AIProvider,\n userApiKey: string,\n userModel?: Model,\n defaultModel: Model = 'gpt-5-mini'\n): Model => {\n // If the user uses their own API key, allow custom model selection\n if (userApiKey) {\n if (provider && provider === AIProvider.OPENAI) {\n return userModel!;\n }\n\n switch (provider) {\n case AIProvider.ANTHROPIC:\n return 'claude-sonnet-4-5-20250929';\n case AIProvider.MISTRAL:\n return 'mistral-large-latest';\n case AIProvider.DEEPSEEK:\n return 'deepseek-coder';\n case AIProvider.GEMINI:\n return 'gemini-2.5-flash';\n default:\n return defaultModel;\n }\n }\n\n // Guard: Prevent custom model usage without a user API key\n if (userModel || provider) {\n throw new Error(\n 'The user should use his own API key to use a custom model'\n );\n }\n\n return defaultModel;\n};\n\nexport type AIConfig = Omit<Parameters<typeof generateText>[0], 'prompt'>;\n\nconst DEFAULT_PROVIDER: AIProvider = AIProvider.OPENAI as AIProvider;\nconst DEFAULT_TEMPERATURE: number = 1; // ChatGPT 5 accept only temperature 1\n\nexport type AIConfigOptions = {\n userOptions?: AIOptions;\n defaultOptions?: AIOptions;\n accessType?: AccessType[];\n};\n\n/**\n * Get AI model configuration based on the selected provider and options\n * This function handles the configuration for different AI providers\n *\n * @param options Configuration options including provider, API keys, models and temperature\n * @returns Configured AI model ready to use with generateText\n */\nexport const getAIConfig = async (\n res: Response,\n options: AIConfigOptions\n): Promise<AIConfig> => {\n const {\n userOptions,\n defaultOptions,\n accessType = ['registered_user'],\n } = options;\n\n const aiOptions = {\n provider: DEFAULT_PROVIDER,\n temperature: DEFAULT_TEMPERATURE,\n ...defaultOptions,\n ...userOptions,\n } satisfies AIOptions;\n\n const apiKey = getAPIKey(res, accessType, aiOptions);\n\n // Check if API key is provided\n if (!apiKey) {\n throw new Error(`API key for ${aiOptions.provider} is missing`);\n }\n\n const selectedModel = getModel(\n aiOptions.provider,\n apiKey,\n aiOptions.model,\n defaultOptions?.model\n );\n\n const protectedOptions = {\n ...aiOptions,\n apiKey,\n model: selectedModel,\n } satisfies AIOptions;\n\n let languageModel: AIConfig['model'];\n\n switch (protectedOptions.provider) {\n case AIProvider.OPENAI: {\n languageModel = createOpenAI({\n apiKey,\n })(selectedModel);\n break;\n }\n\n case AIProvider.ANTHROPIC: {\n languageModel = createAnthropic({\n apiKey,\n })(selectedModel);\n break;\n }\n\n case AIProvider.MISTRAL: {\n languageModel = createMistral({\n apiKey,\n })(selectedModel);\n break;\n }\n\n case AIProvider.DEEPSEEK: {\n languageModel = createDeepSeek({\n apiKey,\n })(selectedModel);\n break;\n }\n\n case AIProvider.GEMINI: {\n languageModel = createGoogleGenerativeAI({\n apiKey,\n })(selectedModel);\n break;\n }\n\n default: {\n throw new Error(`Provider ${protectedOptions.provider} not supported`);\n }\n }\n\n return {\n model: languageModel,\n temperature: protectedOptions.temperature,\n };\n};\n"],"mappings":";;;;;;;;;;AAyCA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;;;AAuBF,MAAM,aACJ,KACA,YACA,cACG;CACH,MAAM,gBAAgB,QAAQ,IAAI;AAElC,KAAI,WAAW,SAAS,SAAS,CAC/B,QAAO,WAAW,UAAU;AAG9B,KAAI,WAAW,SAAS,SAAS,IAAI,WAAW,OAC9C,QAAO,WAAW;AAGpB,KAAI,WAAW,SAAS,kBAAkB,IAAI,IAAI,OAAO,KACvD,QAAO,WAAW,UAAU;AAI9B,KAAI,WAAW,SAAS,eAAe,IAAI,IAAI,OAAO,KACpD,QAAO,WAAW,UAAU;;AAMhC,MAAM,YACJ,UACA,YACA,WACA,eAAsB,iBACZ;AAEV,KAAI,YAAY;AACd,MAAI,YAAY,aAAa,WAAW,OACtC,QAAO;AAGT,UAAQ,UAAR;GACE,KAAK,WAAW,UACd,QAAO;GACT,KAAK,WAAW,QACd,QAAO;GACT,KAAK,WAAW,SACd,QAAO;GACT,KAAK,WAAW,OACd,QAAO;GACT,QACE,QAAO;;;AAKb,KAAI,aAAa,SACf,OAAM,IAAI,MACR,4DACD;AAGH,QAAO;;AAKT,MAAMA,mBAA+B,WAAW;AAChD,MAAMC,sBAA8B;;;;;;;;AAepC,MAAa,cAAc,OACzB,KACA,YACsB;CACtB,MAAM,EACJ,aACA,gBACA,aAAa,CAAC,kBAAkB,KAC9B;CAEJ,MAAM,YAAY;EAChB,UAAU;EACV,aAAa;EACb,GAAG;EACH,GAAG;EACJ;CAED,MAAM,SAAS,UAAU,KAAK,YAAY,UAAU;AAGpD,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,eAAe,UAAU,SAAS,aAAa;CAGjE,MAAM,gBAAgB,SACpB,UAAU,UACV,QACA,UAAU,OACV,gBAAgB,MACjB;CAED,MAAM,mBAAmB;EACvB,GAAG;EACH;EACA,OAAO;EACR;CAED,IAAIC;AAEJ,SAAQ,iBAAiB,UAAzB;EACE,KAAK,WAAW;AACd,mBAAgB,aAAa,EAC3B,QACD,CAAC,CAAC,cAAc;AACjB;EAGF,KAAK,WAAW;AACd,mBAAgB,gBAAgB,EAC9B,QACD,CAAC,CAAC,cAAc;AACjB;EAGF,KAAK,WAAW;AACd,mBAAgB,cAAc,EAC5B,QACD,CAAC,CAAC,cAAc;AACjB;EAGF,KAAK,WAAW;AACd,mBAAgB,eAAe,EAC7B,QACD,CAAC,CAAC,cAAc;AACjB;EAGF,KAAK,WAAW;AACd,mBAAgB,yBAAyB,EACvC,QACD,CAAC,CAAC,cAAc;AACjB;EAGF,QACE,OAAM,IAAI,MAAM,YAAY,iBAAiB,SAAS,gBAAgB;;AAI1E,QAAO;EACL,OAAO;EACP,aAAa,iBAAiB;EAC/B"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AIProvider } from "../aiSdk.mjs";
|
|
2
1
|
import { readAsset } from "../../../_virtual/_utils_asset.mjs";
|
|
3
2
|
import { getMarkdownMetadata } from "@intlayer/core";
|
|
4
3
|
import { getBlogs, getDocs, getFrequentQuestions } from "@intlayer/docs";
|
|
@@ -22,15 +21,8 @@ const readEmbeddingsForFile = (fileKey) => {
|
|
|
22
21
|
* - embedding: The numerical embedding vector for the chunk
|
|
23
22
|
*/
|
|
24
23
|
const vectorStore = [];
|
|
25
|
-
const MODEL = "chatgpt-4o-latest";
|
|
26
|
-
const MODEL_TEMPERATURE = .1;
|
|
27
24
|
const MAX_RELEVANT_CHUNKS_NB = 20;
|
|
28
25
|
const MIN_RELEVANT_CHUNKS_SIMILARITY = .42;
|
|
29
|
-
const aiDefaultOptions = {
|
|
30
|
-
provider: AIProvider.OPENAI,
|
|
31
|
-
model: MODEL,
|
|
32
|
-
temperature: MODEL_TEMPERATURE
|
|
33
|
-
};
|
|
34
26
|
const EMBEDDING_MODEL = "text-embedding-3-large";
|
|
35
27
|
const OVERLAP_TOKENS = 200;
|
|
36
28
|
const MAX_CHUNK_TOKENS = 800;
|
|
@@ -201,5 +193,5 @@ const askDocQuestion = async (messages, aiConfig, options) => {
|
|
|
201
193
|
};
|
|
202
194
|
|
|
203
195
|
//#endregion
|
|
204
|
-
export {
|
|
196
|
+
export { askDocQuestion, initPrompt, loadMarkdownFiles, searchChunkReference };
|
|
205
197
|
//# sourceMappingURL=askDocQuestion.mjs.map
|