@karmaniverous/jeeves-watcher 0.6.4 → 0.6.6
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 +80 -9
- package/dist/index.d.ts +16 -0
- package/dist/index.js +80 -9
- package/package.json +1 -1
|
@@ -456,7 +456,7 @@ z.object({
|
|
|
456
456
|
* @returns The configured AJV instance.
|
|
457
457
|
*/
|
|
458
458
|
function createRuleAjv() {
|
|
459
|
-
const ajv = new Ajv({ allErrors: true });
|
|
459
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
460
460
|
addFormats(ajv);
|
|
461
461
|
ajv.addKeyword({
|
|
462
462
|
keyword: 'glob',
|
|
@@ -2786,6 +2786,53 @@ function createReindexHandler(deps) {
|
|
|
2786
2786
|
}, deps.logger, 'Reindex');
|
|
2787
2787
|
}
|
|
2788
2788
|
|
|
2789
|
+
/**
|
|
2790
|
+
* @module api/handlers/rulesReapply
|
|
2791
|
+
* Fastify route handler for POST /rules/reapply.
|
|
2792
|
+
* Re-applies current inference rules to already-indexed files matching given globs.
|
|
2793
|
+
*/
|
|
2794
|
+
/**
|
|
2795
|
+
* Create handler for POST /rules/reapply.
|
|
2796
|
+
*
|
|
2797
|
+
* Scrolls through all indexed points, finds files matching the given globs,
|
|
2798
|
+
* and re-applies current inference rules without re-embedding.
|
|
2799
|
+
*/
|
|
2800
|
+
function createRulesReapplyHandler(deps) {
|
|
2801
|
+
return wrapHandler(async (request) => {
|
|
2802
|
+
await Promise.resolve();
|
|
2803
|
+
const { globs } = request.body;
|
|
2804
|
+
if (!Array.isArray(globs) || globs.length === 0) {
|
|
2805
|
+
throw new Error('Missing required field: globs (non-empty string array)');
|
|
2806
|
+
}
|
|
2807
|
+
const normalizedGlobs = globs.map((g) => normalizeSlashes(g));
|
|
2808
|
+
const isMatch = picomatch(normalizedGlobs, { dot: true, nocase: true });
|
|
2809
|
+
// Collect unique file paths matching the globs
|
|
2810
|
+
const matchingFiles = new Set();
|
|
2811
|
+
for await (const point of deps.vectorStore.scroll()) {
|
|
2812
|
+
const filePath = point.payload['file_path'];
|
|
2813
|
+
if (typeof filePath === 'string' && isMatch(filePath)) {
|
|
2814
|
+
matchingFiles.add(filePath);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
deps.logger.info({ globs: normalizedGlobs, matchCount: matchingFiles.size }, 'Re-applying rules to matching files');
|
|
2818
|
+
let updated = 0;
|
|
2819
|
+
for (const filePath of matchingFiles) {
|
|
2820
|
+
try {
|
|
2821
|
+
const result = await deps.processor.processRulesUpdate(filePath);
|
|
2822
|
+
if (result !== null)
|
|
2823
|
+
updated++;
|
|
2824
|
+
}
|
|
2825
|
+
catch (error) {
|
|
2826
|
+
deps.logger.warn({ filePath, err: error }, 'Failed to re-apply rules to file');
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
return {
|
|
2830
|
+
matched: matchingFiles.size,
|
|
2831
|
+
updated,
|
|
2832
|
+
};
|
|
2833
|
+
}, deps.logger, 'RulesReapply');
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2789
2836
|
/**
|
|
2790
2837
|
* @module api/handlers/rulesRegister
|
|
2791
2838
|
* Fastify route handler for POST /rules/register.
|
|
@@ -3028,6 +3075,7 @@ function createApiServer(options) {
|
|
|
3028
3075
|
onRulesChanged,
|
|
3029
3076
|
}));
|
|
3030
3077
|
app.post('/points/delete', createPointsDeleteHandler({ vectorStore, logger }));
|
|
3078
|
+
app.post('/rules/reapply', createRulesReapplyHandler({ processor, vectorStore, logger }));
|
|
3031
3079
|
}
|
|
3032
3080
|
return app;
|
|
3033
3081
|
}
|
|
@@ -3949,7 +3997,10 @@ class DocumentProcessor {
|
|
|
3949
3997
|
if (customMapLib !== undefined) {
|
|
3950
3998
|
this.config = { ...this.config, customMapLib };
|
|
3951
3999
|
}
|
|
3952
|
-
this.logger.info({
|
|
4000
|
+
this.logger.info({
|
|
4001
|
+
rules: compiledRules.length,
|
|
4002
|
+
ruleNames: compiledRules.map((r) => r.rule.name),
|
|
4003
|
+
}, 'Inference rules updated');
|
|
3953
4004
|
}
|
|
3954
4005
|
}
|
|
3955
4006
|
|
|
@@ -4330,6 +4381,7 @@ async function* scrollCollection(client, collectionName, filter, limit = 100) {
|
|
|
4330
4381
|
*/
|
|
4331
4382
|
class VectorStoreClient {
|
|
4332
4383
|
client;
|
|
4384
|
+
clientConfig;
|
|
4333
4385
|
collectionName;
|
|
4334
4386
|
dims;
|
|
4335
4387
|
log;
|
|
@@ -4342,16 +4394,27 @@ class VectorStoreClient {
|
|
|
4342
4394
|
* @param logger - Optional pino logger for retry warnings.
|
|
4343
4395
|
*/
|
|
4344
4396
|
constructor(config, dimensions, logger) {
|
|
4345
|
-
this.
|
|
4346
|
-
|
|
4347
|
-
apiKey: config.apiKey,
|
|
4348
|
-
checkCompatibility: false,
|
|
4349
|
-
});
|
|
4397
|
+
this.clientConfig = { url: config.url, apiKey: config.apiKey };
|
|
4398
|
+
this.client = this.createClient();
|
|
4350
4399
|
this.collectionName = config.collectionName;
|
|
4351
4400
|
this.dims = dimensions;
|
|
4352
4401
|
this.log = getLogger(logger);
|
|
4353
4402
|
this.pinoLogger = logger;
|
|
4354
4403
|
}
|
|
4404
|
+
/**
|
|
4405
|
+
* Create a fresh QdrantClient instance.
|
|
4406
|
+
*
|
|
4407
|
+
* Used to avoid stale HTTP keep-alive connections. The Qdrant JS client's
|
|
4408
|
+
* internal undici Agent uses keepAliveTimeout: 10s, which causes ECONNRESET
|
|
4409
|
+
* when connections sit idle during slow embedding calls (Gemini p99 ~8s).
|
|
4410
|
+
* Creating a fresh client for write operations ensures clean TCP connections.
|
|
4411
|
+
*/
|
|
4412
|
+
createClient() {
|
|
4413
|
+
return new QdrantClient({
|
|
4414
|
+
...this.clientConfig,
|
|
4415
|
+
checkCompatibility: false,
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4355
4418
|
/**
|
|
4356
4419
|
* Ensure the collection exists with correct dimensions and Cosine distance.
|
|
4357
4420
|
*/
|
|
@@ -4399,13 +4462,18 @@ class VectorStoreClient {
|
|
|
4399
4462
|
/**
|
|
4400
4463
|
* Upsert points into the collection.
|
|
4401
4464
|
*
|
|
4465
|
+
* Uses a fresh QdrantClient per attempt to avoid stale keep-alive connections.
|
|
4466
|
+
* Between embedding calls and upserts, idle connections may be closed by the
|
|
4467
|
+
* server, causing ECONNRESET on reuse.
|
|
4468
|
+
*
|
|
4402
4469
|
* @param points - The points to upsert.
|
|
4403
4470
|
*/
|
|
4404
4471
|
async upsert(points) {
|
|
4405
4472
|
if (points.length === 0)
|
|
4406
4473
|
return;
|
|
4407
4474
|
await this.retryOperation('upsert', async () => {
|
|
4408
|
-
|
|
4475
|
+
const freshClient = this.createClient();
|
|
4476
|
+
await freshClient.upsert(this.collectionName, {
|
|
4409
4477
|
wait: true,
|
|
4410
4478
|
points: points.map((p) => ({
|
|
4411
4479
|
id: p.id,
|
|
@@ -4418,13 +4486,16 @@ class VectorStoreClient {
|
|
|
4418
4486
|
/**
|
|
4419
4487
|
* Delete points by their IDs.
|
|
4420
4488
|
*
|
|
4489
|
+
* Uses a fresh QdrantClient per attempt to avoid stale keep-alive connections.
|
|
4490
|
+
*
|
|
4421
4491
|
* @param ids - The point IDs to delete.
|
|
4422
4492
|
*/
|
|
4423
4493
|
async delete(ids) {
|
|
4424
4494
|
if (ids.length === 0)
|
|
4425
4495
|
return;
|
|
4426
4496
|
await this.retryOperation('delete', async () => {
|
|
4427
|
-
|
|
4497
|
+
const freshClient = this.createClient();
|
|
4498
|
+
await freshClient.delete(this.collectionName, {
|
|
4428
4499
|
wait: true,
|
|
4429
4500
|
points: ids,
|
|
4430
4501
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -742,6 +742,7 @@ interface VectorStore {
|
|
|
742
742
|
*/
|
|
743
743
|
declare class VectorStoreClient implements VectorStore {
|
|
744
744
|
private readonly client;
|
|
745
|
+
private readonly clientConfig;
|
|
745
746
|
private readonly collectionName;
|
|
746
747
|
private readonly dims;
|
|
747
748
|
private readonly log;
|
|
@@ -754,6 +755,15 @@ declare class VectorStoreClient implements VectorStore {
|
|
|
754
755
|
* @param logger - Optional pino logger for retry warnings.
|
|
755
756
|
*/
|
|
756
757
|
constructor(config: VectorStoreConfig, dimensions: number, logger?: pino.Logger);
|
|
758
|
+
/**
|
|
759
|
+
* Create a fresh QdrantClient instance.
|
|
760
|
+
*
|
|
761
|
+
* Used to avoid stale HTTP keep-alive connections. The Qdrant JS client's
|
|
762
|
+
* internal undici Agent uses keepAliveTimeout: 10s, which causes ECONNRESET
|
|
763
|
+
* when connections sit idle during slow embedding calls (Gemini p99 ~8s).
|
|
764
|
+
* Creating a fresh client for write operations ensures clean TCP connections.
|
|
765
|
+
*/
|
|
766
|
+
private createClient;
|
|
757
767
|
/**
|
|
758
768
|
* Ensure the collection exists with correct dimensions and Cosine distance.
|
|
759
769
|
*/
|
|
@@ -768,12 +778,18 @@ declare class VectorStoreClient implements VectorStore {
|
|
|
768
778
|
/**
|
|
769
779
|
* Upsert points into the collection.
|
|
770
780
|
*
|
|
781
|
+
* Uses a fresh QdrantClient per attempt to avoid stale keep-alive connections.
|
|
782
|
+
* Between embedding calls and upserts, idle connections may be closed by the
|
|
783
|
+
* server, causing ECONNRESET on reuse.
|
|
784
|
+
*
|
|
771
785
|
* @param points - The points to upsert.
|
|
772
786
|
*/
|
|
773
787
|
upsert(points: VectorPoint[]): Promise<void>;
|
|
774
788
|
/**
|
|
775
789
|
* Delete points by their IDs.
|
|
776
790
|
*
|
|
791
|
+
* Uses a fresh QdrantClient per attempt to avoid stale keep-alive connections.
|
|
792
|
+
*
|
|
777
793
|
* @param ids - The point IDs to delete.
|
|
778
794
|
*/
|
|
779
795
|
delete(ids: string[]): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -905,7 +905,7 @@ function buildAttributes(filePath, stats, extractedFrontmatter, extractedJson) {
|
|
|
905
905
|
* @returns The configured AJV instance.
|
|
906
906
|
*/
|
|
907
907
|
function createRuleAjv() {
|
|
908
|
-
const ajv = new Ajv({ allErrors: true });
|
|
908
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
909
909
|
addFormats(ajv);
|
|
910
910
|
ajv.addKeyword({
|
|
911
911
|
keyword: 'glob',
|
|
@@ -2478,6 +2478,53 @@ function createReindexHandler(deps) {
|
|
|
2478
2478
|
}, deps.logger, 'Reindex');
|
|
2479
2479
|
}
|
|
2480
2480
|
|
|
2481
|
+
/**
|
|
2482
|
+
* @module api/handlers/rulesReapply
|
|
2483
|
+
* Fastify route handler for POST /rules/reapply.
|
|
2484
|
+
* Re-applies current inference rules to already-indexed files matching given globs.
|
|
2485
|
+
*/
|
|
2486
|
+
/**
|
|
2487
|
+
* Create handler for POST /rules/reapply.
|
|
2488
|
+
*
|
|
2489
|
+
* Scrolls through all indexed points, finds files matching the given globs,
|
|
2490
|
+
* and re-applies current inference rules without re-embedding.
|
|
2491
|
+
*/
|
|
2492
|
+
function createRulesReapplyHandler(deps) {
|
|
2493
|
+
return wrapHandler(async (request) => {
|
|
2494
|
+
await Promise.resolve();
|
|
2495
|
+
const { globs } = request.body;
|
|
2496
|
+
if (!Array.isArray(globs) || globs.length === 0) {
|
|
2497
|
+
throw new Error('Missing required field: globs (non-empty string array)');
|
|
2498
|
+
}
|
|
2499
|
+
const normalizedGlobs = globs.map((g) => normalizeSlashes(g));
|
|
2500
|
+
const isMatch = picomatch(normalizedGlobs, { dot: true, nocase: true });
|
|
2501
|
+
// Collect unique file paths matching the globs
|
|
2502
|
+
const matchingFiles = new Set();
|
|
2503
|
+
for await (const point of deps.vectorStore.scroll()) {
|
|
2504
|
+
const filePath = point.payload['file_path'];
|
|
2505
|
+
if (typeof filePath === 'string' && isMatch(filePath)) {
|
|
2506
|
+
matchingFiles.add(filePath);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
deps.logger.info({ globs: normalizedGlobs, matchCount: matchingFiles.size }, 'Re-applying rules to matching files');
|
|
2510
|
+
let updated = 0;
|
|
2511
|
+
for (const filePath of matchingFiles) {
|
|
2512
|
+
try {
|
|
2513
|
+
const result = await deps.processor.processRulesUpdate(filePath);
|
|
2514
|
+
if (result !== null)
|
|
2515
|
+
updated++;
|
|
2516
|
+
}
|
|
2517
|
+
catch (error) {
|
|
2518
|
+
deps.logger.warn({ filePath, err: error }, 'Failed to re-apply rules to file');
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
return {
|
|
2522
|
+
matched: matchingFiles.size,
|
|
2523
|
+
updated,
|
|
2524
|
+
};
|
|
2525
|
+
}, deps.logger, 'RulesReapply');
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2481
2528
|
/**
|
|
2482
2529
|
* @module api/handlers/rulesRegister
|
|
2483
2530
|
* Fastify route handler for POST /rules/register.
|
|
@@ -2720,6 +2767,7 @@ function createApiServer(options) {
|
|
|
2720
2767
|
onRulesChanged,
|
|
2721
2768
|
}));
|
|
2722
2769
|
app.post('/points/delete', createPointsDeleteHandler({ vectorStore, logger }));
|
|
2770
|
+
app.post('/rules/reapply', createRulesReapplyHandler({ processor, vectorStore, logger }));
|
|
2723
2771
|
}
|
|
2724
2772
|
return app;
|
|
2725
2773
|
}
|
|
@@ -3931,7 +3979,10 @@ class DocumentProcessor {
|
|
|
3931
3979
|
if (customMapLib !== undefined) {
|
|
3932
3980
|
this.config = { ...this.config, customMapLib };
|
|
3933
3981
|
}
|
|
3934
|
-
this.logger.info({
|
|
3982
|
+
this.logger.info({
|
|
3983
|
+
rules: compiledRules.length,
|
|
3984
|
+
ruleNames: compiledRules.map((r) => r.rule.name),
|
|
3985
|
+
}, 'Inference rules updated');
|
|
3935
3986
|
}
|
|
3936
3987
|
}
|
|
3937
3988
|
|
|
@@ -4312,6 +4363,7 @@ async function* scrollCollection(client, collectionName, filter, limit = 100) {
|
|
|
4312
4363
|
*/
|
|
4313
4364
|
class VectorStoreClient {
|
|
4314
4365
|
client;
|
|
4366
|
+
clientConfig;
|
|
4315
4367
|
collectionName;
|
|
4316
4368
|
dims;
|
|
4317
4369
|
log;
|
|
@@ -4324,16 +4376,27 @@ class VectorStoreClient {
|
|
|
4324
4376
|
* @param logger - Optional pino logger for retry warnings.
|
|
4325
4377
|
*/
|
|
4326
4378
|
constructor(config, dimensions, logger) {
|
|
4327
|
-
this.
|
|
4328
|
-
|
|
4329
|
-
apiKey: config.apiKey,
|
|
4330
|
-
checkCompatibility: false,
|
|
4331
|
-
});
|
|
4379
|
+
this.clientConfig = { url: config.url, apiKey: config.apiKey };
|
|
4380
|
+
this.client = this.createClient();
|
|
4332
4381
|
this.collectionName = config.collectionName;
|
|
4333
4382
|
this.dims = dimensions;
|
|
4334
4383
|
this.log = getLogger(logger);
|
|
4335
4384
|
this.pinoLogger = logger;
|
|
4336
4385
|
}
|
|
4386
|
+
/**
|
|
4387
|
+
* Create a fresh QdrantClient instance.
|
|
4388
|
+
*
|
|
4389
|
+
* Used to avoid stale HTTP keep-alive connections. The Qdrant JS client's
|
|
4390
|
+
* internal undici Agent uses keepAliveTimeout: 10s, which causes ECONNRESET
|
|
4391
|
+
* when connections sit idle during slow embedding calls (Gemini p99 ~8s).
|
|
4392
|
+
* Creating a fresh client for write operations ensures clean TCP connections.
|
|
4393
|
+
*/
|
|
4394
|
+
createClient() {
|
|
4395
|
+
return new QdrantClient({
|
|
4396
|
+
...this.clientConfig,
|
|
4397
|
+
checkCompatibility: false,
|
|
4398
|
+
});
|
|
4399
|
+
}
|
|
4337
4400
|
/**
|
|
4338
4401
|
* Ensure the collection exists with correct dimensions and Cosine distance.
|
|
4339
4402
|
*/
|
|
@@ -4381,13 +4444,18 @@ class VectorStoreClient {
|
|
|
4381
4444
|
/**
|
|
4382
4445
|
* Upsert points into the collection.
|
|
4383
4446
|
*
|
|
4447
|
+
* Uses a fresh QdrantClient per attempt to avoid stale keep-alive connections.
|
|
4448
|
+
* Between embedding calls and upserts, idle connections may be closed by the
|
|
4449
|
+
* server, causing ECONNRESET on reuse.
|
|
4450
|
+
*
|
|
4384
4451
|
* @param points - The points to upsert.
|
|
4385
4452
|
*/
|
|
4386
4453
|
async upsert(points) {
|
|
4387
4454
|
if (points.length === 0)
|
|
4388
4455
|
return;
|
|
4389
4456
|
await this.retryOperation('upsert', async () => {
|
|
4390
|
-
|
|
4457
|
+
const freshClient = this.createClient();
|
|
4458
|
+
await freshClient.upsert(this.collectionName, {
|
|
4391
4459
|
wait: true,
|
|
4392
4460
|
points: points.map((p) => ({
|
|
4393
4461
|
id: p.id,
|
|
@@ -4400,13 +4468,16 @@ class VectorStoreClient {
|
|
|
4400
4468
|
/**
|
|
4401
4469
|
* Delete points by their IDs.
|
|
4402
4470
|
*
|
|
4471
|
+
* Uses a fresh QdrantClient per attempt to avoid stale keep-alive connections.
|
|
4472
|
+
*
|
|
4403
4473
|
* @param ids - The point IDs to delete.
|
|
4404
4474
|
*/
|
|
4405
4475
|
async delete(ids) {
|
|
4406
4476
|
if (ids.length === 0)
|
|
4407
4477
|
return;
|
|
4408
4478
|
await this.retryOperation('delete', async () => {
|
|
4409
|
-
|
|
4479
|
+
const freshClient = this.createClient();
|
|
4480
|
+
await freshClient.delete(this.collectionName, {
|
|
4410
4481
|
wait: true,
|
|
4411
4482
|
points: ids,
|
|
4412
4483
|
});
|
package/package.json
CHANGED