@intlayer/ai 7.5.2-canary.1 → 7.5.2-canary.2
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/auditDictionaryMetadata/EXAMPLE_REQUEST.md +21 -0
- package/dist/assets/auditDictionaryMetadata/EXAMPLE_RESPONSE.md +1 -0
- package/dist/assets/auditDictionaryMetadata/PROMPT copy 2.md +73 -0
- package/dist/assets/auditDictionaryMetadata/PROMPT copy.md +73 -0
- package/dist/assets/auditDictionaryMetadata/PROMPT.md +0 -41
- package/dist/assets/translateJSON/PROMPT.md +25 -29
- package/dist/cjs/aiSdk.cjs +1 -1
- package/dist/cjs/aiSdk.cjs.map +1 -1
- package/dist/cjs/auditDictionaryMetadata/index.cjs +30 -15
- package/dist/cjs/auditDictionaryMetadata/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/translateJSON/index.cjs +29 -6
- package/dist/cjs/translateJSON/index.cjs.map +1 -1
- package/dist/esm/aiSdk.mjs +1 -1
- package/dist/esm/aiSdk.mjs.map +1 -1
- package/dist/esm/auditDictionaryMetadata/index.mjs +31 -16
- package/dist/esm/auditDictionaryMetadata/index.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/translateJSON/index.mjs +30 -7
- package/dist/esm/translateJSON/index.mjs.map +1 -1
- package/dist/types/translateJSON/index.d.ts +7 -7
- package/dist/types/translateJSON/index.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
```ts
|
|
2
|
+
import { t, type Dictionary } from "intlayer";
|
|
3
|
+
import { Metadata } from "next";
|
|
4
|
+
|
|
5
|
+
const metadataContent = {
|
|
6
|
+
key: "pricing-metadata",
|
|
7
|
+
content: {
|
|
8
|
+
title: t({
|
|
9
|
+
en: "Pricing | Intlayer",
|
|
10
|
+
}),
|
|
11
|
+
description: t({
|
|
12
|
+
en: "Discover our pricing plans and get access to premium features with Intlayer. Choose the plan that suits you best.",
|
|
13
|
+
}),
|
|
14
|
+
keywords: t<string[]>({
|
|
15
|
+
en: ["Pricing", "Subscription"],
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
} satisfies Dictionary<Metadata>;
|
|
19
|
+
|
|
20
|
+
export default metadataContent;
|
|
21
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "title": "Pricing page metadata", "description": "Metadata related to the pricing page, includes title, description, keywords, metadata for SEO purpose. It will help search engines understand the content of the page.", "tags": ["page metadata", "pricing page"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Your role is to describe a content declaration.
|
|
2
|
+
|
|
3
|
+
1. **Terminology:**
|
|
4
|
+
|
|
5
|
+
- **Dictionary:** A content declaration is a is a file (.ts, .js or .json) that contains the multilingual declaration related to a specific content. A content declaration file is usually related to a specific component, or page or section of a website.
|
|
6
|
+
- **Tag:** A tag is attached to a content declaration and is used to group content declaration and harmonize them.
|
|
7
|
+
|
|
8
|
+
2. **Audit Requirements:**
|
|
9
|
+
- **Do Not Alter Structure:** If the file structure is correct, do not modify it. Only add, update, or remove content declarations as necessary.
|
|
10
|
+
- **Misspelled Content:** If declared, detect each `title`, `description` and `tags` are not misspelled. If some content is misspelled, correct it.
|
|
11
|
+
|
|
12
|
+
3. **Fields functions:**
|
|
13
|
+
|
|
14
|
+
- **Title:** The title of the content declaration allows to easily identify it. It should be considered as a readable way to represent the `key` (example: `page-metadata` -> `Page metadata`). It should be a short and descriptive title that accurately reflects the dictionary.
|
|
15
|
+
- **Description:** The description of the content declaration provides a brief summary of the content declaration. It should be a concise and informative description that explains the purpose and content of the dictionary.
|
|
16
|
+
- **Tags:** The tags is an array of strings that represent the key of the tags associated with the content declaration.
|
|
17
|
+
|
|
18
|
+
**Expected Response:**
|
|
19
|
+
|
|
20
|
+
After completion, provide only the final title, description and tags fields in a JSON without any Markdown, code block formatting or any additional comments or explanations.
|
|
21
|
+
|
|
22
|
+
Correct this fields if they are misspelled or do not match the expected content, and / or complete missing title / description / tags.
|
|
23
|
+
|
|
24
|
+
**Example of expected response:**
|
|
25
|
+
|
|
26
|
+
The following case is just an example of expected behavior:
|
|
27
|
+
|
|
28
|
+
- Example of entry:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { t, type Dictionary } from "intlayer";
|
|
32
|
+
import { Metadata } from "next";
|
|
33
|
+
|
|
34
|
+
const metadataContent = {
|
|
35
|
+
key: "pricing-metadata",
|
|
36
|
+
title: "",
|
|
37
|
+
description:
|
|
38
|
+
"here a description that doesn't make any sense and should be replaced",
|
|
39
|
+
content: {
|
|
40
|
+
title: t({
|
|
41
|
+
en: "Pricing | Intlayer",
|
|
42
|
+
}),
|
|
43
|
+
description: t({
|
|
44
|
+
en: "Discover our pricing plans and get access to premium features with Intlayer. Choose the plan that suits you best.",
|
|
45
|
+
}),
|
|
46
|
+
keywords: t<string[]>({
|
|
47
|
+
en: ["Pricing", "Subscription"],
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
} satisfies Dictionary<Metadata>;
|
|
51
|
+
|
|
52
|
+
export default metadataContent;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- Example of response:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"title": "Pricing page metadata",
|
|
60
|
+
"description": "Metadata related to the pricing page, includes title, description, keywords, metadata for SEO purpose. It will help search engines understand the content of the page.",
|
|
61
|
+
"tags": ["page metadata", "pricing page"]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Application Context**
|
|
66
|
+
|
|
67
|
+
{{applicationContext}}
|
|
68
|
+
|
|
69
|
+
**List of existing Tags:**
|
|
70
|
+
|
|
71
|
+
Here the list of existing tags as a context to help you to pick related ones.
|
|
72
|
+
|
|
73
|
+
{{tags}}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Your role is to describe a content declaration.
|
|
2
|
+
|
|
3
|
+
1. **Terminology:**
|
|
4
|
+
|
|
5
|
+
- **Dictionary:** A content declaration is a is a file (.ts, .js or .json) that contains the multilingual declaration related to a specific content. A content declaration file is usually related to a specific component, or page or section of a website.
|
|
6
|
+
- **Tag:** A tag is attached to a content declaration and is used to group content declaration and harmonize them.
|
|
7
|
+
|
|
8
|
+
2. **Audit Requirements:**
|
|
9
|
+
- **Do Not Alter Structure:** If the file structure is correct, do not modify it. Only add, update, or remove content declarations as necessary.
|
|
10
|
+
- **Misspelled Content:** If declared, detect each `title`, `description` and `tags` are not misspelled. If some content is misspelled, correct it.
|
|
11
|
+
|
|
12
|
+
3. **Fields functions:**
|
|
13
|
+
|
|
14
|
+
- **Title:** The title of the content declaration allows to easily identify it. It should be considered as a readable way to represent the `key` (example: `page-metadata` -> `Page metadata`). It should be a short and descriptive title that accurately reflects the dictionary.
|
|
15
|
+
- **Description:** The description of the content declaration provides a brief summary of the content declaration. It should be a concise and informative description that explains the purpose and content of the dictionary.
|
|
16
|
+
- **Tags:** The tags is an array of strings that represent the key of the tags associated with the content declaration.
|
|
17
|
+
|
|
18
|
+
**Expected Response:**
|
|
19
|
+
|
|
20
|
+
After completion, provide only the final title, description and tags fields in a JSON without any Markdown, code block formatting or any additional comments or explanations.
|
|
21
|
+
|
|
22
|
+
Correct this fields if they are misspelled or do not match the expected content, and / or complete missing title / description / tags.
|
|
23
|
+
|
|
24
|
+
**Example of expected response:**
|
|
25
|
+
|
|
26
|
+
The following case is just an example of expected behavior:
|
|
27
|
+
|
|
28
|
+
- Example of entry:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { t, type Dictionary } from "intlayer";
|
|
32
|
+
import { Metadata } from "next";
|
|
33
|
+
|
|
34
|
+
const metadataContent = {
|
|
35
|
+
key: "pricing-metadata",
|
|
36
|
+
title: "",
|
|
37
|
+
description:
|
|
38
|
+
"here a description that doesn't make any sense and should be replaced",
|
|
39
|
+
content: {
|
|
40
|
+
title: t({
|
|
41
|
+
en: "Pricing | Intlayer",
|
|
42
|
+
}),
|
|
43
|
+
description: t({
|
|
44
|
+
en: "Discover our pricing plans and get access to premium features with Intlayer. Choose the plan that suits you best.",
|
|
45
|
+
}),
|
|
46
|
+
keywords: t<string[]>({
|
|
47
|
+
en: ["Pricing", "Subscription"],
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
} satisfies Dictionary<Metadata>;
|
|
51
|
+
|
|
52
|
+
export default metadataContent;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- Example of response:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"title": "Pricing page metadata",
|
|
60
|
+
"description": "Metadata related to the pricing page, includes title, description, keywords, metadata for SEO purpose. It will help search engines understand the content of the page.",
|
|
61
|
+
"tags": ["page metadata", "pricing page"]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Application Context**
|
|
66
|
+
|
|
67
|
+
{{applicationContext}}
|
|
68
|
+
|
|
69
|
+
**List of existing Tags:**
|
|
70
|
+
|
|
71
|
+
Here the list of existing tags as a context to help you to pick related ones.
|
|
72
|
+
|
|
73
|
+
{{tags}}
|
|
@@ -21,47 +21,6 @@ After completion, provide only the final title, description and tags fields in a
|
|
|
21
21
|
|
|
22
22
|
Correct this fields if they are misspelled or do not match the expected content, and / or complete missing title / description / tags.
|
|
23
23
|
|
|
24
|
-
**Example of expected response:**
|
|
25
|
-
|
|
26
|
-
The following case is just an example of expected behavior:
|
|
27
|
-
|
|
28
|
-
- Example of entry:
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
import { t, type Dictionary } from "intlayer";
|
|
32
|
-
import { Metadata } from "next";
|
|
33
|
-
|
|
34
|
-
const metadataContent = {
|
|
35
|
-
key: "pricing-metadata",
|
|
36
|
-
title: "",
|
|
37
|
-
description:
|
|
38
|
-
"here a description that doesn't make any sense and should be replaced",
|
|
39
|
-
content: {
|
|
40
|
-
title: t({
|
|
41
|
-
en: "Pricing | Intlayer",
|
|
42
|
-
}),
|
|
43
|
-
description: t({
|
|
44
|
-
en: "Discover our pricing plans and get access to premium features with Intlayer. Choose the plan that suits you best.",
|
|
45
|
-
}),
|
|
46
|
-
keywords: t<string[]>({
|
|
47
|
-
en: ["Pricing", "Subscription"],
|
|
48
|
-
}),
|
|
49
|
-
},
|
|
50
|
-
} satisfies Dictionary<Metadata>;
|
|
51
|
-
|
|
52
|
-
export default metadataContent;
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
- Example of response:
|
|
56
|
-
|
|
57
|
-
```json
|
|
58
|
-
{
|
|
59
|
-
"title": "Pricing page metadata",
|
|
60
|
-
"description": "Metadata related to the pricing page, includes title, description, keywords, metadata for SEO purpose. It will help search engines understand the content of the page.",
|
|
61
|
-
"tags": ["page metadata", "pricing page"]
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
24
|
**Application Context**
|
|
66
25
|
|
|
67
26
|
{{applicationContext}}
|
|
@@ -1,45 +1,41 @@
|
|
|
1
|
-
You are an expert
|
|
1
|
+
You are an expert AI localization engine specialized in software internationalization (i18n). Your task is to translate and adapt JSON content from a source locale to a specific target locale.
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**CRITICAL INSTRUCTION:**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- **Misplaced Content:** Detect if any translations are placed under incorrect keys.
|
|
9
|
-
- **Type Compliance:** Verify that the content types match the declarations (e.g., strings, string arrays).
|
|
5
|
+
- **Source Language:** {{entryLocale}}
|
|
6
|
+
- **Target Language:** {{outputLocale}}
|
|
7
|
+
- **Strict Compliance:** You must translate the content **ONLY** into {{outputLocale}}. Never use any other language.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
- **Do Not Alter Structure:** If the file structure is correct, do not modify it. Only add, update, or remove content declarations as necessary.
|
|
13
|
-
- **Missing Content:** If one key is missing from the Preset Output Content, or if the Preset Output Content is empty, the output content should be completed by translating the Entry Content to Translate into the output locale.
|
|
14
|
-
- **Return Only Final File Content:** Provide the updated file content without any additional comments or explanations.
|
|
15
|
-
- **Manage Localizations:** If the output languages targeted is a variant contains similar languages, as `en` and `en-GB`, consider `en` as English US, and adapt it into `en-GB` as English UK.
|
|
16
|
-
- **Escape Special Characters:** If the translations contain special characters, escape them using the appropriate escape sequence.
|
|
17
|
-
- **Respect the tags and description instructions:** If the tags and description instructions are provided, ensure that the audited file adheres to them.
|
|
18
|
-
- **Consider the Preset Output Content** If Preset Output Content is provided, and coherent with the entry, you can consider reuse it to fill the output file content.
|
|
19
|
-
- **TypeNode field should not be translated** A value as `{ 'nodeType': 'XXX', ...}` should not be translated.
|
|
9
|
+
**Translation Guidelines:**
|
|
20
10
|
|
|
21
|
-
**
|
|
11
|
+
1. **Preserve Structure:** The output JSON structure (keys, nesting, array lengths) must exactly match the input structure.
|
|
12
|
+
2. **Key Integrity:** Do not translate, rename, or remove object keys.
|
|
13
|
+
3. **Data Types:** Respect specific data types. If the source is a string, return a string. If it is a boolean or number, preserve it.
|
|
14
|
+
4. **Formatting:**
|
|
15
|
+
- Escape special characters if necessary for valid JSON.
|
|
16
|
+
- Do not add markdown formatting (like `json ... `) in the final output; return pure data matching the schema.
|
|
17
|
+
5. **Node Types:** Do not translate values containing technical metadata such as `{ 'nodeType': 'XXX' }`.
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
**Localization Logic:**
|
|
24
20
|
|
|
25
|
-
**
|
|
21
|
+
- **Missing Content:** If a key is missing in the preset output, translate the entry content into the target language.
|
|
22
|
+
- **Dialect Handling:** Adapt the translation to the specific variant (e.g., `en-GB` vs `en-US`, `es-ES` vs `es-MX`).
|
|
23
|
+
- **Context Awareness:** Use the provided Dictionary Description and Application Context to inform tone and terminology.
|
|
26
24
|
|
|
25
|
+
**Mode Instruction:**
|
|
27
26
|
{{modeInstructions}}
|
|
28
27
|
|
|
29
|
-
**Tags
|
|
30
|
-
|
|
28
|
+
**Tags Context:**
|
|
31
29
|
{{tagsInstructions}}
|
|
32
30
|
|
|
33
|
-
**
|
|
31
|
+
**Application Context:**
|
|
32
|
+
{{applicationContext}}
|
|
34
33
|
|
|
34
|
+
**Dictionary Description:**
|
|
35
35
|
{{dictionaryDescription}}
|
|
36
36
|
|
|
37
|
-
**Preset Output Content:**
|
|
38
|
-
|
|
39
|
-
- Target Language: {{outputLocale}}
|
|
40
|
-
|
|
37
|
+
**Preset Output Content (Current State):**
|
|
41
38
|
{{presetOutputContent}}
|
|
42
39
|
|
|
43
|
-
**
|
|
44
|
-
|
|
45
|
-
After auditing, provide only the final content of the file as plain text without any Markdown or code block formatting. If no changes are needed, return the file content exactly as it is.
|
|
40
|
+
**Final Goal:**
|
|
41
|
+
Return the fully translated/updated JSON object for the Target Language ({{outputLocale}}).
|
package/dist/cjs/aiSdk.cjs
CHANGED
|
@@ -66,7 +66,7 @@ const getLanguageModel = (aiOptions, apiKey, defaultModel) => {
|
|
|
66
66
|
})(selectedModel);
|
|
67
67
|
case AIProvider.OLLAMA: return (0, _ai_sdk_openai.createOpenAI)({
|
|
68
68
|
baseURL: baseURL ?? "http://localhost:11434/v1",
|
|
69
|
-
apiKey: "ollama"
|
|
69
|
+
apiKey: apiKey ?? "ollama"
|
|
70
70
|
}).chat(selectedModel);
|
|
71
71
|
default: throw new Error(`Provider ${aiOptions.provider} not supported`);
|
|
72
72
|
}
|
package/dist/cjs/aiSdk.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiSdk.cjs","names":["DEFAULT_PROVIDER: AIProvider","DEFAULT_TEMPERATURE: number"],"sources":["../../src/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';\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 OLLAMA = 'ollama',\n}\n\nexport type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'none';\n\n/**\n * Common options for all AI providers\n */\nexport type AIOptions = {\n provider?: AIProvider;\n model?: Model;\n temperature?: number;\n baseURL?: string;\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 accessType: AccessType[],\n aiOptions?: AIOptions,\n isAuthenticated: boolean = false\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') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n // TODO: Implement premium user access\n if (accessType.includes('premium_user') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n return undefined;\n};\n\nconst getModelName = (\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 || provider === AIProvider.OLLAMA) {\n if (provider === AIProvider.OPENAI) {\n return userModel ?? defaultModel;\n }\n\n if (userModel) {\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 case AIProvider.OLLAMA:\n return '';\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\nconst getLanguageModel = (\n aiOptions: AIOptions,\n apiKey: string,\n defaultModel?: Model\n) => {\n const selectedModel = getModelName(\n aiOptions.provider as AIProvider,\n apiKey,\n aiOptions.model,\n defaultModel\n );\n\n const baseURL = aiOptions.baseURL;\n\n switch (aiOptions.provider) {\n case AIProvider.OPENAI: {\n return createOpenAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.ANTHROPIC: {\n return createAnthropic({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.MISTRAL: {\n return createMistral({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.DEEPSEEK: {\n return createDeepSeek({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.GEMINI: {\n return createGoogleGenerativeAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.OLLAMA: {\n // Ollama compatible mode:\n const ollama = createOpenAI({\n baseURL: baseURL ?? 'http://localhost:11434/v1',\n apiKey: 'ollama', // Required but unused by Ollama\n });\n\n return ollama.chat(selectedModel);\n }\n\n default: {\n throw new Error(`Provider ${aiOptions.provider} not supported`);\n }\n }\n};\n\nexport type AIConfig = Omit<Parameters<typeof generateText>[0], 'prompt'> & {\n reasoningEffort?: ReasoningEffort;\n textVerbosity?: 'low' | 'medium' | 'high';\n};\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 options: AIConfigOptions,\n isAuthenticated: boolean = false\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(accessType, aiOptions, isAuthenticated);\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 languageModel = getLanguageModel(\n aiOptions,\n apiKey,\n defaultOptions?.model\n );\n\n return {\n model: languageModel,\n temperature: aiOptions.temperature,\n };\n};\n"],"mappings":";;;;;;;;;;AAwCA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;AACA;;;AA0BF,MAAM,aACJ,YACA,WACA,kBAA2B,UACxB;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,gBAC5C,QAAO,WAAW,UAAU;AAI9B,KAAI,WAAW,SAAS,eAAe,IAAI,gBACzC,QAAO,WAAW,UAAU;;AAMhC,MAAM,gBACJ,UACA,YACA,WACA,eAAsB,iBACZ;AAEV,KAAI,cAAc,aAAa,WAAW,QAAQ;AAChD,MAAI,aAAa,WAAW,OAC1B,QAAO,aAAa;AAGtB,MAAI,UACF,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,KAAK,WAAW,OACd,QAAO;GACT,QACE,QAAO;;;AAKb,KAAI,aAAa,SACf,OAAM,IAAI,MACR,4DACD;AAGH,QAAO;;AAGT,MAAM,oBACJ,WACA,QACA,iBACG;CACH,MAAM,gBAAgB,aACpB,UAAU,UACV,QACA,UAAU,OACV,aACD;CAED,MAAM,UAAU,UAAU;AAE1B,SAAQ,UAAU,UAAlB;EACE,KAAK,WAAW,OACd,yCAAoB;GAClB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,UACd,+CAAuB;GACrB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,QACd,2CAAqB;GACnB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,SACd,6CAAsB;GACpB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OACd,qDAAgC;GAC9B;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OAOd,yCAL4B;GAC1B,SAAS,WAAW;GACpB,QAAQ;
|
|
1
|
+
{"version":3,"file":"aiSdk.cjs","names":["DEFAULT_PROVIDER: AIProvider","DEFAULT_TEMPERATURE: number"],"sources":["../../src/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';\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 OLLAMA = 'ollama',\n}\n\nexport type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'none';\n\n/**\n * Common options for all AI providers\n */\nexport type AIOptions = {\n provider?: AIProvider;\n model?: Model;\n temperature?: number;\n baseURL?: string;\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 accessType: AccessType[],\n aiOptions?: AIOptions,\n isAuthenticated: boolean = false\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') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n // TODO: Implement premium user access\n if (accessType.includes('premium_user') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n return undefined;\n};\n\nconst getModelName = (\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 || provider === AIProvider.OLLAMA) {\n if (provider === AIProvider.OPENAI) {\n return userModel ?? defaultModel;\n }\n\n if (userModel) {\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 case AIProvider.OLLAMA:\n return '';\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\nconst getLanguageModel = (\n aiOptions: AIOptions,\n apiKey: string,\n defaultModel?: Model\n) => {\n const selectedModel = getModelName(\n aiOptions.provider as AIProvider,\n apiKey,\n aiOptions.model,\n defaultModel\n );\n\n const baseURL = aiOptions.baseURL;\n\n switch (aiOptions.provider) {\n case AIProvider.OPENAI: {\n return createOpenAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.ANTHROPIC: {\n return createAnthropic({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.MISTRAL: {\n return createMistral({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.DEEPSEEK: {\n return createDeepSeek({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.GEMINI: {\n return createGoogleGenerativeAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.OLLAMA: {\n // Ollama compatible mode:\n const ollama = createOpenAI({\n baseURL: baseURL ?? 'http://localhost:11434/v1',\n apiKey: apiKey ?? 'ollama', // Required but unused by Ollama\n });\n\n return ollama.chat(selectedModel);\n }\n\n default: {\n throw new Error(`Provider ${aiOptions.provider} not supported`);\n }\n }\n};\n\nexport type AIConfig = Omit<Parameters<typeof generateText>[0], 'prompt'> & {\n reasoningEffort?: ReasoningEffort;\n textVerbosity?: 'low' | 'medium' | 'high';\n};\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 options: AIConfigOptions,\n isAuthenticated: boolean = false\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(accessType, aiOptions, isAuthenticated);\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 languageModel = getLanguageModel(\n aiOptions,\n apiKey,\n defaultOptions?.model\n );\n\n return {\n model: languageModel,\n temperature: aiOptions.temperature,\n };\n};\n"],"mappings":";;;;;;;;;;AAwCA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;AACA;;;AA0BF,MAAM,aACJ,YACA,WACA,kBAA2B,UACxB;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,gBAC5C,QAAO,WAAW,UAAU;AAI9B,KAAI,WAAW,SAAS,eAAe,IAAI,gBACzC,QAAO,WAAW,UAAU;;AAMhC,MAAM,gBACJ,UACA,YACA,WACA,eAAsB,iBACZ;AAEV,KAAI,cAAc,aAAa,WAAW,QAAQ;AAChD,MAAI,aAAa,WAAW,OAC1B,QAAO,aAAa;AAGtB,MAAI,UACF,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,KAAK,WAAW,OACd,QAAO;GACT,QACE,QAAO;;;AAKb,KAAI,aAAa,SACf,OAAM,IAAI,MACR,4DACD;AAGH,QAAO;;AAGT,MAAM,oBACJ,WACA,QACA,iBACG;CACH,MAAM,gBAAgB,aACpB,UAAU,UACV,QACA,UAAU,OACV,aACD;CAED,MAAM,UAAU,UAAU;AAE1B,SAAQ,UAAU,UAAlB;EACE,KAAK,WAAW,OACd,yCAAoB;GAClB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,UACd,+CAAuB;GACrB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,QACd,2CAAqB;GACnB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,SACd,6CAAsB;GACpB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OACd,qDAAgC;GAC9B;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OAOd,yCAL4B;GAC1B,SAAS,WAAW;GACpB,QAAQ,UAAU;GACnB,CAAC,CAEY,KAAK,cAAc;EAGnC,QACE,OAAM,IAAI,MAAM,YAAY,UAAU,SAAS,gBAAgB;;;AAUrE,MAAMA,mBAA+B,WAAW;AAChD,MAAMC,sBAA8B;;;;;;;;AAepC,MAAa,cAAc,OACzB,SACA,kBAA2B,UACL;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,YAAY,WAAW,gBAAgB;AAGhE,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,eAAe,UAAU,SAAS,aAAa;AASjE,QAAO;EACL,OAPoB,iBACpB,WACA,QACA,gBAAgB,MACjB;EAIC,aAAa,UAAU;EACxB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require__utils_asset = require('../_virtual/_utils_asset.cjs');
|
|
2
|
-
const require_utils_extractJSON = require('../utils/extractJSON.cjs');
|
|
3
2
|
let ai = require("ai");
|
|
3
|
+
let zod = require("zod");
|
|
4
4
|
|
|
5
5
|
//#region src/auditDictionaryMetadata/index.ts
|
|
6
6
|
const aiDefaultOptions = {};
|
|
@@ -10,23 +10,38 @@ const aiDefaultOptions = {};
|
|
|
10
10
|
* and requests for identifying issues or inconsistencies.
|
|
11
11
|
*/
|
|
12
12
|
const auditDictionaryMetadata = async ({ fileContent, tags, aiConfig, applicationContext }) => {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const CHAT_GPT_PROMPT = require__utils_asset.readAsset("./PROMPT.md");
|
|
14
|
+
const EXAMPLE_REQUEST = require__utils_asset.readAsset("./EXAMPLE_REQUEST.md");
|
|
15
|
+
const EXAMPLE_RESPONSE = require__utils_asset.readAsset("./EXAMPLE_RESPONSE.md");
|
|
16
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{applicationContext}}", applicationContext ?? "").replace("{{tags}}", tags ? JSON.stringify(tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n"), null, 2) : "");
|
|
17
|
+
const { object, usage } = await (0, ai.generateObject)({
|
|
15
18
|
...aiConfig,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
schema: zod.z.object({
|
|
20
|
+
title: zod.z.string(),
|
|
21
|
+
description: zod.z.string(),
|
|
22
|
+
tags: zod.z.array(zod.z.string())
|
|
23
|
+
}),
|
|
24
|
+
messages: [
|
|
25
|
+
{
|
|
26
|
+
role: "system",
|
|
27
|
+
content: prompt
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
role: "user",
|
|
31
|
+
content: EXAMPLE_REQUEST
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
role: "assistant",
|
|
35
|
+
content: EXAMPLE_RESPONSE
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
role: "user",
|
|
39
|
+
content: fileContent
|
|
40
|
+
}
|
|
41
|
+
]
|
|
27
42
|
});
|
|
28
43
|
return {
|
|
29
|
-
fileContent:
|
|
44
|
+
fileContent: object,
|
|
30
45
|
tokenUsed: usage?.totalTokens ?? 0
|
|
31
46
|
};
|
|
32
47
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["aiDefaultOptions: AIOptions","readAsset","
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["aiDefaultOptions: AIOptions","readAsset","z"],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { generateObject } from 'ai';\nimport { z } from 'zod';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type AuditDictionaryMetadataOptions = {\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\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 fileContent,\n tags,\n aiConfig,\n applicationContext,\n}: AuditDictionaryMetadataOptions): Promise<\n AuditFileResultData | undefined\n> => {\n const CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n const EXAMPLE_REQUEST = readAsset('./EXAMPLE_REQUEST.md');\n const EXAMPLE_RESPONSE = readAsset('./EXAMPLE_RESPONSE.md');\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n ).replace(\n '{{tags}}',\n tags\n ? JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )\n : ''\n );\n\n // Use the AI SDK to generate the completion\n const { object, usage } = await generateObject({\n ...aiConfig,\n schema: z.object({\n title: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n }),\n messages: [\n { role: 'system', content: prompt },\n { role: 'user', content: EXAMPLE_REQUEST },\n { role: 'assistant', content: EXAMPLE_RESPONSE },\n {\n role: 'user',\n content: fileContent,\n },\n ],\n });\n\n return {\n fileContent: object,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;AA0BA,MAAaA,mBAA8B,EAE1C;;;;;;AAOD,MAAa,0BAA0B,OAAO,EAC5C,aACA,MACA,UACA,yBAGG;CACH,MAAM,kBAAkBC,+BAAU,cAAc;CAChD,MAAM,kBAAkBA,+BAAU,uBAAuB;CACzD,MAAM,mBAAmBA,+BAAU,wBAAwB;CAG3D,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,GACvB,CAAC,QACA,YACA,OACI,KAAK,UACH,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CACzD,KAAK,OAAO,EACf,MACA,EACD,GACD,GACL;CAGD,MAAM,EAAE,QAAQ,UAAU,6BAAqB;EAC7C,GAAG;EACH,QAAQC,MAAE,OAAO;GACf,OAAOA,MAAE,QAAQ;GACjB,aAAaA,MAAE,QAAQ;GACvB,MAAMA,MAAE,MAAMA,MAAE,QAAQ,CAAC;GAC1B,CAAC;EACF,UAAU;GACR;IAAE,MAAM;IAAU,SAAS;IAAQ;GACnC;IAAE,MAAM;IAAQ,SAAS;IAAiB;GAC1C;IAAE,MAAM;IAAa,SAAS;IAAkB;GAChD;IACE,MAAM;IACN,SAAS;IACV;GACF;EACF,CAAC;AAEF,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_aiSdk = require('./aiSdk.cjs');
|
|
2
2
|
const require_customQuery = require('./customQuery.cjs');
|
|
3
|
-
const require_utils_extractJSON = require('./utils/extractJSON.cjs');
|
|
4
3
|
const require_auditDictionaryMetadata_index = require('./auditDictionaryMetadata/index.cjs');
|
|
5
4
|
const require_translateJSON_index = require('./translateJSON/index.cjs');
|
|
5
|
+
const require_utils_extractJSON = require('./utils/extractJSON.cjs');
|
|
6
6
|
let ai = require("ai");
|
|
7
7
|
|
|
8
8
|
exports.AIProvider = require_aiSdk.AIProvider;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_aiSdk = require('../aiSdk.cjs');
|
|
2
2
|
const require__utils_asset = require('../_virtual/_utils_asset.cjs');
|
|
3
|
-
const require_utils_extractJSON = require('../utils/extractJSON.cjs');
|
|
4
3
|
let ai = require("ai");
|
|
4
|
+
let zod = require("zod");
|
|
5
5
|
let _intlayer_core = require("@intlayer/core");
|
|
6
6
|
let _intlayer_types = require("@intlayer/types");
|
|
7
7
|
|
|
@@ -34,29 +34,52 @@ const getModeInstructions = (mode) => {
|
|
|
34
34
|
if (mode === "complete") 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.";
|
|
35
35
|
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.";
|
|
36
36
|
};
|
|
37
|
+
const jsonToZod = (content) => {
|
|
38
|
+
if (typeof content === "string") return zod.z.string();
|
|
39
|
+
if (typeof content === "number") return zod.z.number();
|
|
40
|
+
if (typeof content === "boolean") return zod.z.boolean();
|
|
41
|
+
if (Array.isArray(content)) {
|
|
42
|
+
if (content.length === 0) return zod.z.array(zod.z.string());
|
|
43
|
+
return zod.z.array(jsonToZod(content[0]));
|
|
44
|
+
}
|
|
45
|
+
if (typeof content === "object" && content !== null) {
|
|
46
|
+
const shape = {};
|
|
47
|
+
for (const key in content) shape[key] = jsonToZod(content[key]);
|
|
48
|
+
return zod.z.object(shape);
|
|
49
|
+
}
|
|
50
|
+
return zod.z.any();
|
|
51
|
+
};
|
|
37
52
|
/**
|
|
38
53
|
* TranslateJSONs a content declaration file by constructing a prompt for AI models.
|
|
39
54
|
* The prompt includes details about the project's locales, file paths of content declarations,
|
|
40
55
|
* and requests for identifying issues or inconsistencies.
|
|
41
56
|
*/
|
|
42
57
|
const translateJSON = async ({ entryFileContent, presetOutputContent, dictionaryDescription, aiConfig, entryLocale, outputLocale, tags, mode, applicationContext }) => {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
58
|
+
const promptFile = require__utils_asset.readAsset("./PROMPT.md");
|
|
59
|
+
const formattedEntryLocale = formatLocaleWithName(entryLocale);
|
|
60
|
+
const formattedOutputLocale = formatLocaleWithName(outputLocale);
|
|
61
|
+
const prompt = promptFile.replace("{{entryLocale}}", formattedEntryLocale).replace("{{outputLocale}}", formattedOutputLocale).replace("{{presetOutputContent}}", JSON.stringify(presetOutputContent)).replace("{{dictionaryDescription}}", dictionaryDescription ?? "").replace("{{applicationContext}}", applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags ?? [])).replace("{{modeInstructions}}", getModeInstructions(mode));
|
|
62
|
+
const { object, usage } = await (0, ai.generateObject)({
|
|
45
63
|
...aiConfig,
|
|
64
|
+
schema: jsonToZod(entryFileContent),
|
|
46
65
|
messages: [{
|
|
47
66
|
role: "system",
|
|
48
67
|
content: prompt
|
|
49
68
|
}, {
|
|
50
69
|
role: "user",
|
|
51
70
|
content: [
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
`# Translation Request`,
|
|
72
|
+
`Please translate the following JSON content.`,
|
|
73
|
+
`- **From:** ${formattedEntryLocale}`,
|
|
74
|
+
`- **To:** ${formattedOutputLocale}`,
|
|
75
|
+
``,
|
|
76
|
+
`## Entry Content:`,
|
|
54
77
|
JSON.stringify(entryFileContent)
|
|
55
78
|
].join("\n")
|
|
56
79
|
}]
|
|
57
80
|
});
|
|
58
81
|
return {
|
|
59
|
-
fileContent:
|
|
82
|
+
fileContent: object,
|
|
60
83
|
tokenUsed: usage?.totalTokens ?? 0
|
|
61
84
|
};
|
|
62
85
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["aiDefaultOptions: AIOptions","AIProvider","Locales","
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["aiDefaultOptions: AIOptions","AIProvider","Locales","z","shape: Record<string, z.ZodTypeAny>","readAsset"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { generateObject } from 'ai';\nimport { z } from 'zod';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type TranslateJSONOptions<T> = {\n entryFileContent: T;\n presetOutputContent: Partial<T>;\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<T> = {\n fileContent: T;\n tokenUsed: number;\n};\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\nconst jsonToZod = (content: any): z.ZodTypeAny => {\n // Base case: content is a string (the translation target)\n if (typeof content === 'string') {\n return z.string();\n }\n\n // Base cases: primitives often preserved in i18n files (e.g. strict numbers/booleans)\n if (typeof content === 'number') {\n return z.number();\n }\n if (typeof content === 'boolean') {\n return z.boolean();\n }\n\n // Recursive case: Array\n if (Array.isArray(content)) {\n // If array is empty, we assume array of strings as default for i18n\n if (content.length === 0) {\n return z.array(z.string());\n }\n // We assume all items in the array share the structure of the first item\n return z.array(jsonToZod(content[0]));\n }\n\n // Recursive case: Object\n if (typeof content === 'object' && content !== null) {\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const key in content) {\n shape[key] = jsonToZod(content[key]);\n }\n return z.object(shape);\n }\n\n // Fallback\n return z.any();\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 <T>({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions<T>): Promise<\n TranslateJSONResultData<T> | undefined\n> => {\n const promptFile = readAsset('./PROMPT.md');\n\n const formattedEntryLocale = formatLocaleWithName(entryLocale);\n const formattedOutputLocale = formatLocaleWithName(outputLocale);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = promptFile\n .replace('{{entryLocale}}', formattedEntryLocale)\n .replace('{{outputLocale}}', formattedOutputLocale)\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 { object, usage } = await generateObject({\n ...aiConfig,\n schema: jsonToZod(entryFileContent),\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n // KEY CHANGE: Explicitly repeating instructions in the user message\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n JSON.stringify(entryFileContent),\n ].join('\\n'),\n },\n ],\n });\n\n return {\n fileContent: object as T,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;AA6BA,MAAaA,mBAA8B;CACzC,UAAUC,yBAAW;CACrB,OAAO;CACR;;;;;;;AAQD,MAAM,wBAAwB,WAC5B,GAAG,OAAO,sCAAkB,QAAQC,wBAAQ,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;;AAGT,MAAM,aAAa,YAA+B;AAEhD,KAAI,OAAO,YAAY,SACrB,QAAOC,MAAE,QAAQ;AAInB,KAAI,OAAO,YAAY,SACrB,QAAOA,MAAE,QAAQ;AAEnB,KAAI,OAAO,YAAY,UACrB,QAAOA,MAAE,SAAS;AAIpB,KAAI,MAAM,QAAQ,QAAQ,EAAE;AAE1B,MAAI,QAAQ,WAAW,EACrB,QAAOA,MAAE,MAAMA,MAAE,QAAQ,CAAC;AAG5B,SAAOA,MAAE,MAAM,UAAU,QAAQ,GAAG,CAAC;;AAIvC,KAAI,OAAO,YAAY,YAAY,YAAY,MAAM;EACnD,MAAMC,QAAsC,EAAE;AAC9C,OAAK,MAAM,OAAO,QAChB,OAAM,OAAO,UAAU,QAAQ,KAAK;AAEtC,SAAOD,MAAE,OAAO,MAAM;;AAIxB,QAAOA,MAAE,KAAK;;;;;;;AAQhB,MAAa,gBAAgB,OAAU,EACrC,kBACA,qBACA,uBACA,UACA,aACA,cACA,MACA,MACA,yBAGG;CACH,MAAM,aAAaE,+BAAU,cAAc;CAE3C,MAAM,uBAAuB,qBAAqB,YAAY;CAC9D,MAAM,wBAAwB,qBAAqB,aAAa;CAGhE,MAAM,SAAS,WACZ,QAAQ,mBAAmB,qBAAqB,CAChD,QAAQ,oBAAoB,sBAAsB,CAClD,QAAQ,2BAA2B,KAAK,UAAU,oBAAoB,CAAC,CACvE,QAAQ,6BAA6B,yBAAyB,GAAG,CACjE,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,QAAQ,EAAE,CAAC,CAAC,CAClE,QAAQ,wBAAwB,oBAAoB,KAAK,CAAC;CAG7D,MAAM,EAAE,QAAQ,UAAU,6BAAqB;EAC7C,GAAG;EACH,QAAQ,UAAU,iBAAiB;EACnC,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GAEN,SAAS;IACP;IACA;IACA,eAAe;IACf,aAAa;IACb;IACA;IACA,KAAK,UAAU,iBAAiB;IACjC,CAAC,KAAK,KAAK;GACb,CACF;EACF,CAAC;AAEF,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
package/dist/esm/aiSdk.mjs
CHANGED
|
@@ -66,7 +66,7 @@ const getLanguageModel = (aiOptions, apiKey, defaultModel) => {
|
|
|
66
66
|
})(selectedModel);
|
|
67
67
|
case AIProvider.OLLAMA: return createOpenAI({
|
|
68
68
|
baseURL: baseURL ?? "http://localhost:11434/v1",
|
|
69
|
-
apiKey: "ollama"
|
|
69
|
+
apiKey: apiKey ?? "ollama"
|
|
70
70
|
}).chat(selectedModel);
|
|
71
71
|
default: throw new Error(`Provider ${aiOptions.provider} not supported`);
|
|
72
72
|
}
|
package/dist/esm/aiSdk.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiSdk.mjs","names":["DEFAULT_PROVIDER: AIProvider","DEFAULT_TEMPERATURE: number"],"sources":["../../src/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';\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 OLLAMA = 'ollama',\n}\n\nexport type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'none';\n\n/**\n * Common options for all AI providers\n */\nexport type AIOptions = {\n provider?: AIProvider;\n model?: Model;\n temperature?: number;\n baseURL?: string;\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 accessType: AccessType[],\n aiOptions?: AIOptions,\n isAuthenticated: boolean = false\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') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n // TODO: Implement premium user access\n if (accessType.includes('premium_user') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n return undefined;\n};\n\nconst getModelName = (\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 || provider === AIProvider.OLLAMA) {\n if (provider === AIProvider.OPENAI) {\n return userModel ?? defaultModel;\n }\n\n if (userModel) {\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 case AIProvider.OLLAMA:\n return '';\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\nconst getLanguageModel = (\n aiOptions: AIOptions,\n apiKey: string,\n defaultModel?: Model\n) => {\n const selectedModel = getModelName(\n aiOptions.provider as AIProvider,\n apiKey,\n aiOptions.model,\n defaultModel\n );\n\n const baseURL = aiOptions.baseURL;\n\n switch (aiOptions.provider) {\n case AIProvider.OPENAI: {\n return createOpenAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.ANTHROPIC: {\n return createAnthropic({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.MISTRAL: {\n return createMistral({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.DEEPSEEK: {\n return createDeepSeek({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.GEMINI: {\n return createGoogleGenerativeAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.OLLAMA: {\n // Ollama compatible mode:\n const ollama = createOpenAI({\n baseURL: baseURL ?? 'http://localhost:11434/v1',\n apiKey: 'ollama', // Required but unused by Ollama\n });\n\n return ollama.chat(selectedModel);\n }\n\n default: {\n throw new Error(`Provider ${aiOptions.provider} not supported`);\n }\n }\n};\n\nexport type AIConfig = Omit<Parameters<typeof generateText>[0], 'prompt'> & {\n reasoningEffort?: ReasoningEffort;\n textVerbosity?: 'low' | 'medium' | 'high';\n};\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 options: AIConfigOptions,\n isAuthenticated: boolean = false\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(accessType, aiOptions, isAuthenticated);\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 languageModel = getLanguageModel(\n aiOptions,\n apiKey,\n defaultOptions?.model\n );\n\n return {\n model: languageModel,\n temperature: aiOptions.temperature,\n };\n};\n"],"mappings":";;;;;;;;;;AAwCA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;AACA;;;AA0BF,MAAM,aACJ,YACA,WACA,kBAA2B,UACxB;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,gBAC5C,QAAO,WAAW,UAAU;AAI9B,KAAI,WAAW,SAAS,eAAe,IAAI,gBACzC,QAAO,WAAW,UAAU;;AAMhC,MAAM,gBACJ,UACA,YACA,WACA,eAAsB,iBACZ;AAEV,KAAI,cAAc,aAAa,WAAW,QAAQ;AAChD,MAAI,aAAa,WAAW,OAC1B,QAAO,aAAa;AAGtB,MAAI,UACF,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,KAAK,WAAW,OACd,QAAO;GACT,QACE,QAAO;;;AAKb,KAAI,aAAa,SACf,OAAM,IAAI,MACR,4DACD;AAGH,QAAO;;AAGT,MAAM,oBACJ,WACA,QACA,iBACG;CACH,MAAM,gBAAgB,aACpB,UAAU,UACV,QACA,UAAU,OACV,aACD;CAED,MAAM,UAAU,UAAU;AAE1B,SAAQ,UAAU,UAAlB;EACE,KAAK,WAAW,OACd,QAAO,aAAa;GAClB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,UACd,QAAO,gBAAgB;GACrB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,QACd,QAAO,cAAc;GACnB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,SACd,QAAO,eAAe;GACpB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OACd,QAAO,yBAAyB;GAC9B;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OAOd,QALe,aAAa;GAC1B,SAAS,WAAW;GACpB,QAAQ;
|
|
1
|
+
{"version":3,"file":"aiSdk.mjs","names":["DEFAULT_PROVIDER: AIProvider","DEFAULT_TEMPERATURE: number"],"sources":["../../src/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';\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 OLLAMA = 'ollama',\n}\n\nexport type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'none';\n\n/**\n * Common options for all AI providers\n */\nexport type AIOptions = {\n provider?: AIProvider;\n model?: Model;\n temperature?: number;\n baseURL?: string;\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 accessType: AccessType[],\n aiOptions?: AIOptions,\n isAuthenticated: boolean = false\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') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n // TODO: Implement premium user access\n if (accessType.includes('premium_user') && isAuthenticated) {\n return aiOptions?.apiKey ?? defaultApiKey;\n }\n\n return undefined;\n};\n\nconst getModelName = (\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 || provider === AIProvider.OLLAMA) {\n if (provider === AIProvider.OPENAI) {\n return userModel ?? defaultModel;\n }\n\n if (userModel) {\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 case AIProvider.OLLAMA:\n return '';\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\nconst getLanguageModel = (\n aiOptions: AIOptions,\n apiKey: string,\n defaultModel?: Model\n) => {\n const selectedModel = getModelName(\n aiOptions.provider as AIProvider,\n apiKey,\n aiOptions.model,\n defaultModel\n );\n\n const baseURL = aiOptions.baseURL;\n\n switch (aiOptions.provider) {\n case AIProvider.OPENAI: {\n return createOpenAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.ANTHROPIC: {\n return createAnthropic({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.MISTRAL: {\n return createMistral({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.DEEPSEEK: {\n return createDeepSeek({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.GEMINI: {\n return createGoogleGenerativeAI({\n apiKey,\n baseURL,\n })(selectedModel);\n }\n\n case AIProvider.OLLAMA: {\n // Ollama compatible mode:\n const ollama = createOpenAI({\n baseURL: baseURL ?? 'http://localhost:11434/v1',\n apiKey: apiKey ?? 'ollama', // Required but unused by Ollama\n });\n\n return ollama.chat(selectedModel);\n }\n\n default: {\n throw new Error(`Provider ${aiOptions.provider} not supported`);\n }\n }\n};\n\nexport type AIConfig = Omit<Parameters<typeof generateText>[0], 'prompt'> & {\n reasoningEffort?: ReasoningEffort;\n textVerbosity?: 'low' | 'medium' | 'high';\n};\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 options: AIConfigOptions,\n isAuthenticated: boolean = false\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(accessType, aiOptions, isAuthenticated);\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 languageModel = getLanguageModel(\n aiOptions,\n apiKey,\n defaultOptions?.model\n );\n\n return {\n model: languageModel,\n temperature: aiOptions.temperature,\n };\n};\n"],"mappings":";;;;;;;;;;AAwCA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;AACA;;;AA0BF,MAAM,aACJ,YACA,WACA,kBAA2B,UACxB;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,gBAC5C,QAAO,WAAW,UAAU;AAI9B,KAAI,WAAW,SAAS,eAAe,IAAI,gBACzC,QAAO,WAAW,UAAU;;AAMhC,MAAM,gBACJ,UACA,YACA,WACA,eAAsB,iBACZ;AAEV,KAAI,cAAc,aAAa,WAAW,QAAQ;AAChD,MAAI,aAAa,WAAW,OAC1B,QAAO,aAAa;AAGtB,MAAI,UACF,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,KAAK,WAAW,OACd,QAAO;GACT,QACE,QAAO;;;AAKb,KAAI,aAAa,SACf,OAAM,IAAI,MACR,4DACD;AAGH,QAAO;;AAGT,MAAM,oBACJ,WACA,QACA,iBACG;CACH,MAAM,gBAAgB,aACpB,UAAU,UACV,QACA,UAAU,OACV,aACD;CAED,MAAM,UAAU,UAAU;AAE1B,SAAQ,UAAU,UAAlB;EACE,KAAK,WAAW,OACd,QAAO,aAAa;GAClB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,UACd,QAAO,gBAAgB;GACrB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,QACd,QAAO,cAAc;GACnB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,SACd,QAAO,eAAe;GACpB;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OACd,QAAO,yBAAyB;GAC9B;GACA;GACD,CAAC,CAAC,cAAc;EAGnB,KAAK,WAAW,OAOd,QALe,aAAa;GAC1B,SAAS,WAAW;GACpB,QAAQ,UAAU;GACnB,CAAC,CAEY,KAAK,cAAc;EAGnC,QACE,OAAM,IAAI,MAAM,YAAY,UAAU,SAAS,gBAAgB;;;AAUrE,MAAMA,mBAA+B,WAAW;AAChD,MAAMC,sBAA8B;;;;;;;;AAepC,MAAa,cAAc,OACzB,SACA,kBAA2B,UACL;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,YAAY,WAAW,gBAAgB;AAGhE,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,eAAe,UAAU,SAAS,aAAa;AASjE,QAAO;EACL,OAPoB,iBACpB,WACA,QACA,gBAAgB,MACjB;EAIC,aAAa,UAAU;EACxB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readAsset } from "../_virtual/_utils_asset.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { generateObject } from "ai";
|
|
3
|
+
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
//#region src/auditDictionaryMetadata/index.ts
|
|
6
6
|
const aiDefaultOptions = {};
|
|
@@ -10,23 +10,38 @@ const aiDefaultOptions = {};
|
|
|
10
10
|
* and requests for identifying issues or inconsistencies.
|
|
11
11
|
*/
|
|
12
12
|
const auditDictionaryMetadata = async ({ fileContent, tags, aiConfig, applicationContext }) => {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const CHAT_GPT_PROMPT = readAsset("./PROMPT.md");
|
|
14
|
+
const EXAMPLE_REQUEST = readAsset("./EXAMPLE_REQUEST.md");
|
|
15
|
+
const EXAMPLE_RESPONSE = readAsset("./EXAMPLE_RESPONSE.md");
|
|
16
|
+
const prompt = CHAT_GPT_PROMPT.replace("{{applicationContext}}", applicationContext ?? "").replace("{{tags}}", tags ? JSON.stringify(tags.map(({ key, description }) => `- ${key}: ${description}`).join("\n\n"), null, 2) : "");
|
|
17
|
+
const { object, usage } = await generateObject({
|
|
15
18
|
...aiConfig,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
schema: z.object({
|
|
20
|
+
title: z.string(),
|
|
21
|
+
description: z.string(),
|
|
22
|
+
tags: z.array(z.string())
|
|
23
|
+
}),
|
|
24
|
+
messages: [
|
|
25
|
+
{
|
|
26
|
+
role: "system",
|
|
27
|
+
content: prompt
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
role: "user",
|
|
31
|
+
content: EXAMPLE_REQUEST
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
role: "assistant",
|
|
35
|
+
content: EXAMPLE_RESPONSE
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
role: "user",
|
|
39
|
+
content: fileContent
|
|
40
|
+
}
|
|
41
|
+
]
|
|
27
42
|
});
|
|
28
43
|
return {
|
|
29
|
-
fileContent:
|
|
44
|
+
fileContent: object,
|
|
30
45
|
tokenUsed: usage?.totalTokens ?? 0
|
|
31
46
|
};
|
|
32
47
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["aiDefaultOptions: AIOptions"],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["aiDefaultOptions: AIOptions"],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { generateObject } from 'ai';\nimport { z } from 'zod';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type AuditDictionaryMetadataOptions = {\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\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 fileContent,\n tags,\n aiConfig,\n applicationContext,\n}: AuditDictionaryMetadataOptions): Promise<\n AuditFileResultData | undefined\n> => {\n const CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n const EXAMPLE_REQUEST = readAsset('./EXAMPLE_REQUEST.md');\n const EXAMPLE_RESPONSE = readAsset('./EXAMPLE_RESPONSE.md');\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n ).replace(\n '{{tags}}',\n tags\n ? JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )\n : ''\n );\n\n // Use the AI SDK to generate the completion\n const { object, usage } = await generateObject({\n ...aiConfig,\n schema: z.object({\n title: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n }),\n messages: [\n { role: 'system', content: prompt },\n { role: 'user', content: EXAMPLE_REQUEST },\n { role: 'assistant', content: EXAMPLE_RESPONSE },\n {\n role: 'user',\n content: fileContent,\n },\n ],\n });\n\n return {\n fileContent: object,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;AA0BA,MAAaA,mBAA8B,EAE1C;;;;;;AAOD,MAAa,0BAA0B,OAAO,EAC5C,aACA,MACA,UACA,yBAGG;CACH,MAAM,kBAAkB,UAAU,cAAc;CAChD,MAAM,kBAAkB,UAAU,uBAAuB;CACzD,MAAM,mBAAmB,UAAU,wBAAwB;CAG3D,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,GACvB,CAAC,QACA,YACA,OACI,KAAK,UACH,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,cAAc,CACzD,KAAK,OAAO,EACf,MACA,EACD,GACD,GACL;CAGD,MAAM,EAAE,QAAQ,UAAU,MAAM,eAAe;EAC7C,GAAG;EACH,QAAQ,EAAE,OAAO;GACf,OAAO,EAAE,QAAQ;GACjB,aAAa,EAAE,QAAQ;GACvB,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;GAC1B,CAAC;EACF,UAAU;GACR;IAAE,MAAM;IAAU,SAAS;IAAQ;GACnC;IAAE,MAAM;IAAQ,SAAS;IAAiB;GAC1C;IAAE,MAAM;IAAa,SAAS;IAAkB;GAChD;IACE,MAAM;IACN,SAAS;IACV;GACF;EACF,CAAC;AAEF,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AIProvider, getAIConfig } from "./aiSdk.mjs";
|
|
2
2
|
import { customQuery } from "./customQuery.mjs";
|
|
3
|
-
import { extractJson } from "./utils/extractJSON.mjs";
|
|
4
3
|
import { auditDictionaryMetadata } from "./auditDictionaryMetadata/index.mjs";
|
|
5
4
|
import { translateJSON } from "./translateJSON/index.mjs";
|
|
5
|
+
import { extractJson } from "./utils/extractJSON.mjs";
|
|
6
6
|
import { generateText, streamText } from "ai";
|
|
7
7
|
|
|
8
8
|
export { AIProvider, auditDictionaryMetadata, customQuery, extractJson, generateText, getAIConfig, streamText, translateJSON };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AIProvider } from "../aiSdk.mjs";
|
|
2
2
|
import { readAsset } from "../_virtual/_utils_asset.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { generateObject } from "ai";
|
|
4
|
+
import { z } from "zod";
|
|
5
5
|
import { getLocaleName } from "@intlayer/core";
|
|
6
6
|
import { Locales } from "@intlayer/types";
|
|
7
7
|
|
|
@@ -34,29 +34,52 @@ const getModeInstructions = (mode) => {
|
|
|
34
34
|
if (mode === "complete") 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.";
|
|
35
35
|
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.";
|
|
36
36
|
};
|
|
37
|
+
const jsonToZod = (content) => {
|
|
38
|
+
if (typeof content === "string") return z.string();
|
|
39
|
+
if (typeof content === "number") return z.number();
|
|
40
|
+
if (typeof content === "boolean") return z.boolean();
|
|
41
|
+
if (Array.isArray(content)) {
|
|
42
|
+
if (content.length === 0) return z.array(z.string());
|
|
43
|
+
return z.array(jsonToZod(content[0]));
|
|
44
|
+
}
|
|
45
|
+
if (typeof content === "object" && content !== null) {
|
|
46
|
+
const shape = {};
|
|
47
|
+
for (const key in content) shape[key] = jsonToZod(content[key]);
|
|
48
|
+
return z.object(shape);
|
|
49
|
+
}
|
|
50
|
+
return z.any();
|
|
51
|
+
};
|
|
37
52
|
/**
|
|
38
53
|
* TranslateJSONs a content declaration file by constructing a prompt for AI models.
|
|
39
54
|
* The prompt includes details about the project's locales, file paths of content declarations,
|
|
40
55
|
* and requests for identifying issues or inconsistencies.
|
|
41
56
|
*/
|
|
42
57
|
const translateJSON = async ({ entryFileContent, presetOutputContent, dictionaryDescription, aiConfig, entryLocale, outputLocale, tags, mode, applicationContext }) => {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
58
|
+
const promptFile = readAsset("./PROMPT.md");
|
|
59
|
+
const formattedEntryLocale = formatLocaleWithName(entryLocale);
|
|
60
|
+
const formattedOutputLocale = formatLocaleWithName(outputLocale);
|
|
61
|
+
const prompt = promptFile.replace("{{entryLocale}}", formattedEntryLocale).replace("{{outputLocale}}", formattedOutputLocale).replace("{{presetOutputContent}}", JSON.stringify(presetOutputContent)).replace("{{dictionaryDescription}}", dictionaryDescription ?? "").replace("{{applicationContext}}", applicationContext ?? "").replace("{{tagsInstructions}}", formatTagInstructions(tags ?? [])).replace("{{modeInstructions}}", getModeInstructions(mode));
|
|
62
|
+
const { object, usage } = await generateObject({
|
|
45
63
|
...aiConfig,
|
|
64
|
+
schema: jsonToZod(entryFileContent),
|
|
46
65
|
messages: [{
|
|
47
66
|
role: "system",
|
|
48
67
|
content: prompt
|
|
49
68
|
}, {
|
|
50
69
|
role: "user",
|
|
51
70
|
content: [
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
`# Translation Request`,
|
|
72
|
+
`Please translate the following JSON content.`,
|
|
73
|
+
`- **From:** ${formattedEntryLocale}`,
|
|
74
|
+
`- **To:** ${formattedOutputLocale}`,
|
|
75
|
+
``,
|
|
76
|
+
`## Entry Content:`,
|
|
54
77
|
JSON.stringify(entryFileContent)
|
|
55
78
|
].join("\n")
|
|
56
79
|
}]
|
|
57
80
|
});
|
|
58
81
|
return {
|
|
59
|
-
fileContent:
|
|
82
|
+
fileContent: object,
|
|
60
83
|
tokenUsed: usage?.totalTokens ?? 0
|
|
61
84
|
};
|
|
62
85
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["aiDefaultOptions: AIOptions"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["aiDefaultOptions: AIOptions","shape: Record<string, z.ZodTypeAny>"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { generateObject } from 'ai';\nimport { z } from 'zod';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type TranslateJSONOptions<T> = {\n entryFileContent: T;\n presetOutputContent: Partial<T>;\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<T> = {\n fileContent: T;\n tokenUsed: number;\n};\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\nconst jsonToZod = (content: any): z.ZodTypeAny => {\n // Base case: content is a string (the translation target)\n if (typeof content === 'string') {\n return z.string();\n }\n\n // Base cases: primitives often preserved in i18n files (e.g. strict numbers/booleans)\n if (typeof content === 'number') {\n return z.number();\n }\n if (typeof content === 'boolean') {\n return z.boolean();\n }\n\n // Recursive case: Array\n if (Array.isArray(content)) {\n // If array is empty, we assume array of strings as default for i18n\n if (content.length === 0) {\n return z.array(z.string());\n }\n // We assume all items in the array share the structure of the first item\n return z.array(jsonToZod(content[0]));\n }\n\n // Recursive case: Object\n if (typeof content === 'object' && content !== null) {\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const key in content) {\n shape[key] = jsonToZod(content[key]);\n }\n return z.object(shape);\n }\n\n // Fallback\n return z.any();\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 <T>({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions<T>): Promise<\n TranslateJSONResultData<T> | undefined\n> => {\n const promptFile = readAsset('./PROMPT.md');\n\n const formattedEntryLocale = formatLocaleWithName(entryLocale);\n const formattedOutputLocale = formatLocaleWithName(outputLocale);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = promptFile\n .replace('{{entryLocale}}', formattedEntryLocale)\n .replace('{{outputLocale}}', formattedOutputLocale)\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 { object, usage } = await generateObject({\n ...aiConfig,\n schema: jsonToZod(entryFileContent),\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n // KEY CHANGE: Explicitly repeating instructions in the user message\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n JSON.stringify(entryFileContent),\n ].join('\\n'),\n },\n ],\n });\n\n return {\n fileContent: object as T,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;;;AA6BA,MAAaA,mBAA8B;CACzC,UAAU,WAAW;CACrB,OAAO;CACR;;;;;;;AAQD,MAAM,wBAAwB,WAC5B,GAAG,OAAO,IAAI,cAAc,QAAQ,QAAQ,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;;AAGT,MAAM,aAAa,YAA+B;AAEhD,KAAI,OAAO,YAAY,SACrB,QAAO,EAAE,QAAQ;AAInB,KAAI,OAAO,YAAY,SACrB,QAAO,EAAE,QAAQ;AAEnB,KAAI,OAAO,YAAY,UACrB,QAAO,EAAE,SAAS;AAIpB,KAAI,MAAM,QAAQ,QAAQ,EAAE;AAE1B,MAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAG5B,SAAO,EAAE,MAAM,UAAU,QAAQ,GAAG,CAAC;;AAIvC,KAAI,OAAO,YAAY,YAAY,YAAY,MAAM;EACnD,MAAMC,QAAsC,EAAE;AAC9C,OAAK,MAAM,OAAO,QAChB,OAAM,OAAO,UAAU,QAAQ,KAAK;AAEtC,SAAO,EAAE,OAAO,MAAM;;AAIxB,QAAO,EAAE,KAAK;;;;;;;AAQhB,MAAa,gBAAgB,OAAU,EACrC,kBACA,qBACA,uBACA,UACA,aACA,cACA,MACA,MACA,yBAGG;CACH,MAAM,aAAa,UAAU,cAAc;CAE3C,MAAM,uBAAuB,qBAAqB,YAAY;CAC9D,MAAM,wBAAwB,qBAAqB,aAAa;CAGhE,MAAM,SAAS,WACZ,QAAQ,mBAAmB,qBAAqB,CAChD,QAAQ,oBAAoB,sBAAsB,CAClD,QAAQ,2BAA2B,KAAK,UAAU,oBAAoB,CAAC,CACvE,QAAQ,6BAA6B,yBAAyB,GAAG,CACjE,QAAQ,0BAA0B,sBAAsB,GAAG,CAC3D,QAAQ,wBAAwB,sBAAsB,QAAQ,EAAE,CAAC,CAAC,CAClE,QAAQ,wBAAwB,oBAAoB,KAAK,CAAC;CAG7D,MAAM,EAAE,QAAQ,UAAU,MAAM,eAAe;EAC7C,GAAG;EACH,QAAQ,UAAU,iBAAiB;EACnC,UAAU,CACR;GAAE,MAAM;GAAU,SAAS;GAAQ,EACnC;GACE,MAAM;GAEN,SAAS;IACP;IACA;IACA,eAAe;IACf,aAAa;IACb;IACA;IACA,KAAK,UAAU,iBAAiB;IACjC,CAAC,KAAK,KAAK;GACb,CACF;EACF,CAAC;AAEF,QAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;EAClC"}
|
|
@@ -6,9 +6,9 @@ type Tag = {
|
|
|
6
6
|
key: string;
|
|
7
7
|
description?: string;
|
|
8
8
|
};
|
|
9
|
-
type TranslateJSONOptions = {
|
|
10
|
-
entryFileContent:
|
|
11
|
-
presetOutputContent:
|
|
9
|
+
type TranslateJSONOptions<T> = {
|
|
10
|
+
entryFileContent: T;
|
|
11
|
+
presetOutputContent: Partial<T>;
|
|
12
12
|
dictionaryDescription?: string;
|
|
13
13
|
entryLocale: Locale;
|
|
14
14
|
outputLocale: Locale;
|
|
@@ -17,8 +17,8 @@ type TranslateJSONOptions = {
|
|
|
17
17
|
mode: 'complete' | 'review';
|
|
18
18
|
applicationContext?: string;
|
|
19
19
|
};
|
|
20
|
-
type TranslateJSONResultData = {
|
|
21
|
-
fileContent:
|
|
20
|
+
type TranslateJSONResultData<T> = {
|
|
21
|
+
fileContent: T;
|
|
22
22
|
tokenUsed: number;
|
|
23
23
|
};
|
|
24
24
|
declare const aiDefaultOptions: AIOptions;
|
|
@@ -27,7 +27,7 @@ declare const aiDefaultOptions: AIOptions;
|
|
|
27
27
|
* The prompt includes details about the project's locales, file paths of content declarations,
|
|
28
28
|
* and requests for identifying issues or inconsistencies.
|
|
29
29
|
*/
|
|
30
|
-
declare const translateJSON: ({
|
|
30
|
+
declare const translateJSON: <T>({
|
|
31
31
|
entryFileContent,
|
|
32
32
|
presetOutputContent,
|
|
33
33
|
dictionaryDescription,
|
|
@@ -37,7 +37,7 @@ declare const translateJSON: ({
|
|
|
37
37
|
tags,
|
|
38
38
|
mode,
|
|
39
39
|
applicationContext
|
|
40
|
-
}: TranslateJSONOptions) => Promise<TranslateJSONResultData | undefined>;
|
|
40
|
+
}: TranslateJSONOptions<T>) => Promise<TranslateJSONResultData<T> | undefined>;
|
|
41
41
|
//#endregion
|
|
42
42
|
export { TranslateJSONOptions, TranslateJSONResultData, aiDefaultOptions, translateJSON };
|
|
43
43
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":[],"mappings":";;;;KAOK,GAAA;;EAAA,WAAG,CAAA,EAAA,MAAA;AAKR,CAAA;AACoB,KADR,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":[],"mappings":";;;;KAOK,GAAA;;EAAA,WAAG,CAAA,EAAA,MAAA;AAKR,CAAA;AACoB,KADR,oBACQ,CAAA,CAAA,CAAA,GAAA;EACW,gBAAA,EADX,CACW;EAAR,mBAAA,EAAA,OAAA,CAAQ,CAAR,CAAA;EAER,qBAAA,CAAA,EAAA,MAAA;EACC,WAAA,EADD,MACC;EACP,YAAA,EADO,MACP;EACG,IAAA,CAAA,EADH,GACG,EAAA;EAAQ,QAAA,EAAR,QAAQ;EAKR,IAAA,EAAA,UAAA,GAAA,QAAuB;EAKtB,kBAAA,CAGZ,EAAA,MAAA;AA+ED,CAAA;AAAuC,KAvF3B,uBAuF2B,CAAA,CAAA,CAAA,GAAA;EAAA,WAAA,EAtFxB,CAsFwB;EAAA,SAAA,EAAA,MAAA;CAAA;AAAA,cAlF1B,gBAkF0B,EAlFR,SAkFQ;;;;;;AAUpC,cAVU,aAUV,EAAA,CAAA,CAAA,CAAA,CAAA;EAAA,gBAAA;EAAA,mBAAA;EAAA,qBAAA;EAAA,QAAA;EAAA,WAAA;EAAA,YAAA;EAAA,IAAA;EAAA,IAAA;EAAA;AAAA,CAAA,EAAA,oBAAA,CAAqB,CAArB,CAAA,EAAA,GAA0B,OAA1B,CACD,uBADC,CACuB,CADvB,CAAA,GAAA,SAAA,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/ai",
|
|
3
|
-
"version": "7.5.2-canary.
|
|
3
|
+
"version": "7.5.2-canary.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "SDK that provides AI capabilities for Intlayer applications",
|
|
6
6
|
"keywords": [
|
|
@@ -82,7 +82,8 @@
|
|
|
82
82
|
"@intlayer/config": "7.5.2-canary.0",
|
|
83
83
|
"@intlayer/core": "7.5.2-canary.0",
|
|
84
84
|
"@intlayer/types": "7.5.2-canary.0",
|
|
85
|
-
"ai": "5.0.116"
|
|
85
|
+
"ai": "5.0.116",
|
|
86
|
+
"zod": "^4.2.1"
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
88
89
|
"@types/node": "25.0.3",
|