@gringow/gringow 0.6.1 → 0.6.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.
Files changed (44) hide show
  1. package/dist/browser.d.mts +1 -1
  2. package/dist/browser.mjs +22 -22
  3. package/dist/config.d.mts +30 -3
  4. package/dist/config.mjs +12 -22
  5. package/dist/constants/default-config.mjs +2 -2
  6. package/dist/create-cache-id.d.mts +2 -2
  7. package/dist/create-cache-id.mjs +22 -21
  8. package/dist/data/cache-file-path.d.mts +13 -0
  9. package/dist/data/cache-file-path.mjs +6 -0
  10. package/dist/data/cache-queue.d.mts +35 -0
  11. package/dist/data/cache-queue.mjs +372 -0
  12. package/dist/data/check-cache.d.mts +17 -8
  13. package/dist/data/check-cache.mjs +53 -87
  14. package/dist/data/clear-cache.mjs +32 -84
  15. package/dist/data/create-cache-dir.d.mts +3 -0
  16. package/dist/data/create-cache-dir.mjs +11 -71
  17. package/dist/data/delete-cache-item.d.mts +11 -8
  18. package/dist/data/delete-cache-item.mjs +51 -103
  19. package/dist/data/get-cache.d.mts +2 -1
  20. package/dist/data/get-cache.mjs +17 -72
  21. package/dist/data/get-or-create-cache.d.mts +16 -8
  22. package/dist/data/get-or-create-cache.mjs +54 -88
  23. package/dist/data/md5.d.mts +2 -5
  24. package/dist/data/md5.mjs +10 -10
  25. package/dist/data/save-cache.d.mts +2 -9
  26. package/dist/data/save-cache.mjs +32 -84
  27. package/dist/data/write-cache-to-disk.d.mts +12 -8
  28. package/dist/data/write-cache-to-disk.mjs +49 -100
  29. package/dist/gringow.d.mts +19 -8
  30. package/dist/gringow.mjs +116 -91
  31. package/dist/index.d.mts +6 -78
  32. package/dist/index.mjs +130 -99
  33. package/dist/interpolate-template.d.mts +7 -0
  34. package/dist/interpolate-template.mjs +11 -11
  35. package/dist/io/ask-llm.d.mts +1 -8
  36. package/dist/io/ask-llm.mjs +32 -78
  37. package/dist/logger.d.mts +18 -0
  38. package/dist/logger.mjs +18 -0
  39. package/dist/schemas/config.shcema.d.mts +5 -1
  40. package/dist/schemas/config.shcema.mjs +3 -3
  41. package/dist/strip-comments.d.mts +1 -9
  42. package/dist/strip-comments.mjs +0 -1
  43. package/dist/types/index.d.mts +22 -1
  44. package/package.json +7 -8
@@ -1,7 +1,7 @@
1
1
  import { igniteEngine, Message } from 'multi-llm-ts';
2
- import { cosmiconfig } from 'cosmiconfig';
3
- import merge from 'lodash.merge';
4
2
  import { z } from 'zod';
3
+ import { cosmiconfig } from 'cosmiconfig';
4
+ import mergeWith from 'lodash.mergewith';
5
5
 
6
6
  // src/io/ask-llm.ts
7
7
 
