utilitas 2000.3.48 → 2000.3.52

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/lib/alan.mjs CHANGED
@@ -1220,7 +1220,7 @@ const initChat = async (options = {}) => {
1220
1220
  'Invalid session storage provider.'
1221
1221
  );
1222
1222
  chatConfig.sessions = options.sessions;
1223
- }
1223
+ } else { log(`WARNING: Sessions persistence is not enabled.`); }
1224
1224
  options.instructions && (chatConfig.systemPrompt = options.instructions);
1225
1225
  // Use Gemini instead of ChatGPT because of the longer package.
1226
1226
  const [spTokens, ais] = await Promise.all([countTokens([await buildMessage(
@@ -1446,6 +1446,7 @@ export {
1446
1446
  prompt,
1447
1447
  promptOpenRouter,
1448
1448
  resetSession,
1449
+ setSession,
1449
1450
  stt,
1450
1451
  talk,
1451
1452
  trimPrompt,
package/lib/manifest.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  const manifest = {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "2000.3.48",
4
+ "version": "2000.3.52",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
package/lib/rag.mjs CHANGED
@@ -1,5 +1,5 @@
1
+ import { BASE64, convert } from './storage.mjs';
1
2
  import { countTokens, trimText } from './alan.mjs';
2
- import { convert } from './storage.mjs';
3
3
  import { ensureArray, ensureString, need } from './utilitas.mjs';
4
4
 
5
5
  const _NEED = ['openai', '@google-cloud/discoveryengine'];
@@ -14,6 +14,7 @@ const [
14
14
  GOOGLE_MODEL_GEMINI_EMBED,
15
15
  JINA_MODEL_V_4,
16
16
  GOOGLE_MODEL_SEMANTIC_RANKER,
17
+ JINA_MODEL_RERANKER_M0,
17
18
  ] = [
18
19
  'OPENAI', 'GOOGLE', 'OPENROUTER', 'JINA',
19
20
  'global', 'default_ranking_config',
@@ -22,6 +23,7 @@ const [
22
23
  'gemini-embedding-001', // dim: 768(default), 1536, or 3072(google default)
23
24
  'jina-embeddings-v4', // dim: 256‑2048
24
25
  'semantic-ranker-default@latest',
26
+ 'jina-reranker-m0',
25
27
  ];
26
28
 
27
29
  const PROVIDER_BASE_URL = {
@@ -37,6 +39,7 @@ const DEFAULT_EMBEDDING_MODELS = {
37
39
 
38
40
  const DEFAULT_RERANKER_MODELS = {
39
41
  [GOOGLE]: GOOGLE_MODEL_SEMANTIC_RANKER,
42
+ [JINA]: JINA_MODEL_RERANKER_M0,
40
43
  };
41
44
 
42
45
  const MODEL_CONFIG = {
@@ -64,6 +67,11 @@ const MODEL_CONFIG = {
64
67
  },
65
68
  [GOOGLE_MODEL_SEMANTIC_RANKER]: {
66
69
  source: 'google', image: false, maxTokens: 1024, recordsLimit: 200,
70
+ options: { ignoreRecordDetailsInResponse: true },
71
+ },
72
+ [JINA_MODEL_RERANKER_M0]: {
73
+ source: 'jina', image: true, maxTokens: 1024, recordsLimit: 200,
74
+ options: { return_documents: false },
67
75
  },
68
76
  };
69
77
 
@@ -147,7 +155,7 @@ const embed = async (input, options = {}) => {
147
155
  );
148
156
  if (options?.input) {
149
157
  x.image = await convert(
150
- x.image, { ...options, expected: 'base64' }
158
+ x.image, { ...options, expected: BASE64 }
151
159
  );
152
160
  }
153
161
  }
@@ -181,6 +189,7 @@ const embed = async (input, options = {}) => {
181
189
 
182
190
  const initReranker = async (options = {}) => {
183
191
  const provider = ensureRerankerProvider(options);
192
+ const model = options?.model || DEFAULT_RERANKER_MODELS[provider];
184
193
  switch (provider) {
185
194
  case GOOGLE:
186
195
  ensureGoogleCredentials(options);
@@ -195,13 +204,20 @@ const initReranker = async (options = {}) => {
195
204
  };
196
205
  const client = new RankServiceClient(clientOptions);
197
206
  rerankerClients[provider] = {
198
- model: options?.model || DEFAULT_RERANKER_MODELS[provider],
199
- client, rankingConfigPath: client.rankingConfigPath(
207
+ client, model, rankingConfigPath: client.rankingConfigPath(
200
208
  options.projectId, location,
201
209
  options?.rerankerConfigId || GOOGLE_RERANK_CONFIG_ID
202
210
  ),
203
211
  };
204
212
  break;
213
+ case JINA:
214
+ const OpenAI = await need('openai');
215
+ const baseURL = options?.baseURL || PROVIDER_BASE_URL[provider];
216
+ rerankerClients[provider] = {
217
+ client: new OpenAI({ ...options, baseURL }),
218
+ model, source: MODEL_CONFIG[model]?.source,
219
+ };
220
+ break;
205
221
  default:
206
222
  throw new Error(`Unsupported reranker provider: ${provider}`);
207
223
  }
@@ -226,20 +242,43 @@ const rerank = async (query, records, options = {}) => {
226
242
  records[i].content = availableTokens > 0 ? await trimText(
227
243
  records[i].content, availableTokens
228
244
  ) : '';
245
+ records[i].image = records[i].image ? await convert(records[i].image, {
246
+ ...options, expected: BASE64,
247
+ }) : undefined;
229
248
  }
230
249
  switch (provider) {
231
250
  case GOOGLE:
232
- const request = {
251
+ var body = {
233
252
  model, query, rankingConfig: rankingConfigPath,
234
253
  records, topN: ~~options?.topN || records.length,
254
+ ...MODEL_CONFIG[model]?.options || {},
255
+ ...options?.requestOptions || {},
256
+ };
257
+ result = (await client.rank(body))?.[0]?.records;
258
+ options?.raw || (result = result.map(x => ({
259
+ index: ~~x.id, score: x.score,
260
+ })));
261
+ break;
262
+ case JINA:
263
+ records = records.map(x =>
264
+ ((x.title || x.content) ? { text: [x.title, x.content].filter(x => x).join('\n') } : null)
265
+ || (x.image ? { image: x.image } : null)
266
+ ).filter(x => x);
267
+ assert(records.length, 'No valid records found.', 400);
268
+ var body = {
269
+ model, query, documents: records,
270
+ ...MODEL_CONFIG[model]?.options || {},
235
271
  ...options?.requestOptions || {},
236
272
  };
237
- result = (await client.rank(request))?.[0]?.records;
273
+ result = (await client.post('/rerank', { body }))?.results;
274
+ options?.raw || (result = result.map(x => ({
275
+ index: ~~(x.index), score: x.relevance_score,
276
+ })));
238
277
  break;
239
278
  default:
240
279
  throw new Error(`Unsupported reranker provider: ${provider}`);
241
280
  }
242
- // print(result);
281
+ result.sort((a, b) => b.score - a.score);
243
282
  return result || [];
244
283
  };
245
284
 
package/lib/storage.mjs CHANGED
@@ -119,18 +119,20 @@ const assertPath = async (path, type, mode, msg, code, options) => {
119
119
  };
120
120
 
121
121
  const isTextFile = async (file, options) => {
122
- const isBuf = Buffer.isBuffer(file);
123
- let [f, bytes, res] = [isBuf ? file : await fs.open(file, 'r'), null, true];
124
- for (let i = 0; i < (~~options?.length || 1000); i++) {
125
- let buf = Buffer.alloc(1);
126
- if (isBuf) { buf[0] = f[i]; bytes = i < f.length ? 1 : 0; }
127
- else { bytes = readSync(f.fd, buf, 0, 1, i); }
128
- if (bytes === 0) { break; } else if (
129
- bytes === 1 && buf.toString().charCodeAt() === 0
130
- ) { res = false; break; }
122
+ const maxBytes = options?.length ?? 1000;
123
+ let buf;
124
+ if (Buffer.isBuffer(file)) {
125
+ buf = file.subarray(0, maxBytes);
126
+ } else {
127
+ const fd = await fs.promises.open(file, 'r');
128
+ buf = Buffer.alloc(maxBytes);
129
+ await fd.read(buf, 0, maxBytes, 0);
130
+ await fd.close();
131
+ }
132
+ for (let i = 0; i < buf.length; i++) {
133
+ if (buf[i] === 0) return false;
131
134
  }
132
- isBuf || f?.close()
133
- return res;
135
+ return true;
134
136
  };
135
137
 
136
138
  const getConfigFilename = async (options) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "2000.3.48",
4
+ "version": "2000.3.52",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",