gptrans 2.1.10 β†’ 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,10 +66,18 @@ const model = ModelMix.new()
66
66
 
67
67
  If `sonnet45` fails, it automatically tries `gpt5mini`, then `gemini3flash`.
68
68
 
69
+ The equivalent `chain()` syntax accepts `shortcut@<effort>` to override unified effort for one model. `<effort>` must be an integer from `0` to `100`; models without the suffix keep their configured or provider-default effort:
70
+
71
+ ```javascript
72
+ const model = ModelMix.new()
73
+ .chain('sonnet5@50', 'gpt56luna@100');
74
+ ```
75
+
69
76
  ## Available Model Shorthands
70
77
 
71
78
  - **OpenAI**: `gpt52` `gpt51` `gpt5` `gpt5mini` `gpt5nano` `gpt41` `gpt41mini` `gpt41nano`
72
- - **Anthropic**: `opus46` `opus45` `sonnet45` `sonnet4` `haiku45` `haiku35` (thinking variants: add `think` suffix)
79
+ - **Anthropic**: `opus46` `opus45` `sonnet5` `sonnet45` `sonnet4` `haiku45` `haiku35` (thinking variants: add `think` suffix)
80
+ - **OpenAI Codex**: `gpt56sol` `gpt56terra` `gpt56luna`
73
81
  - **Google**: `gemini3pro` `gemini3flash` `gemini25pro` `gemini25flash`
74
82
  - **Grok**: `grok4` `grok41` (thinking variant available)
75
83
  - **Perplexity**: `sonar` `sonarPro`
@@ -158,11 +166,11 @@ const description = await model.message();
158
166
  const model = ModelMix.new().gpt5mini();
159
167
  model.setSystemFromFile('./prompts/system.md');
160
168
  model.addTextFromFile('./prompts/task.md');