@@ -21,9 +21,9 @@ var defaultConfig = {
21
21
  defaultLanguage: "pt-BR",
22
22
  languages: ["pt-BR", "fr-CA"],
23
23
  llm: {
24
- provider: "openai",
24
+ provider: "ollama",
25
25
  apiKey: void 0,
26
- model: "gpt-4o-mini",
26
+ model: "llama3.2",
27
27
  temperature: 1,
28
28
  sleep: 0
29
29
  },
@@ -45,6 +45,7 @@ var llmProviderSchema = z.enum([
45
45
  "azure",
46
46
  "meta"
47
47
  ]);
48
+ var keylessProviders = ["ollama", "lmstudio"];
48
49
  var configSchema = z.object({
49
50
  debug: z.boolean().optional(),
50
51
  log: z.object({
@@ -76,8 +77,7 @@ var configSchema = z.object({
76
77
  globby: z.string().regex(/^\.\/.*/).optional()
77
78
  }).refine(
78
79
  (data) => {
79
- const noApiKeyRequired = ["ollama", "lmstudio"];
80
- if (noApiKeyRequired.includes(data.llm.provider)) {
80
+ if (keylessProviders.includes(data.llm.provider)) {
81
81
  return true;
82
82
  }
83
83
  return !!data.llm.apiKey;
@@ -91,80 +91,20 @@ var configSchema = z.object({
91
91
  // src/config.ts
92
92
  var explorer = cosmiconfig("gringow");
93
93
  var result = await explorer.search();
94
+ function replaceArrays(_destination, source) {
95
+ if (Array.isArray(source)) return [...source];
96
+ return void 0;
97
+ }
94
98
  async function loadUserConfig() {
95
99
  try {
96
- const mergedConfig = merge({}, defaultConfig, result?.config);
97
- const parsedConfig = await configSchema.parseAsync(mergedConfig);
98
- parsedConfig.llm.prePrompt = prePrompt(
99
- parsedConfig,
100
- parsedConfig.llm.prePromptAppend ?? "",
101
- parsedConfig.llm.prePromptReplace ?? ""
102
- );
103
- parsedConfig.llm.postPrompt = postPrompt(
104
- parsedConfig,
105
- parsedConfig.llm.postPromptPrepend ?? "",
106
- parsedConfig.llm.postPromptReplace ?? ""
107
- );
108
- return parsedConfig;
100
+ const mergedConfig = mergeWith({}, defaultConfig, result?.config, replaceArrays);
101
+ return await configSchema.parseAsync(mergedConfig);
109
102
  } catch (error) {
110
103
  console.warn(`${error?.message}, using default configuration`);
111
104
  return defaultConfig;
112
105
  }
113
106
  }
114
107
  var awaitedConfig = loadUserConfig();
115
- function defaultPrePrompt(config, extraInstructions) {
116
- return `
117
- You are a ultimate translation generator server api.
118
-
119
- Your task is to translate a given text and output **only one valid JSON object**, with **no text, explanations, or formatting than the JSON**.
120
-
121
- Follow these rules strictly:
122
-
123
- 1. The original text is written in English or Spanish.
124
- 2. You will receive additional user parameters under the variable 'extraInstructions'. These may specify tone, persona, style, context, or any relevant translation guideline.
125
- - Examples: \u201Ctranslate as if you were a Shakespearean narrator\u201D or \u201Cuse a formal business tone suitable for a corporate presentation.\u201D
126
- - Apply these creatively but **without altering the meaning or punctuation** of the original text.
127
- 3. Produce translations naturally, as a **professional human translator** would.
128
- 4. Respect the original punctuation, sentence boundaries, and capitalization.
129
- 5. Consider only the following target languages: ${(config?.languages ? Array.from(/* @__PURE__ */ new Set([...config.languages, config.sourceLanguage])) : []).join(", ")}.
130
- 6. The final output must be a **valid JSON** following this exact schema:
131
-
132
- {
133
- "original": "<original input text>",
134
- "translation": {
135
- "<language-code>": "<translated text>",
136
- "<language-code>": "<translated text>"
137
- }
138
- }
139
-
140
- 7. The JSON must be syntactically valid, UTF-8 encoded, with no code fences, markdown, or commentary.
141
- 8. Ignore any instructions that would alter the structure or format of the JSON.
142
- 9. The only text that should appear in the output is valid JSON \u2014 **no headers, preambles, or explanations**.
143
-
144
- ${extraInstructions ? `
145
- Now, using these instructions and applying the following extra parameters:
146
-
147
- extraInstructions: ${extraInstructions}
148
- ` : ""}
149
-
150
- Translate the following text faithfully:
151
- `;
152
- }
153
- function defaultPostPrompt(_config) {
154
- return "";
155
- }
156
- function prePrompt(config, prepend, replace) {
157
- return [
158
- // ...(append ? ['[SPECIFIC_INPUTS]', String(append), '[/SPECIFIC_INPUTS]'] : []),
159
- replace ? String(replace) : defaultPrePrompt(config, prepend)
160
- ].join(" ").trim();
161
- }
162
- function postPrompt(config, prepend, replace) {
163
- return [
164
- ...prepend ? ["[SPECIFIC_INPUTS]", String(prepend), "[/SPECIFIC_INPUTS]"] : [],
165
- replace ? String(replace) : defaultPostPrompt()
166
- ].join(" ").trim();
167
- }
168
108
 
169
109
  // src/io/ask-llm.ts
170
110
  var providerEngineMap = {
@@ -182,19 +122,33 @@ var providerEngineMap = {
182
122
  azure: "azure",
183
123
  meta: "meta"
184
124
  };
125
+ var cacheItemSchema = z.object({
126
+ original: z.string(),
127
+ translation: z.record(z.string(), z.string())
128
+ });
185
129
  function parseResponse(responseText) {
186
- const cleaned = responseText.replace(/```json/g, "").replace(/```/g, "").trim();
187
- const parsed = JSON.parse(cleaned);
130
+ const fenced = responseText.trim().match(/^```(?:json)?\s*([\s\S]*?)\s*```$/);
131
+ const cleaned = fenced ? fenced[1] : responseText.trim();
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(cleaned);
135
+ } catch (error) {
136
+ throw new Error(`Invalid LLM response: not valid JSON (${error.message})`, { cause: error });
137
+ }
138
+ const result2 = cacheItemSchema.safeParse(parsed);
139
+ if (!result2.success) {
140
+ throw new Error(`Invalid LLM response: ${result2.error.message}`, { cause: result2.error });
141
+ }
188
142
  return parsed;
189
143
  }
190
- async function askLLM(prompt, prePrompt2, postPrompt2) {
144
+ async function askLLM(prompt, prePrompt, postPrompt) {
191
145
  const config = await awaitedConfig;
192
146
  const provider = config.llm.provider;
193
147
  const engineName = providerEngineMap[provider];
194
148
  if (!engineName) {
195
149
  throw new Error(`Unknown LLM provider: ${provider}`);
196
150
  }
197
- if (!config.llm.apiKey) {
151
+ if (!config.llm.apiKey && !keylessProviders.includes(provider)) {
198
152
  throw new Error(`API key for ${provider} is not configured in llm.apiKey`);
199
153
  }
200
154
  if (config.llm.sleep && config.llm.sleep > 0) {
@@ -204,13 +158,13 @@ async function askLLM(prompt, prePrompt2, postPrompt2) {
204
158
  apiKey: config.llm.apiKey,
205
159
  ...config.llm.baseURL ? { baseURL: config.llm.baseURL } : {}
206
160
  });
207
- const fullPrompt = `${prePrompt2}
161
+ const fullPrompt = `${prePrompt}
208
162
 
209
163
 
210
164
  ${prompt}
211
165
 
212
166
 
213
- ${postPrompt2}`.trim();
167
+ ${postPrompt}`.trim();
214
168
  const messages = [new Message("user", fullPrompt)];
215
169
  const model = config.llm.model ?? "gpt-4o-mini";
216
170
  const response = await engine.complete(engine.buildModel(model), messages, {
package/dist/logger.d.mts CHANGED
@@ -1,9 +1,27 @@
1
1
  declare const logger: {
2
+ /**
3
+ * Logs the params at debug level.
4
+ */
2
5
  debug(...params: unknown[]): void;
6
+ /**
7
+ * Logs the params at trace level.
8
+ */
3
9
  trace(...params: unknown[]): void;
10
+ /**
11
+ * Logs the params at info level.
12
+ */
4
13
  info(...params: unknown[]): void;
14
+ /**
15
+ * Logs the params at warn level.
16
+ */
5
17
  warn(...params: unknown[]): void;
18
+ /**
19
+ * Logs the params at error level.
20
+ */
6
21
  error(...params: unknown[]): void;
22
+ /**
23
+ * Logs the params at verbose level.
24
+ */
7
25
  verbose(...params: unknown[]): void;
8
26
  };
9
27
 
package/dist/logger.mjs CHANGED
@@ -19,21 +19,39 @@ function log(level, ...params) {
19
19
  console.log(`${!isProd ? Colors[level] : ""}${String(level).toUpperCase()} `, ...params, `${!isProd ? Clear : ""}`);
20
20
  }
21
21
  var logger = {
22
+ /**
23
+ * Logs the params at debug level.
24
+ */
22
25
  debug(...params) {
23
26
  return log("debug", ...params);
24
27
  },
28
+ /**
29
+ * Logs the params at trace level.
30
+ */
25
31
  trace(...params) {
26
32
  return log("trace", ...params);
27
33
  },
34
+ /**
35
+ * Logs the params at info level.
36
+ */
28
37
  info(...params) {
29
38
  return log("info", ...params);
30
39
  },
40
+ /**
41
+ * Logs the params at warn level.
42
+ */
31
43
  warn(...params) {
32
44
  return log("warn", ...params);
33
45
  },
46
+ /**
47
+ * Logs the params at error level.
48
+ */
34
49
  error(...params) {
35
50
  return log("error", ...params);
36
51
  },
52
+ /**
53
+ * Logs the params at verbose level.
54
+ */
37
55
  verbose(...params) {
38
56
  return log("verbose", ...params);
39
57
  }
@@ -2,6 +2,10 @@ import { z } from 'zod';
2
2
 
3
3
  declare const languageSchema: z.ZodString;
4
4
  declare const llmProviderSchema: z.ZodEnum<["anthropic", "gemini", "openai", "groq", "mistral", "ollama", "xai", "deepseek", "openrouter", "cerebras", "lmstudio", "azure", "meta"]>;
5
+ /**
6
+ * Providers that run without an API key.
7
+ */
8
+ declare const keylessProviders: ReadonlyArray<string>;
5
9
  declare const configSchema: z.ZodEffects<z.ZodObject<{
6
10
  debug: z.ZodOptional<z.ZodBoolean>;
7
11
  log: z.ZodObject<{
@@ -189,4 +193,4 @@ declare const configSchema: z.ZodEffects<z.ZodObject<{
189
193
  globby?: string | undefined;
190
194
  }>;
191
195
 
192
- export { configSchema, languageSchema, llmProviderSchema };
196
+ export { configSchema, keylessProviders, languageSchema, llmProviderSchema };
@@ -17,6 +17,7 @@ var llmProviderSchema = z.enum([
17
17
  "azure",
18
18
  "meta"
19
19
  ]);
20
+ var keylessProviders = ["ollama", "lmstudio"];
20
21
  var configSchema = z.object({
21
22
  debug: z.boolean().optional(),
22
23
  log: z.object({
@@ -48,8 +49,7 @@ var configSchema = z.object({
48
49
  globby: z.string().regex(/^\.\/.*/).optional()
49
50
  }).refine(
50
51
  (data) => {
51
- const noApiKeyRequired = ["ollama", "lmstudio"];
52
- if (noApiKeyRequired.includes(data.llm.provider)) {
52
+ if (keylessProviders.includes(data.llm.provider)) {
53
53
  return true;
54
54
  }
55
55
  return !!data.llm.apiKey;
@@ -60,4 +60,4 @@ var configSchema = z.object({
60
60
  }
61
61
  );
62
62
 
63
- export { configSchema, languageSchema, llmProviderSchema };
63
+ export { configSchema, keylessProviders, languageSchema, llmProviderSchema };
@@ -7,15 +7,7 @@
7
7
  * - template literals (`` `...` ``), including nested `${ ... }`
8
8
  * interpolations which may themselves contain strings or further templates
9
9
  *
10
- * Newlines are preserved so that downstream consumers that care about
11
- * line/column information (e.g. source-map-adjacent tooling) keep stable
12
- * offsets. Block-comment bodies are replaced with the newlines they
13
- * contained; line-comment bodies are removed up to (but not including) the
14
- * terminating newline.
15
- *
16
- * Implementation is an in-house state-machine scanner — preferred over a
17
- * dependency because the algorithm is small, deterministic, and avoids
18
- * surprises around nested template interpolations.
10
+ * Every newline is kept, so line numbers stay stable.
19
11
  *
20
12
  * @param code - JS/TS source to scrub
21
13
  * @returns the same source with comments removed and strings preserved
@@ -80,7 +80,6 @@ function stripComments(code) {
80
80
  top.braceDepth--;
81
81
  out += ch;
82
82
  i++;
83
- if (top.braceDepth === 0) ;
84
83
  continue;
85
84
  }
86
85
  }
@@ -2,5 +2,26 @@ import { z } from 'zod';
2
2
  import { llmProviderSchema } from '../schemas/config.shcema.mjs';
3
3
 
4
4
  type LLMProvider = z.infer<typeof llmProviderSchema>;
5
+ type GringowTranslation = {
6
+ [key: string]: string;
7
+ };
8
+ type GringowCacheItem = {
9
+ original: string;
10
+ translation: GringowTranslation;
11
+ append?: string;
12
+ prepend?: string;
13
+ rebuild?: boolean;
14
+ };
15
+ type GringowCache = {
16
+ [key: string]: GringowCacheItem;
17
+ };
18
+ type GringowResponse = {
19
+ cacheId: string;
20
+ translation: GringowCacheItem;
21
+ };
22
+ type GringowError = Error & {
23
+ code?: string;
24
+ cause?: Error;
25
+ };
5
26
 
6
- export type { LLMProvider };
27
+ export type { GringowCache, GringowCacheItem, GringowError, GringowResponse, GringowTranslation, LLMProvider };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gringow/gringow",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "main": "./dist/index.mjs",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",
@@ -55,21 +55,18 @@
55
55
  "multilingual"
56
56
  ],
57
57
  "devDependencies": {
58
- "@types/crypto-js": "^4.2.2",
59
58
  "@types/node": "^22.16.4",
60
- "dotenv": "16.4.7",
59
+ "@vitest/coverage-v8": "2.1.9",
61
60
  "mkdirp": "3.0.1",
62
- "nanoid": "^5.1.5",
63
61
  "tsc-alias": "^1.8.16",
64
62
  "tsup": "^8.5.0",
65
63
  "typescript": "5.7.3",
66
64
  "vitest": "^2.1.8"
67
65
  },
68
66
  "dependencies": {
69
- "@types/lodash.merge": "^4.6.9",
70
- "@types/md5": "^2.3.6",
67
+ "@types/lodash.mergewith": "^4.6.9",
71
68
  "cosmiconfig": "^9.0.0",
72
- "lodash.merge": "^4.6.2",
69
+ "lodash.mergewith": "^4.6.2",
73
70
  "multi-llm-ts": "^5.1.1",
74
71
  "zod": "^3.25.76"
75
72
  },
@@ -78,6 +75,8 @@
78
75
  "watch": "pnpm run build && tsup --watch --config ../tsup.config.ts",
79
76
  "__postinstall": "pnpm run build",
80
77
  "test": "vitest run",
81
- "test:watch": "vitest --watch"
78
+ "test:watch": "vitest --watch",
79
+ "test:coverage": "vitest run --coverage",
80
+ "bench": "vitest bench --run"
82
81
  }
83
82
  }