@karmaniverous/jeeves-watcher 0.9.2 → 0.9.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.
- package/dist/cli/jeeves-watcher/index.js +52 -12
- package/dist/index.js +52 -12
- package/package.json +1 -3
|
@@ -25,7 +25,7 @@ import { JSONPath } from 'jsonpath-plus';
|
|
|
25
25
|
import { createHash } from 'node:crypto';
|
|
26
26
|
import crypto from 'crypto';
|
|
27
27
|
import { cosmiconfig } from 'cosmiconfig';
|
|
28
|
-
import
|
|
28
|
+
import https from 'node:https';
|
|
29
29
|
import pino from 'pino';
|
|
30
30
|
import { v5 } from 'uuid';
|
|
31
31
|
import * as cheerio from 'cheerio';
|
|
@@ -3897,10 +3897,43 @@ function getLogger(logger) {
|
|
|
3897
3897
|
|
|
3898
3898
|
/**
|
|
3899
3899
|
* @module embedding/geminiProvider
|
|
3900
|
-
* Gemini embedding provider using Google Generative AI.
|
|
3901
|
-
|
|
3900
|
+
* Gemini embedding provider using the Google Generative AI REST API directly.
|
|
3901
|
+
* Uses node:https with a keep-alive agent for reliable performance in
|
|
3902
|
+
* long-running processes (avoids undici/fetch event-loop contention).
|
|
3903
|
+
*/
|
|
3904
|
+
const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta';
|
|
3905
|
+
/** Persistent HTTPS agent for connection reuse. */
|
|
3906
|
+
const agent = new https.Agent({ keepAlive: true });
|
|
3907
|
+
/** Make an HTTPS POST request using node:https (bypasses undici/fetch). */
|
|
3908
|
+
function httpsPost(url, body) {
|
|
3909
|
+
return new Promise((resolve, reject) => {
|
|
3910
|
+
const parsed = new URL(url);
|
|
3911
|
+
const req = https.request({
|
|
3912
|
+
hostname: parsed.hostname,
|
|
3913
|
+
path: parsed.pathname + parsed.search,
|
|
3914
|
+
method: 'POST',
|
|
3915
|
+
agent,
|
|
3916
|
+
headers: {
|
|
3917
|
+
'Content-Type': 'application/json',
|
|
3918
|
+
'Content-Length': Buffer.byteLength(body),
|
|
3919
|
+
},
|
|
3920
|
+
}, (res) => {
|
|
3921
|
+
const chunks = [];
|
|
3922
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
3923
|
+
res.on('end', () => {
|
|
3924
|
+
resolve({
|
|
3925
|
+
status: res.statusCode ?? 0,
|
|
3926
|
+
body: Buffer.concat(chunks).toString('utf8'),
|
|
3927
|
+
});
|
|
3928
|
+
});
|
|
3929
|
+
});
|
|
3930
|
+
req.on('error', reject);
|
|
3931
|
+
req.write(body);
|
|
3932
|
+
req.end();
|
|
3933
|
+
});
|
|
3934
|
+
}
|
|
3902
3935
|
/**
|
|
3903
|
-
* Create a Gemini embedding provider using the Google Generative AI
|
|
3936
|
+
* Create a Gemini embedding provider using the Google Generative AI REST API.
|
|
3904
3937
|
*
|
|
3905
3938
|
* @param config - The embedding configuration.
|
|
3906
3939
|
* @param logger - Optional pino logger for retry warnings.
|
|
@@ -3912,20 +3945,27 @@ function createGeminiProvider(config, logger) {
|
|
|
3912
3945
|
throw new Error('Gemini embedding provider requires config.embedding.apiKey');
|
|
3913
3946
|
}
|
|
3914
3947
|
const dimensions = config.dimensions ?? 3072;
|
|
3948
|
+
const model = config.model;
|
|
3949
|
+
const apiKey = config.apiKey;
|
|
3915
3950
|
const log = getLogger(logger);
|
|
3916
|
-
const
|
|
3917
|
-
apiKey: config.apiKey,
|
|
3918
|
-
model: config.model,
|
|
3919
|
-
});
|
|
3951
|
+
const url = `${GEMINI_API_BASE}/models/${model}:batchEmbedContents?key=${apiKey}`;
|
|
3920
3952
|
return {
|
|
3921
3953
|
dimensions,
|
|
3922
3954
|
async embed(texts) {
|
|
3923
3955
|
const vectors = await retry(async (attempt) => {
|
|
3924
3956
|
if (attempt > 1) {
|
|
3925
|
-
log.warn({ attempt, provider: 'gemini', model
|
|
3957
|
+
log.warn({ attempt, provider: 'gemini', model }, 'Retrying embedding request');
|
|
3958
|
+
}
|
|
3959
|
+
const requests = texts.map((text) => ({
|
|
3960
|
+
model: `models/${model}`,
|
|
3961
|
+
content: { parts: [{ text }] },
|
|
3962
|
+
}));
|
|
3963
|
+
const response = await httpsPost(url, JSON.stringify({ requests }));
|
|
3964
|
+
if (response.status < 200 || response.status >= 300) {
|
|
3965
|
+
throw new Error(`Gemini API error ${String(response.status)}: ${response.body}`);
|
|
3926
3966
|
}
|
|
3927
|
-
|
|
3928
|
-
return
|
|
3967
|
+
const data = JSON.parse(response.body);
|
|
3968
|
+
return data.embeddings.map((e) => e.values);
|
|
3929
3969
|
}, {
|
|
3930
3970
|
attempts: 5,
|
|
3931
3971
|
baseDelayMs: 500,
|
|
@@ -3936,7 +3976,7 @@ function createGeminiProvider(config, logger) {
|
|
|
3936
3976
|
attempt,
|
|
3937
3977
|
delayMs,
|
|
3938
3978
|
provider: 'gemini',
|
|
3939
|
-
model
|
|
3979
|
+
model,
|
|
3940
3980
|
err: normalizeError(error),
|
|
3941
3981
|
}, 'Embedding call failed; will retry');
|
|
3942
3982
|
},
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { createHash } from 'node:crypto';
|
|
|
22
22
|
import crypto from 'crypto';
|
|
23
23
|
import chokidar from 'chokidar';
|
|
24
24
|
import { cosmiconfig } from 'cosmiconfig';
|
|
25
|
-
import
|
|
25
|
+
import https from 'node:https';
|
|
26
26
|
import pino from 'pino';
|
|
27
27
|
import { v5 } from 'uuid';
|
|
28
28
|
import * as cheerio from 'cheerio';
|
|
@@ -3873,10 +3873,43 @@ function getLogger(logger) {
|
|
|
3873
3873
|
|
|
3874
3874
|
/**
|
|
3875
3875
|
* @module embedding/geminiProvider
|
|
3876
|
-
* Gemini embedding provider using Google Generative AI.
|
|
3877
|
-
|
|
3876
|
+
* Gemini embedding provider using the Google Generative AI REST API directly.
|
|
3877
|
+
* Uses node:https with a keep-alive agent for reliable performance in
|
|
3878
|
+
* long-running processes (avoids undici/fetch event-loop contention).
|
|
3879
|
+
*/
|
|
3880
|
+
const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta';
|
|
3881
|
+
/** Persistent HTTPS agent for connection reuse. */
|
|
3882
|
+
const agent = new https.Agent({ keepAlive: true });
|
|
3883
|
+
/** Make an HTTPS POST request using node:https (bypasses undici/fetch). */
|
|
3884
|
+
function httpsPost(url, body) {
|
|
3885
|
+
return new Promise((resolve, reject) => {
|
|
3886
|
+
const parsed = new URL(url);
|
|
3887
|
+
const req = https.request({
|
|
3888
|
+
hostname: parsed.hostname,
|
|
3889
|
+
path: parsed.pathname + parsed.search,
|
|
3890
|
+
method: 'POST',
|
|
3891
|
+
agent,
|
|
3892
|
+
headers: {
|
|
3893
|
+
'Content-Type': 'application/json',
|
|
3894
|
+
'Content-Length': Buffer.byteLength(body),
|
|
3895
|
+
},
|
|
3896
|
+
}, (res) => {
|
|
3897
|
+
const chunks = [];
|
|
3898
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
3899
|
+
res.on('end', () => {
|
|
3900
|
+
resolve({
|
|
3901
|
+
status: res.statusCode ?? 0,
|
|
3902
|
+
body: Buffer.concat(chunks).toString('utf8'),
|
|
3903
|
+
});
|
|
3904
|
+
});
|
|
3905
|
+
});
|
|
3906
|
+
req.on('error', reject);
|
|
3907
|
+
req.write(body);
|
|
3908
|
+
req.end();
|
|
3909
|
+
});
|
|
3910
|
+
}
|
|
3878
3911
|
/**
|
|
3879
|
-
* Create a Gemini embedding provider using the Google Generative AI
|
|
3912
|
+
* Create a Gemini embedding provider using the Google Generative AI REST API.
|
|
3880
3913
|
*
|
|
3881
3914
|
* @param config - The embedding configuration.
|
|
3882
3915
|
* @param logger - Optional pino logger for retry warnings.
|
|
@@ -3888,20 +3921,27 @@ function createGeminiProvider(config, logger) {
|
|
|
3888
3921
|
throw new Error('Gemini embedding provider requires config.embedding.apiKey');
|
|
3889
3922
|
}
|
|
3890
3923
|
const dimensions = config.dimensions ?? 3072;
|
|
3924
|
+
const model = config.model;
|
|
3925
|
+
const apiKey = config.apiKey;
|
|
3891
3926
|
const log = getLogger(logger);
|
|
3892
|
-
const
|
|
3893
|
-
apiKey: config.apiKey,
|
|
3894
|
-
model: config.model,
|
|
3895
|
-
});
|
|
3927
|
+
const url = `${GEMINI_API_BASE}/models/${model}:batchEmbedContents?key=${apiKey}`;
|
|
3896
3928
|
return {
|
|
3897
3929
|
dimensions,
|
|
3898
3930
|
async embed(texts) {
|
|
3899
3931
|
const vectors = await retry(async (attempt) => {
|
|
3900
3932
|
if (attempt > 1) {
|
|
3901
|
-
log.warn({ attempt, provider: 'gemini', model
|
|
3933
|
+
log.warn({ attempt, provider: 'gemini', model }, 'Retrying embedding request');
|
|
3934
|
+
}
|
|
3935
|
+
const requests = texts.map((text) => ({
|
|
3936
|
+
model: `models/${model}`,
|
|
3937
|
+
content: { parts: [{ text }] },
|
|
3938
|
+
}));
|
|
3939
|
+
const response = await httpsPost(url, JSON.stringify({ requests }));
|
|
3940
|
+
if (response.status < 200 || response.status >= 300) {
|
|
3941
|
+
throw new Error(`Gemini API error ${String(response.status)}: ${response.body}`);
|
|
3902
3942
|
}
|
|
3903
|
-
|
|
3904
|
-
return
|
|
3943
|
+
const data = JSON.parse(response.body);
|
|
3944
|
+
return data.embeddings.map((e) => e.values);
|
|
3905
3945
|
}, {
|
|
3906
3946
|
attempts: 5,
|
|
3907
3947
|
baseDelayMs: 500,
|
|
@@ -3912,7 +3952,7 @@ function createGeminiProvider(config, logger) {
|
|
|
3912
3952
|
attempt,
|
|
3913
3953
|
delayMs,
|
|
3914
3954
|
provider: 'gemini',
|
|
3915
|
-
model
|
|
3955
|
+
model,
|
|
3916
3956
|
err: normalizeError(error),
|
|
3917
3957
|
}, 'Embedding call failed; will retry');
|
|
3918
3958
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karmaniverous/jeeves-watcher",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"author": "Jason Williscroft",
|
|
5
5
|
"description": "Filesystem watcher that keeps a Qdrant vector store in sync with document changes",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -52,8 +52,6 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@commander-js/extra-typings": "^14.0.0",
|
|
54
54
|
"@karmaniverous/jsonmap": "^2.1.1",
|
|
55
|
-
"@langchain/google-genai": "*",
|
|
56
|
-
"@langchain/qdrant": "*",
|
|
57
55
|
"@langchain/textsplitters": "*",
|
|
58
56
|
"@qdrant/js-client-rest": "*",
|
|
59
57
|
"ajv": "^8.18.0",
|