161
- model.replace({
162
- '{role}': 'data analyst',
163
- '{language}': 'Spanish'
169
+ model.assign({
170
+ role: 'data analyst',
171
+ language: 'Spanish'
164
172
  });
165
- model.replaceKeyFromFile('{code}', './src/utils.js');
173
+ model.assignKeyFromFile('code', './src/utils.js');
166
174
  console.log(await model.message());
167
175
  ```
168
176
 
@@ -268,7 +276,7 @@ const reply = await chat.message(); // "Martin"
268
276
  - Use `.json()` for structured output instead of parsing text manually.
269
277
  - Use `.message()` for simple text, `.raw()` when you need tokens/thinking/toolCalls.
270
278
  - For thinking models, append `think` to the method name (e.g. `sonnet45think()`).
271
- - Template placeholders use `{key}` syntax in both system prompts and user messages.
279
+ - Template placeholders use EJS syntax such as `<%- key %>` in system prompts and user messages.
272
280
  - The library uses CommonJS internally (`require`) but supports ESM import via `{ ModelMix }`.
273
281
  - Available provider Mix classes for custom setups: `MixOpenAI`, `MixAnthropic`, `MixGoogle`, `MixPerplexity`, `MixGroq`, `MixTogether`, `MixGrok`, `MixOpenRouter`, `MixOllama`, `MixLMStudio`, `MixCustom`, `MixCerebras`, `MixFireworks`, `MixMiniMax`.
274
282
 
@@ -282,8 +290,9 @@ const reply = await chat.message(); // "Martin"
282
290
  | `.setSystemFromFile(path)` | `this` | Set system prompt from file |
283
291
  | `.addImage(path)` | `this` | Add image from file |
284
292
  | `.addImageFromUrl(url)` | `this` | Add image from URL or data URI |
285
- | `.replace({})` | `this` | Set placeholder replacements |
286
- | `.replaceKeyFromFile(key, path)` | `this` | Replace placeholder with file content |
293
+ | `.assign({})` | `this` | Assign EJS template data |
294
+ | `.assignKeyFromFile(key, path)` | `this` | Assign rendered file content to one template key |
295
+ | `.chain(...models)` | `this` | Attach ordered shortcuts; each may use `@<effort>` from `0` to `100` |
287
296
  | `.message()` | `Promise<string>` | Get text response |
288
297
  | `.json(example, desc?, opts?)` | `Promise<object>` | Get structured JSON |
289
298
  | `.raw()` | `Promise<{message, think, toolCalls, tokens, response}>` | Full response |
package/README.md CHANGED
@@ -30,7 +30,7 @@ npm install gptrans
30
30
 
31
31
  ### 🌐 Environment Setup
32
32
 
33
- On Node.js 20.6+, GPTrans loads a `.env` from the current working directory via `process.loadEnvFile()` when you construct `GPTrans` (missing or invalid files are ignored). Create a `.env` in your project root and add your API keys:
33
+ GPTrans reads provider credentials from `process.env` and does not load `.env` files itself. If you use a `.env` file, load it in your application before constructing `GPTrans`. Create the file in your project root and add your API keys:
34
34
 
35
35
  ```env
36
36
  OPENAI_API_KEY=your_openai_api_key
@@ -44,11 +44,15 @@ Here's a simple example to get you started:
44
44
 
45
45
  ```javascript
46
46
  import GPTrans from 'gptrans';
47
+ import { join } from 'node:path';
48
+ import { loadEnvFile } from 'node:process';
47
49
 
50
+ loadEnvFile(join(import.meta.dirname, '.env'));
51
+ const dbPath = join(import.meta.dirname, 'db');
48
52
  const gptrans = new GPTrans({
53
+ path: dbPath,
49
54
  from: 'en-US',
50
- target: 'es-AR',
51
- model: 'sonnet45'
55
+ target: 'es-AR'
52
56
  });
53
57
 
54
58
  // Translate text with parameter substitution
@@ -74,9 +78,10 @@ When creating a new instance of GPTrans, you can customize:
74
78
 
75
79
  | Option | Description | Default |
76
80
  |--------|-------------|---------|
81
+ | `path` | Absolute directory path for the DeepBase translation cache | Required |
77
82
  | `from` | Source language locale (BCP 47) | `en-US` |
78
83
  | `target` | Target language locale (BCP 47) | `es` |
79
- | `model` | Translation model key or array of models for fallback | `sonnet45` `gpt41` |
84
+ | `model` | ModelMix shortcut or array for an ordered fallback chain; append `@<effort>` (`0`–`100`) to override one model | `['sonnet5@50', 'gpt56luna@100']` |
80
85
  | `batchThreshold` | Maximum number of characters to accumulate before triggering batch processing | `1500` |
81
86
  | `debounceTimeout` | Time in milliseconds to wait before processing translations | `500` |
82
87
  | `instruction` | Additional instruction for the translator (e.g., tone, style). Does not affect the cache key | `''` |
@@ -96,7 +101,7 @@ For simplified or universal language codes, you can omit the region specificatio
96
101
  ## πŸ” How It Works
97
102
 
98
103
  1. **First-Time Translation Behavior:** On the first request, Gptrans will return the original text while processing the translation in the background. This ensures your application remains responsive without waiting for API calls.
99
- 2. **Translation Caching:** Once processed, translations are stored in `db/gptrans_<tag>.json`. Subsequent requests for the same text will be served instantly from the cache.
104
+ 2. **Translation Caching:** Once processed, translations are stored as `gptrans_<tag>.json` under the required absolute `path`. Subsequent requests for the same text will be served instantly from the cache.
100
105
  3. **Smart Batch Processing:** Automatically groups translation requests to optimize API usage and provide better context.
101
106
  4. **Dynamic Model Integration:** Easily plug in multiple AI translation providers with the ModelMix library.
102
107
  5. **Customizable Prompts:** Load and modify translation prompts (see the `prompt/translate.md` file) to fine-tune the translation output.
@@ -132,6 +137,7 @@ The `instruction` option lets you guide the AI translator's style, tone, or beha
132
137
 
133
138
  ```javascript
134
139
  const gptrans = new GPTrans({
140
+ path: dbPath,
135
141
  from: 'en',
136
142
  target: 'es-AR',
137
143
  instruction: 'Use natural and colloquial tone'
@@ -168,7 +174,7 @@ Include translations from other languages as context to improve accuracy and con
168
174
 
169
175
  ```javascript
170
176
  // Use English and Portuguese translations as reference
171
- const gptrans = new GPTrans({ from: 'es', target: 'fr' });
177
+ const gptrans = new GPTrans({ path: dbPath, from: 'es', target: 'fr' });
172
178
  await gptrans.preload({
173
179
  references: ['en', 'pt']
174
180
  });
@@ -182,7 +188,7 @@ Translate from an intermediate language instead of the original:
182
188
 
183
189
  ```javascript
184
190
  // Original is Spanish, but translate FROM English TO Portuguese
185
- const gptrans = new GPTrans({ from: 'es', target: 'pt' });
191
+ const gptrans = new GPTrans({ path: dbPath, from: 'es', target: 'pt' });
186
192
  await gptrans.preload({
187
193
  baseLanguage: 'en'
188
194
  });
@@ -199,7 +205,7 @@ You can use both options together:
199
205
 
200
206
  ```javascript
201
207
  // Translate from English to German, showing Spanish and Portuguese as reference
202
- const gptrans = new GPTrans({ from: 'es', target: 'de' });
208
+ const gptrans = new GPTrans({ path: dbPath, from: 'es', target: 'de' });
203
209
  await gptrans.preload({
204
210
  baseLanguage: 'en',
205
211
  references: ['es', 'pt']
@@ -215,7 +221,7 @@ await gptrans.preload({
215
221
  // English: "The student is very good" (neutral)
216
222
 
217
223
  // Solution: Translate to Portuguese using English as base
218
- const ptTranslator = new GPTrans({ from: 'es', target: 'pt' });
224
+ const ptTranslator = new GPTrans({ path: dbPath, from: 'es', target: 'pt' });
219
225
  await ptTranslator.preload({
220
226
  baseLanguage: 'en', // Use neutral English version
221
227
  references: ['es'] // Show original Spanish for context
@@ -236,7 +242,8 @@ GPTrans supports a fallback mechanism for translation models. Instead of providi
236
242
 
237
243
  ```javascript
238
244
  const translator = new GPTrans({
239
- model: ['claude46', 'gpt54'],
245
+ path: dbPath,
246
+ model: ['sonnet5@50', 'gpt56luna@100'],
240
247
  // ... other options
241
248
  });
242
249
  ```
@@ -244,6 +251,7 @@ const translator = new GPTrans({
244
251
  When using multiple models:
245
252
  - The first model in the array is used as the primary translation service
246
253
  - If the primary model fails (due to API errors, rate limits, etc.), GPTrans automatically falls back to the next model
254
+ - Use `model@<effort>` with an integer from `0` to `100` to set ModelMix's unified effort for that model only. By default, `sonnet5` uses effort `50`; if it fails, `gpt56luna` uses effort `100`. Models without the suffix keep their configured or provider-default effort.
247
255
  - This ensures higher availability and resilience of your translation service
248
256
 
249
257
  ## ✏️ Refining Translations
@@ -251,7 +259,7 @@ When using multiple models:
251
259
  The `refine()` method lets you improve existing translations by running them through the AI again with a specific instruction. It processes translations in batches (same as the translation flow) and only updates entries that genuinely benefit from the refinement.
252
260
 
253
261
  ```javascript
254
- const gptrans = new GPTrans({ from: 'en', target: 'es-AR' });
262
+ const gptrans = new GPTrans({ path: dbPath, from: 'en', target: 'es-AR' });
255
263
 
256
264
  // After translations already exist...
257
265
  // Refine with a single instruction
@@ -306,4 +314,3 @@ Contributions are welcome! Please open an issue or submit a pull request on GitH
306
314
  GPTrans is released under the MIT License.
307
315
 
308
316
  Happy translating! 🌍✨
309
-
package/demo/case_1.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import GPTrans from '../index.js';
2
+ import { join } from 'path';
2
3
 
3
- const gptrans = new GPTrans({ model: 'sonnet45' });
4
+ const dbPath = join(import.meta.dirname, '../db');
5
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
6
+ const gptrans = new GPTrans({ path: dbPath, model: 'sonnet45' });
4
7
 
5
8
  console.log(gptrans.t('Hello, {name}!', { name: 'Anya' }));
6
9
 
@@ -14,6 +17,7 @@ console.log(gptrans.t('Card'));
14
17
 
15
18
  // Case 2: Translate from Spanish Spain to Spanish Argentina
16
19
  const es2ar = new GPTrans({
20
+ path: dbPath,
17
21
  from: 'es-ES',
18
22
  target: 'es-AR',
19
23
  model: 'sonnet46'
@@ -25,10 +29,10 @@ console.log(es2ar.setContext().t('Tienes fuego?'));
25
29
 
26
30
  // Case 3
27
31
  const ar2es = new GPTrans({
32
+ path: dbPath,
28
33
  from: 'es-AR',
29
34
  target: 'es-ES',
30
- model: 'gpt41'
35
+ model: 'gpt56terra@20'
31
36
  });
32
37
 
33
38
  console.log(ar2es.t('ΒΏTenΓ©s fuego?'));
34
-
package/demo/case_2.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import GPTrans from '../index.js';
2
+ import { join } from 'path';
3
+
4
+ const dbPath = join(import.meta.dirname, '../db');
5
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
2
6
 
3
7
  try {
4
8
  const gptrans = new GPTrans({
9
+ path: dbPath,
5
10
  target: 'it',
6
11
  });
7
12
 
@@ -17,4 +22,4 @@ try {
17
22
  console.log(gptrans.t('Card'));
18
23
  } catch (e) {
19
24
  console.error(e);
20
- }
25
+ }
package/demo/case_3.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import GPTrans from '../index.js';
2
+ import { join } from 'path';
3
+
4
+ const dbPath = join(import.meta.dirname, '../db');
5
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
2
6
 
3
7
  try {
4
8
  const gptrans = new GPTrans({
9
+ path: dbPath,
5
10
  target: 'ar',
6
11
  from: 'es',
7
12
  });
@@ -9,4 +14,4 @@ try {
9
14
  console.log(gptrans.t('Cargando...'));
10
15
  } catch (e) {
11
16
  console.error(e);
12
- }
17
+ }
package/demo/case_4.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import GPTrans from '../index.js';
2
- import { dirname } from 'path';
3
- import { fileURLToPath } from 'url';
2
+ import { join } from 'path';
4
3
  import { promises as fs } from 'fs';
5
4
 
6
- // Get current file directory
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ const dbPath = join(import.meta.dirname, '../db');
6
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
8
7
 
9
8
  // Initialize translator
10
9
  const model = new GPTrans({
11
- model: ['sonnet46', 'gpt54'],
10
+ path: dbPath,
11
+ model: ['sonnet50', 'gpt56luna@80'],
12
12
  from: 'es', // Assuming the source file is in Spanish
13
13
  target: 'en',
14
14
  });
@@ -17,11 +17,9 @@ const model = new GPTrans({
17
17
  await model.preload();
18
18
 
19
19
  // Read and translate the file
20
- const filePath = `${__dirname}/georgia_incident.md`;
20
+ const filePath = join(import.meta.dirname, 'georgia_incident.md');
21
21
  const content = await fs.readFile(filePath, 'utf-8');
22
22
 
23
23
  // Translate the content
24
24
  const translatedContent = model.t(content);
25
25
  console.log(translatedContent);
26
-
27
-
@@ -1,8 +1,13 @@
1
1
  import GPTrans from '../index.js';
2
+ import { join } from 'path';
3
+
4
+ const dbPath = join(import.meta.dirname, '../db');
5
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
2
6
 
3
7
  // Ejemplo: CΓ³mo obtener la informaciΓ³n completa del idioma
4
8
 
5
9
  const gptrans = new GPTrans({
10
+ path: dbPath,
6
11
  from: 'es',
7
12
  target: 'en',
8
13
  });
@@ -25,6 +30,7 @@ console.log(' Gentilicio:', gptrans.replaceFrom.FROM_DENONYM); // 'Spani
25
30
  console.log('\nπŸ“Œ Ejemplo con variantes regionales:');
26
31
 
27
32
  const gptrans2 = new GPTrans({
33
+ path: dbPath,
28
34
  from: 'en-GB',
29
35
  target: 'pt-BR',
30
36
  });
@@ -1,15 +1,9 @@
1
1
  import GPTrans from '../index.js';
2
- import { fileURLToPath } from 'url';
3
- import { dirname, join } from 'path';
2
+ import { join } from 'path';
4
3
 
5
4
  // Cargar .env desde la carpeta demo
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = dirname(__filename);
8
- try {
9
- process.loadEnvFile(join(__dirname, '.env'));
10
- } catch {
11
- /* optional .env missing or unreadable */
12
- }
5
+ const dbPath = join(import.meta.dirname, '../db');
6
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
13
7
 
14
8
  console.log('πŸš€ Prueba de Paralelismo en GPTrans');
15
9
  console.log('⚠️ MΓΊltiples instancias con MISMO NOMBRE y MISMO PAR DE IDIOMAS\n');
@@ -43,6 +37,7 @@ async function testParallelTranslations() {
43
37
  texts.map(async (text, index) => {
44
38
  // Todas las instancias comparten el MISMO NOMBRE
45
39
  const gptrans = new GPTrans({
40
+ path: dbPath,
46
41
  from: sourceLang,
47
42
  target: targetLang,
48
43
  model: 'sonnet45',
@@ -1,15 +1,9 @@
1
1
  import GPTrans from '../index.js';
2
- import { fileURLToPath } from 'url';
3
- import { dirname, join } from 'path';
2
+ import { join } from 'path';
4
3
 
5
4
  // Load .env from demo folder
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = dirname(__filename);
8
- try {
9
- process.loadEnvFile(join(__dirname, '.env'));
10
- } catch {
11
- /* optional .env missing or unreadable */
12
- }
5
+ const dbPath = join(import.meta.dirname, '../db');
6
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
13
7
 
14
8
  console.log('πŸš€ Testing GPTrans with Reference Translations\n');
15
9
  console.log('='.repeat(70));
@@ -19,6 +13,7 @@ async function testReferences() {
19
13
 
20
14
  // First, create translations in English and Portuguese
21
15
  const enTranslator = new GPTrans({
16
+ path: dbPath,
22
17
  from: 'es',
23
18
  target: 'en',
24
19
  model: 'sonnet45',
@@ -27,6 +22,7 @@ async function testReferences() {
27
22
  });
28
23
 
29
24
  const ptTranslator = new GPTrans({
25
+ path: dbPath,
30
26
  from: 'es',
31
27
  target: 'pt',
32
28
  model: 'sonnet45',
@@ -63,6 +59,7 @@ async function testReferences() {
63
59
 
64
60
  // Now translate to French using English as reference
65
61
  const frTranslator = new GPTrans({
62
+ path: dbPath,
66
63
  from: 'es',
67
64
  target: 'fr',
68
65
  model: 'sonnet45',
@@ -86,6 +83,7 @@ async function testReferences() {
86
83
 
87
84
  // Translate from English to Italian (using English as base instead of Spanish)
88
85
  const itTranslator = new GPTrans({
86
+ path: dbPath,
89
87
  from: 'es',
90
88
  target: 'it',
91
89
  model: 'sonnet45',
@@ -110,6 +108,7 @@ async function testReferences() {
110
108
 
111
109
  // Translate to German with multiple references
112
110
  const deTranslator = new GPTrans({
111
+ path: dbPath,
113
112
  from: 'es',
114
113
  target: 'de',
115
114
  model: 'sonnet45',
@@ -1,4 +1,8 @@
1
1
  import GPTrans from '../index.js';
2
+ import { join } from 'path';
3
+
4
+ const dbPath = join(import.meta.dirname, '../db');
5
+ try { process.loadEnvFile(join(import.meta.dirname, '.env')); } catch { }
2
6
 
3
7
  console.log('πŸš€ Testing GPTrans Refine\n');
4
8
  console.log('='.repeat(70));
@@ -6,6 +10,7 @@ console.log('='.repeat(70));
6
10
  async function testRefine() {
7
11
  // Step 1: Create initial translations
8
12
  const gptrans = new GPTrans({
13
+ path: dbPath,
9
14
  from: 'en-US',
10
15
  target: 'es-AR',
11
16
  model: 'sonnet45',
@@ -2,15 +2,12 @@
2
2
  import GPTrans from '../index.js';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
5
- import { fileURLToPath } from 'url';
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = path.dirname(__filename);
9
5
 
10
6
  async function main() {
11
7
  try {
12
8
  // Initialize GPTrans with Spanish as target
13
9
  const gptrans = new GPTrans({
10
+ path: path.join(import.meta.dirname, 'db'),
14
11
  from: 'en-US',
15
12
  target: 'es'
16
13
  });
@@ -23,7 +20,7 @@ async function main() {
23
20
  console.log(' Input: en/image.jpg');
24
21
  console.log(' Expected output: es/image.jpg (sibling folder)\n');
25
22
 
26
- const imageInLangFolder = path.join(__dirname, 'en', 'camera_4126.jpg');
23
+ const imageInLangFolder = path.join(import.meta.dirname, 'en', 'camera_4126.jpg');
27
24
  if (fs.existsSync(imageInLangFolder)) {
28
25
  const result1 = await gptrans.img(imageInLangFolder);
29
26
  console.log(' Result:', result1);
@@ -38,7 +35,7 @@ async function main() {
38
35
  // console.log(' Input: ./image.jpg');
39
36
  // console.log(' Expected output: ./es/image.jpg (subfolder)\n');
40
37
 
41
- // const imageInRoot = path.join(__dirname, 'camera_4126.jpg');
38
+ // const imageInRoot = path.join(import.meta.dirname, 'camera_4126.jpg');
42
39
  // if (fs.existsSync(imageInRoot)) {
43
40
  // const result2 = await gptrans.img(imageInRoot);
44
41
  // console.log(' Result:', result2);
@@ -53,7 +50,7 @@ async function main() {
53
50
  // console.log(' Input: images/photo.jpg');
54
51
  // console.log(' Expected output: images/es/photo.jpg (subfolder)\n');
55
52
 
56
- // const imageInCustomFolder = path.join(__dirname, 'images', 'photo.jpg');
53
+ // const imageInCustomFolder = path.join(import.meta.dirname, 'images', 'photo.jpg');
57
54
  // if (fs.existsSync(imageInCustomFolder)) {
58
55
  // const result3 = await gptrans.img(imageInCustomFolder);
59
56
  // console.log(' Result:', result3);
@@ -81,4 +78,3 @@ async function main() {
81
78
  }
82
79
 
83
80
  main();
84
-