ds4-context-core 0.1.1 → 0.2.0-beta.1
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/README.md +17 -4
- package/dist/adapter/conformance.d.ts +35 -0
- package/dist/adapter/conformance.d.ts.map +1 -0
- package/dist/adapter/conformance.js +260 -0
- package/dist/adapter/conformance.js.map +1 -0
- package/dist/adapter/runtime-adapter.d.ts +119 -0
- package/dist/adapter/runtime-adapter.d.ts.map +1 -0
- package/dist/adapter/runtime-adapter.js +289 -0
- package/dist/adapter/runtime-adapter.js.map +1 -0
- package/dist/config/config-loader.d.ts +1 -0
- package/dist/config/config-loader.d.ts.map +1 -1
- package/dist/config/config-loader.js +107 -1
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/config.d.ts +45 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +28 -0
- package/dist/config/config.js.map +1 -1
- package/dist/core/canonical-message.d.ts +2 -1
- package/dist/core/canonical-message.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest/context-manifest.d.ts +12 -0
- package/dist/manifest/context-manifest.d.ts.map +1 -1
- package/dist/memory/memory-manager.d.ts +7 -1
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +100 -3
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/memory-types.d.ts +43 -0
- package/dist/memory/memory-types.d.ts.map +1 -1
- package/dist/memory/memory-types.js.map +1 -1
- package/dist/persistence/migrations.d.ts +2 -1
- package/dist/persistence/migrations.d.ts.map +1 -1
- package/dist/persistence/migrations.js +155 -25
- package/dist/persistence/migrations.js.map +1 -1
- package/dist/persistence/repositories/artifact-repository.d.ts +3 -1
- package/dist/persistence/repositories/artifact-repository.d.ts.map +1 -1
- package/dist/persistence/repositories/artifact-repository.js +16 -27
- package/dist/persistence/repositories/artifact-repository.js.map +1 -1
- package/dist/persistence/repositories/context-manifest-repository.d.ts +3 -1
- package/dist/persistence/repositories/context-manifest-repository.d.ts.map +1 -1
- package/dist/persistence/repositories/context-manifest-repository.js +10 -18
- package/dist/persistence/repositories/context-manifest-repository.js.map +1 -1
- package/dist/persistence/repositories/context-quality-repository.d.ts +21 -0
- package/dist/persistence/repositories/context-quality-repository.d.ts.map +1 -0
- package/dist/persistence/repositories/context-quality-repository.js +83 -0
- package/dist/persistence/repositories/context-quality-repository.js.map +1 -0
- package/dist/persistence/repositories/embedding-repository.d.ts +37 -0
- package/dist/persistence/repositories/embedding-repository.d.ts.map +1 -0
- package/dist/persistence/repositories/embedding-repository.js +142 -0
- package/dist/persistence/repositories/embedding-repository.js.map +1 -0
- package/dist/persistence/repositories/lease-repository.d.ts +22 -0
- package/dist/persistence/repositories/lease-repository.d.ts.map +1 -0
- package/dist/persistence/repositories/lease-repository.js +113 -0
- package/dist/persistence/repositories/lease-repository.js.map +1 -0
- package/dist/persistence/repositories/memory-repository.d.ts +34 -3
- package/dist/persistence/repositories/memory-repository.d.ts.map +1 -1
- package/dist/persistence/repositories/memory-repository.js +426 -64
- package/dist/persistence/repositories/memory-repository.js.map +1 -1
- package/dist/persistence/repositories/project-knowledge-repository.d.ts +28 -5
- package/dist/persistence/repositories/project-knowledge-repository.d.ts.map +1 -1
- package/dist/persistence/repositories/project-knowledge-repository.js +123 -53
- package/dist/persistence/repositories/project-knowledge-repository.js.map +1 -1
- package/dist/persistence/repositories/session-index-repository.d.ts +9 -1
- package/dist/persistence/repositories/session-index-repository.d.ts.map +1 -1
- package/dist/persistence/repositories/session-index-repository.js +38 -17
- package/dist/persistence/repositories/session-index-repository.js.map +1 -1
- package/dist/persistence/repositories/summary-repository.d.ts +3 -1
- package/dist/persistence/repositories/summary-repository.d.ts.map +1 -1
- package/dist/persistence/repositories/summary-repository.js +35 -31
- package/dist/persistence/repositories/summary-repository.js.map +1 -1
- package/dist/persistence/sqlite.d.ts +10 -0
- package/dist/persistence/sqlite.d.ts.map +1 -1
- package/dist/persistence/sqlite.js +41 -12
- package/dist/persistence/sqlite.js.map +1 -1
- package/dist/persistence/write-coordinator.d.ts +37 -0
- package/dist/persistence/write-coordinator.d.ts.map +1 -0
- package/dist/persistence/write-coordinator.js +120 -0
- package/dist/persistence/write-coordinator.js.map +1 -0
- package/dist/planner/context-planner.d.ts +5 -0
- package/dist/planner/context-planner.d.ts.map +1 -1
- package/dist/planner/context-planner.js +41 -62
- package/dist/planner/context-planner.js.map +1 -1
- package/dist/project/file-indexer.d.ts +5 -3
- package/dist/project/file-indexer.d.ts.map +1 -1
- package/dist/project/file-indexer.js +108 -42
- package/dist/project/file-indexer.js.map +1 -1
- package/dist/project/project-knowledge.d.ts +15 -3
- package/dist/project/project-knowledge.d.ts.map +1 -1
- package/dist/project/project-knowledge.js +168 -42
- package/dist/project/project-knowledge.js.map +1 -1
- package/dist/project/symbol-parser.d.ts +48 -0
- package/dist/project/symbol-parser.d.ts.map +1 -0
- package/dist/project/symbol-parser.js +427 -0
- package/dist/project/symbol-parser.js.map +1 -0
- package/dist/quality/context-quality.d.ts +172 -0
- package/dist/quality/context-quality.d.ts.map +1 -0
- package/dist/quality/context-quality.js +624 -0
- package/dist/quality/context-quality.js.map +1 -0
- package/dist/ranking/learned-ranker.d.ts +174 -0
- package/dist/ranking/learned-ranker.d.ts.map +1 -0
- package/dist/ranking/learned-ranker.js +782 -0
- package/dist/ranking/learned-ranker.js.map +1 -0
- package/dist/retrieval/embedding.d.ts +92 -0
- package/dist/retrieval/embedding.d.ts.map +1 -0
- package/dist/retrieval/embedding.js +56 -0
- package/dist/retrieval/embedding.js.map +1 -0
- package/dist/retrieval/retrieval-engine.d.ts +9 -1
- package/dist/retrieval/retrieval-engine.d.ts.map +1 -1
- package/dist/retrieval/retrieval-engine.js +105 -7
- package/dist/retrieval/retrieval-engine.js.map +1 -1
- package/dist/retrieval/semantic-index.d.ts +30 -0
- package/dist/retrieval/semantic-index.d.ts.map +1 -0
- package/dist/retrieval/semantic-index.js +284 -0
- package/dist/retrieval/semantic-index.js.map +1 -0
- package/dist/retrieval/semantic-quality.d.ts +52 -0
- package/dist/retrieval/semantic-quality.d.ts.map +1 -0
- package/dist/retrieval/semantic-quality.js +117 -0
- package/dist/retrieval/semantic-quality.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DatabaseSync } from "node:sqlite";
|
|
2
2
|
import type { TokenCalibrationSample } from "../../core/model-awareness.ts";
|
|
3
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.ts";
|
|
3
4
|
import type { ContextManifest } from "../../manifest/context-manifest.ts";
|
|
4
5
|
export interface ProviderTokenUsage {
|
|
5
6
|
inputTokens: number;
|
|
@@ -8,7 +9,8 @@ export interface ProviderTokenUsage {
|
|
|
8
9
|
}
|
|
9
10
|
export declare class ContextManifestRepository {
|
|
10
11
|
private readonly database;
|
|
11
|
-
|
|
12
|
+
private readonly writes;
|
|
13
|
+
constructor(database: DatabaseSync, writes?: SqliteWriteCoordinator);
|
|
12
14
|
save(manifest: ContextManifest): void;
|
|
13
15
|
get(manifestId: string): ContextManifest | undefined;
|
|
14
16
|
getLatest(sessionId: string): ContextManifest | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-manifest-repository.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/context-manifest-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,oCAAoC,CAAC;AAgB5C,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAiBD,qBAAa,yBAAyB;
|
|
1
|
+
{"version":3,"file":"context-manifest-repository.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/context-manifest-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,oCAAoC,CAAC;AAgB5C,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAiBD,qBAAa,yBAAyB;IAElC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,QAAQ,EAAE,YAAY,EACtB,MAAM,yBAAuC;IAGhE,IAAI,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IA2CrC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAOpD,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAWzD,mBAAmB,CACjB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,kBAAkB,EACzB,SAAS,EAAE,MAAM,GAChB,eAAe,GAAG,SAAS;IAkE9B,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAQhH,sBAAsB,CACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,gBAAgB,SAAa,GAC5B,sBAAsB,EAAE;CAmB5B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.js";
|
|
1
2
|
function validTokenCount(value) {
|
|
2
3
|
return Number.isSafeInteger(value) && value >= 0;
|
|
3
4
|
}
|
|
@@ -13,11 +14,13 @@ function providerUsage(input) {
|
|
|
13
14
|
}
|
|
14
15
|
export class ContextManifestRepository {
|
|
15
16
|
database;
|
|
16
|
-
|
|
17
|
+
writes;
|
|
18
|
+
constructor(database, writes = new SqliteWriteCoordinator(database)) {
|
|
17
19
|
this.database = database;
|
|
20
|
+
this.writes = writes;
|
|
18
21
|
}
|
|
19
22
|
save(manifest) {
|
|
20
|
-
this.database.prepare(`
|
|
23
|
+
this.writes.execute("context-manifest-save", () => this.database.prepare(`
|
|
21
24
|
INSERT INTO context_manifests(
|
|
22
25
|
manifest_id, session_id, created_at, provider, model,
|
|
23
26
|
estimated_tokens, actual_tokens, manifest_json,
|
|
@@ -35,7 +38,7 @@ export class ContextManifestRepository {
|
|
|
35
38
|
input_tokens = excluded.input_tokens,
|
|
36
39
|
cache_read_tokens = excluded.cache_read_tokens,
|
|
37
40
|
cache_write_tokens = excluded.cache_write_tokens
|
|
38
|
-
`).run(manifest.id, manifest.sessionId, manifest.createdAt, manifest.provider, manifest.model, manifest.estimatedInputTokens, manifest.actualInputTokens ?? null, JSON.stringify(manifest), manifest.promptHash, manifest.policyVersion, manifest.plannerVersion, manifest.branchLeafId ?? null, manifest.contextWindow, manifest.outputReserve, manifest.hardInputLimit, manifest.targetInputTokens, manifest.piReportedContextTokens ?? null, manifest.providerUsage?.inputTokens ?? null, manifest.providerUsage?.cacheReadTokens ?? null, manifest.providerUsage?.cacheWriteTokens ?? null);
|
|
41
|
+
`).run(manifest.id, manifest.sessionId, manifest.createdAt, manifest.provider, manifest.model, manifest.estimatedInputTokens, manifest.actualInputTokens ?? null, JSON.stringify(manifest), manifest.promptHash, manifest.policyVersion, manifest.plannerVersion, manifest.branchLeafId ?? null, manifest.contextWindow, manifest.outputReserve, manifest.hardInputLimit, manifest.targetInputTokens, manifest.piReportedContextTokens ?? null, manifest.providerUsage?.inputTokens ?? null, manifest.providerUsage?.cacheReadTokens ?? null, manifest.providerUsage?.cacheWriteTokens ?? null));
|
|
39
42
|
}
|
|
40
43
|
get(manifestId) {
|
|
41
44
|
const row = this.database.prepare("SELECT manifest_json, actual_tokens FROM context_manifests WHERE manifest_id = ?").get(manifestId);
|
|
@@ -62,22 +65,17 @@ export class ContextManifestRepository {
|
|
|
62
65
|
const recordedUsage = providerUsage(usage);
|
|
63
66
|
if (recordedUsage.totalInputTokens <= 0)
|
|
64
67
|
return this.get(manifestId);
|
|
65
|
-
this.
|
|
66
|
-
try {
|
|
68
|
+
return this.writes.transaction("context-manifest-provider-usage", () => {
|
|
67
69
|
const row = this.database.prepare(`
|
|
68
70
|
SELECT manifest_json, actual_tokens
|
|
69
71
|
FROM context_manifests
|
|
70
72
|
WHERE manifest_id = ?
|
|
71
73
|
`).get(manifestId);
|
|
72
|
-
if (!row)
|
|
73
|
-
this.database.exec("COMMIT");
|
|
74
|
+
if (!row)
|
|
74
75
|
return undefined;
|
|
75
|
-
}
|
|
76
76
|
const manifest = parseManifest(row.manifest_json);
|
|
77
|
-
if (row.actual_tokens !== null)
|
|
78
|
-
this.database.exec("COMMIT");
|
|
77
|
+
if (row.actual_tokens !== null)
|
|
79
78
|
return manifest;
|
|
80
|
-
}
|
|
81
79
|
const updated = {
|
|
82
80
|
...manifest,
|
|
83
81
|
actualInputTokens: recordedUsage.totalInputTokens,
|
|
@@ -97,14 +95,8 @@ export class ContextManifestRepository {
|
|
|
97
95
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
98
96
|
`).run(manifest.provider, manifest.model, manifest.estimatedInputTokens, recordedUsage.totalInputTokens, recordedUsage.totalInputTokens / manifest.estimatedInputTokens, createdAt, manifestId, manifest.modelAwareness?.calibration.estimator ?? "chars-v1", recordedUsage.inputTokens, recordedUsage.cacheReadTokens, recordedUsage.cacheWriteTokens);
|
|
99
97
|
}
|
|
100
|
-
this.database.exec("COMMIT");
|
|
101
98
|
return updated;
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
if (this.database.isTransaction)
|
|
105
|
-
this.database.exec("ROLLBACK");
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
99
|
+
});
|
|
108
100
|
}
|
|
109
101
|
recordActualInput(manifestId, actualInputTokens, createdAt) {
|
|
110
102
|
return this.recordProviderUsage(manifestId, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-manifest-repository.js","sourceRoot":"","sources":["../../../src/persistence/repositories/context-manifest-repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context-manifest-repository.js","sourceRoot":"","sources":["../../../src/persistence/repositories/context-manifest-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.ts";
|
|
3
|
+
import { qualityTiming, type ContextQualityAggregate, type ContextQualitySample } from "../../quality/context-quality.ts";
|
|
4
|
+
export interface StoredContextQualitySamples {
|
|
5
|
+
samples: ContextQualitySample[];
|
|
6
|
+
ignoredSamples: number;
|
|
7
|
+
}
|
|
8
|
+
export interface StoredContextQualityAggregate extends StoredContextQualitySamples {
|
|
9
|
+
aggregate: ContextQualityAggregate;
|
|
10
|
+
timing: ReturnType<typeof qualityTiming>;
|
|
11
|
+
}
|
|
12
|
+
export declare class ContextQualityRepository {
|
|
13
|
+
private readonly database;
|
|
14
|
+
private readonly writes;
|
|
15
|
+
constructor(database: DatabaseSync, writes?: SqliteWriteCoordinator);
|
|
16
|
+
save(sample: ContextQualitySample, recordedAt: number, maxSamples: number): boolean;
|
|
17
|
+
list(limit?: number): StoredContextQualitySamples;
|
|
18
|
+
aggregate(limit?: number): StoredContextQualityAggregate;
|
|
19
|
+
clear(): void;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=context-quality-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-quality-repository.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/context-quality-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAGL,aAAa,EACb,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,kCAAkC,CAAC;AAM1C,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,6BAA8B,SAAQ,2BAA2B;IAChF,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;CAC1C;AAWD,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,QAAQ,EAAE,YAAY,EACtB,MAAM,yBAAuC;IAGhE,IAAI,CAAC,MAAM,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAyCnF,IAAI,CAAC,KAAK,SAAS,GAAG,2BAA2B;IAkBjD,SAAS,CAAC,KAAK,SAAS,GAAG,6BAA6B;IASxD,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.js";
|
|
2
|
+
import { aggregateContextQuality, isContextQualitySample, qualityTiming, } from "../../quality/context-quality.js";
|
|
3
|
+
function parseSample(value) {
|
|
4
|
+
try {
|
|
5
|
+
const parsed = JSON.parse(value);
|
|
6
|
+
return isContextQualitySample(parsed) ? parsed : undefined;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class ContextQualityRepository {
|
|
13
|
+
database;
|
|
14
|
+
writes;
|
|
15
|
+
constructor(database, writes = new SqliteWriteCoordinator(database)) {
|
|
16
|
+
this.database = database;
|
|
17
|
+
this.writes = writes;
|
|
18
|
+
}
|
|
19
|
+
save(sample, recordedAt, maxSamples) {
|
|
20
|
+
if (!isContextQualitySample(sample)
|
|
21
|
+
|| !Number.isSafeInteger(recordedAt)
|
|
22
|
+
|| recordedAt < 0
|
|
23
|
+
|| !Number.isSafeInteger(maxSamples)
|
|
24
|
+
|| maxSamples <= 0)
|
|
25
|
+
return false;
|
|
26
|
+
return this.writes.transaction("context-quality-save", () => {
|
|
27
|
+
this.database.prepare(`
|
|
28
|
+
INSERT INTO context_quality_samples(
|
|
29
|
+
sample_id, strategy_id, corpus_version, planner_version,
|
|
30
|
+
profile_key, recorded_at, planning_duration_ms, sample_json
|
|
31
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
32
|
+
ON CONFLICT(sample_id, strategy_id) DO UPDATE SET
|
|
33
|
+
corpus_version = excluded.corpus_version,
|
|
34
|
+
planner_version = excluded.planner_version,
|
|
35
|
+
profile_key = excluded.profile_key,
|
|
36
|
+
recorded_at = excluded.recorded_at,
|
|
37
|
+
planning_duration_ms = excluded.planning_duration_ms,
|
|
38
|
+
sample_json = excluded.sample_json
|
|
39
|
+
`).run(sample.sampleId, sample.strategyId, sample.corpusVersion, sample.plannerVersion, sample.profileKey, recordedAt, sample.timing?.planningDurationMs ?? null, JSON.stringify(sample));
|
|
40
|
+
this.database.prepare(`
|
|
41
|
+
DELETE FROM context_quality_samples
|
|
42
|
+
WHERE (sample_id, strategy_id) IN (
|
|
43
|
+
SELECT sample_id, strategy_id FROM context_quality_samples
|
|
44
|
+
ORDER BY recorded_at DESC, sample_id DESC, strategy_id DESC
|
|
45
|
+
LIMIT -1 OFFSET ?
|
|
46
|
+
)
|
|
47
|
+
`).run(maxSamples);
|
|
48
|
+
return true;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
list(limit = 10_000) {
|
|
52
|
+
if (!Number.isSafeInteger(limit) || limit <= 0)
|
|
53
|
+
return { samples: [], ignoredSamples: 0 };
|
|
54
|
+
const rows = this.database.prepare(`
|
|
55
|
+
SELECT sample_json
|
|
56
|
+
FROM context_quality_samples
|
|
57
|
+
ORDER BY recorded_at ASC, sample_id ASC, strategy_id ASC
|
|
58
|
+
LIMIT ?
|
|
59
|
+
`).all(limit);
|
|
60
|
+
const samples = [];
|
|
61
|
+
let ignoredSamples = 0;
|
|
62
|
+
for (const row of rows) {
|
|
63
|
+
const sample = parseSample(row.sample_json);
|
|
64
|
+
if (sample)
|
|
65
|
+
samples.push(sample);
|
|
66
|
+
else
|
|
67
|
+
ignoredSamples++;
|
|
68
|
+
}
|
|
69
|
+
return { samples, ignoredSamples };
|
|
70
|
+
}
|
|
71
|
+
aggregate(limit = 10_000) {
|
|
72
|
+
const stored = this.list(limit);
|
|
73
|
+
return {
|
|
74
|
+
...stored,
|
|
75
|
+
aggregate: aggregateContextQuality(stored.samples),
|
|
76
|
+
timing: qualityTiming(stored.samples),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
clear() {
|
|
80
|
+
this.writes.execute("context-quality-clear", () => this.database.exec("DELETE FROM context_quality_samples"));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=context-quality-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-quality-repository.js","sourceRoot":"","sources":["../../../src/persistence/repositories/context-quality-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.ts";
|
|
3
|
+
import { type EmbeddingModelIdentity, type EmbeddingSourceKind, type SemanticSearchHit } from "../../retrieval/embedding.ts";
|
|
4
|
+
export interface StoredEmbedding {
|
|
5
|
+
sourceKind: EmbeddingSourceKind;
|
|
6
|
+
scopeId: string;
|
|
7
|
+
sourceKey: string;
|
|
8
|
+
sourceGroup: string;
|
|
9
|
+
sourceHash: string;
|
|
10
|
+
chunkingVersion: string;
|
|
11
|
+
provider: string;
|
|
12
|
+
model: string;
|
|
13
|
+
dimensions: number;
|
|
14
|
+
vector: readonly number[];
|
|
15
|
+
indexedAt: number;
|
|
16
|
+
}
|
|
17
|
+
export interface EmbeddingSearchResult {
|
|
18
|
+
hits: SemanticSearchHit[];
|
|
19
|
+
scannedVectors: number;
|
|
20
|
+
corruptVectors: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class EmbeddingRepository {
|
|
23
|
+
private readonly database;
|
|
24
|
+
private readonly writes;
|
|
25
|
+
constructor(database: DatabaseSync, writes?: SqliteWriteCoordinator);
|
|
26
|
+
getVector(kind: EmbeddingSourceKind, scopeId: string, sourceKey: string, sourceHash: string, chunkingVersion: string, identity: EmbeddingModelIdentity): readonly number[] | undefined;
|
|
27
|
+
upsert(rows: readonly StoredEmbedding[]): void;
|
|
28
|
+
/** Remove rows whose canonical source key/hash/chunk version is no longer current. */
|
|
29
|
+
pruneSources(kind: EmbeddingSourceKind, scopeId: string, sources: readonly {
|
|
30
|
+
sourceKey: string;
|
|
31
|
+
sourceHash: string;
|
|
32
|
+
chunkingVersion: string;
|
|
33
|
+
}[]): number;
|
|
34
|
+
searchSimilar(kind: EmbeddingSourceKind, scopeId: string, identity: EmbeddingModelIdentity, queryVector: readonly number[], limit: number, maximumRows: number): EmbeddingSearchResult;
|
|
35
|
+
countProfile(kind: EmbeddingSourceKind, scopeId: string, identity: EmbeddingModelIdentity): number;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=embedding-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-repository.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/embedding-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEvB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,mBAAmB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAoBD,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,QAAQ,EAAE,YAAY,EACtB,MAAM,yBAAuC;IAGhE,SAAS,CACP,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,sBAAsB,GAC/B,SAAS,MAAM,EAAE,GAAG,SAAS;IAoBhC,MAAM,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI;IAoC9C,sFAAsF;IACtF,YAAY,CACV,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,EAAE,GACrF,MAAM;IAiCT,aAAa,CACX,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,sBAAsB,EAChC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,qBAAqB;IAqDxB,YAAY,CACV,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,sBAAsB,GAC/B,MAAM;CAeV"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.js";
|
|
2
|
+
import { cosineSimilarity, validEmbeddingVector, } from "../../retrieval/embedding.js";
|
|
3
|
+
function parseVector(value, dimensions) {
|
|
4
|
+
try {
|
|
5
|
+
const parsed = JSON.parse(value);
|
|
6
|
+
if (!Array.isArray(parsed))
|
|
7
|
+
return undefined;
|
|
8
|
+
const vector = parsed.filter((item) => typeof item === "number");
|
|
9
|
+
return validEmbeddingVector(vector, dimensions) ? vector : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class EmbeddingRepository {
|
|
16
|
+
database;
|
|
17
|
+
writes;
|
|
18
|
+
constructor(database, writes = new SqliteWriteCoordinator(database)) {
|
|
19
|
+
this.database = database;
|
|
20
|
+
this.writes = writes;
|
|
21
|
+
}
|
|
22
|
+
getVector(kind, scopeId, sourceKey, sourceHash, chunkingVersion, identity) {
|
|
23
|
+
const row = this.database.prepare(`
|
|
24
|
+
SELECT vector_json
|
|
25
|
+
FROM derived_embeddings
|
|
26
|
+
WHERE source_kind = ? AND scope_id = ? AND source_key = ?
|
|
27
|
+
AND source_hash = ? AND chunking_version = ?
|
|
28
|
+
AND embedding_provider = ? AND embedding_model = ? AND dimensions = ?
|
|
29
|
+
`).get(kind, scopeId, sourceKey, sourceHash, chunkingVersion, identity.provider, identity.model, identity.dimensions);
|
|
30
|
+
return row ? parseVector(row.vector_json, identity.dimensions) : undefined;
|
|
31
|
+
}
|
|
32
|
+
upsert(rows) {
|
|
33
|
+
if (rows.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
this.writes.transaction("embedding-upsert", () => {
|
|
36
|
+
const statement = this.database.prepare(`
|
|
37
|
+
INSERT INTO derived_embeddings(
|
|
38
|
+
source_kind, scope_id, source_key, source_group, source_hash,
|
|
39
|
+
chunking_version, embedding_provider, embedding_model, dimensions,
|
|
40
|
+
vector_json, indexed_at
|
|
41
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
42
|
+
ON CONFLICT(
|
|
43
|
+
source_kind, scope_id, source_key, source_hash, chunking_version,
|
|
44
|
+
embedding_provider, embedding_model, dimensions
|
|
45
|
+
) DO UPDATE SET
|
|
46
|
+
source_group = excluded.source_group,
|
|
47
|
+
vector_json = excluded.vector_json,
|
|
48
|
+
indexed_at = excluded.indexed_at
|
|
49
|
+
`);
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
if (!validEmbeddingVector(row.vector, row.dimensions))
|
|
52
|
+
continue;
|
|
53
|
+
statement.run(row.sourceKind, row.scopeId, row.sourceKey, row.sourceGroup, row.sourceHash, row.chunkingVersion, row.provider, row.model, row.dimensions, JSON.stringify(row.vector), row.indexedAt);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** Remove rows whose canonical source key/hash/chunk version is no longer current. */
|
|
58
|
+
pruneSources(kind, scopeId, sources) {
|
|
59
|
+
return this.writes.transaction("embedding-prune", () => {
|
|
60
|
+
this.database.exec(`
|
|
61
|
+
CREATE TEMP TABLE IF NOT EXISTS ds4_current_embedding_sources (
|
|
62
|
+
source_key TEXT NOT NULL,
|
|
63
|
+
source_hash TEXT NOT NULL,
|
|
64
|
+
chunking_version TEXT NOT NULL,
|
|
65
|
+
PRIMARY KEY(source_key, source_hash, chunking_version)
|
|
66
|
+
) WITHOUT ROWID, STRICT;
|
|
67
|
+
DELETE FROM ds4_current_embedding_sources;
|
|
68
|
+
`);
|
|
69
|
+
const insert = this.database.prepare(`
|
|
70
|
+
INSERT OR IGNORE INTO ds4_current_embedding_sources(
|
|
71
|
+
source_key, source_hash, chunking_version
|
|
72
|
+
) VALUES (?, ?, ?)
|
|
73
|
+
`);
|
|
74
|
+
for (const source of sources) {
|
|
75
|
+
insert.run(source.sourceKey, source.sourceHash, source.chunkingVersion);
|
|
76
|
+
}
|
|
77
|
+
const result = this.database.prepare(`
|
|
78
|
+
DELETE FROM derived_embeddings
|
|
79
|
+
WHERE source_kind = ? AND scope_id = ?
|
|
80
|
+
AND NOT EXISTS (
|
|
81
|
+
SELECT 1 FROM ds4_current_embedding_sources AS current
|
|
82
|
+
WHERE current.source_key = derived_embeddings.source_key
|
|
83
|
+
AND current.source_hash = derived_embeddings.source_hash
|
|
84
|
+
AND current.chunking_version = derived_embeddings.chunking_version
|
|
85
|
+
)
|
|
86
|
+
`).run(kind, scopeId);
|
|
87
|
+
return Number(result.changes);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
searchSimilar(kind, scopeId, identity, queryVector, limit, maximumRows) {
|
|
91
|
+
if (!validEmbeddingVector(queryVector, identity.dimensions)) {
|
|
92
|
+
return { hits: [], scannedVectors: 0, corruptVectors: 0 };
|
|
93
|
+
}
|
|
94
|
+
const rows = this.database.prepare(`
|
|
95
|
+
SELECT source_key, source_hash, chunking_version, vector_json
|
|
96
|
+
FROM derived_embeddings
|
|
97
|
+
WHERE source_kind = ? AND scope_id = ?
|
|
98
|
+
AND embedding_provider = ? AND embedding_model = ? AND dimensions = ?
|
|
99
|
+
ORDER BY source_key, source_hash, chunking_version
|
|
100
|
+
LIMIT ?
|
|
101
|
+
`).all(kind, scopeId, identity.provider, identity.model, identity.dimensions, maximumRows);
|
|
102
|
+
let corruptVectors = 0;
|
|
103
|
+
const scored = [];
|
|
104
|
+
for (const row of rows) {
|
|
105
|
+
const vector = parseVector(row.vector_json, identity.dimensions);
|
|
106
|
+
if (!vector) {
|
|
107
|
+
corruptVectors++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const similarity = cosineSimilarity(queryVector, vector);
|
|
111
|
+
if (similarity === undefined || !Number.isFinite(similarity)) {
|
|
112
|
+
corruptVectors++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
scored.push({
|
|
116
|
+
sourceKey: row.source_key,
|
|
117
|
+
sourceHash: row.source_hash,
|
|
118
|
+
chunkingVersion: row.chunking_version,
|
|
119
|
+
similarity: Math.round(similarity * 1_000_000) / 1_000_000,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
scored.sort((left, right) => right.similarity - left.similarity
|
|
123
|
+
|| left.sourceKey.localeCompare(right.sourceKey)
|
|
124
|
+
|| left.sourceHash.localeCompare(right.sourceHash)
|
|
125
|
+
|| left.chunkingVersion.localeCompare(right.chunkingVersion));
|
|
126
|
+
return {
|
|
127
|
+
hits: scored.slice(0, Math.max(0, limit)).map((hit, rank) => ({ ...hit, rank })),
|
|
128
|
+
scannedVectors: rows.length,
|
|
129
|
+
corruptVectors,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
countProfile(kind, scopeId, identity) {
|
|
133
|
+
const row = this.database.prepare(`
|
|
134
|
+
SELECT count(*) AS count
|
|
135
|
+
FROM derived_embeddings
|
|
136
|
+
WHERE source_kind = ? AND scope_id = ?
|
|
137
|
+
AND embedding_provider = ? AND embedding_model = ? AND dimensions = ?
|
|
138
|
+
`).get(kind, scopeId, identity.provider, identity.model, identity.dimensions);
|
|
139
|
+
return row.count;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=embedding-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-repository.js","sourceRoot":"","sources":["../../../src/persistence/repositories/embedding-repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACL,gBAAgB,EAIhB,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AA6BtC,SAAS,WAAW,CAAC,KAAa,EAAE,UAAkB;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,SAAS,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;QACjF,OAAO,oBAAoB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,mBAAmB;IAEX;IACA;IAFnB,YACmB,QAAsB,EACtB,SAAS,IAAI,sBAAsB,CAAC,QAAQ,CAAC;QAD7C,aAAQ,GAAR,QAAQ,CAAc;QACtB,WAAM,GAAN,MAAM,CAAuC;IAC7D,CAAC;IAEJ,SAAS,CACP,IAAyB,EACzB,OAAe,EACf,SAAiB,EACjB,UAAkB,EAClB,eAAuB,EACvB,QAAgC;QAEhC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;;;;;;KAMjC,CAAC,CAAC,GAAG,CACJ,IAAI,EACJ,OAAO,EACP,SAAS,EACT,UAAU,EACV,eAAe,EACf,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,UAAU,CACmB,CAAC;QACzC,OAAO,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,CAAC;IAED,MAAM,CAAC,IAAgC;QACrC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9B,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,GAAG,EAAE;YAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;;;;;;;;;;;;;OAavC,CAAC,CAAC;YACH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC;oBAAE,SAAS;gBAChE,SAAS,CAAC,GAAG,CACX,GAAG,CAAC,UAAU,EACd,GAAG,CAAC,OAAO,EACX,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,WAAW,EACf,GAAG,CAAC,UAAU,EACd,GAAG,CAAC,eAAe,EACnB,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,KAAK,EACT,GAAG,CAAC,UAAU,EACd,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAC1B,GAAG,CAAC,SAAS,CACd,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,sFAAsF;IACtF,YAAY,CACV,IAAyB,EACzB,OAAe,EACf,OAAsF;QAEtF,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAiB,EAAE,GAAG,EAAE;YACrD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;;;;;;;;OAQlB,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;;;;OAIpC,CAAC,CAAC;YACH,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;;;;;;;;;OASpC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACtB,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,aAAa,CACX,IAAyB,EACzB,OAAe,EACf,QAAgC,EAChC,WAA8B,EAC9B,KAAa,EACb,WAAmB;QAEnB,IAAI,CAAC,oBAAoB,CAAC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5D,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;QAC5D,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;;;;;;;KAOlC,CAAC,CAAC,GAAG,CACJ,IAAI,EACJ,OAAO,EACP,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,UAAU,EACnB,WAAW,CACiB,CAAC;QAE/B,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,MAAM,MAAM,GAA2C,EAAE,CAAC;QAC1D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YACjE,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,cAAc,EAAE,CAAC;gBACjB,SAAS;YACX,CAAC;YACD,MAAM,UAAU,GAAG,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACzD,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7D,cAAc,EAAE,CAAC;gBACjB,SAAS;YACX,CAAC;YACD,MAAM,CAAC,IAAI,CAAC;gBACV,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,UAAU,EAAE,GAAG,CAAC,WAAW;gBAC3B,eAAe,EAAE,GAAG,CAAC,gBAAgB;gBACrC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,SAAS;aAC3D,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC1B,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;eAC/B,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC;eAC7C,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC;eAC/C,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,CAC7D,CAAC;QACF,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAChF,cAAc,EAAE,IAAI,CAAC,MAAM;YAC3B,cAAc;SACf,CAAC;IACJ,CAAC;IAED,YAAY,CACV,IAAyB,EACzB,OAAe,EACf,QAAgC;QAEhC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;;;;;KAKjC,CAAC,CAAC,GAAG,CACJ,IAAI,EACJ,OAAO,EACP,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,UAAU,CACC,CAAC;QACvB,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.ts";
|
|
3
|
+
export interface ResourceLease {
|
|
4
|
+
resourceType: string;
|
|
5
|
+
resourceKey: string;
|
|
6
|
+
ownerId: string;
|
|
7
|
+
fencingToken: number;
|
|
8
|
+
acquiredAt: number;
|
|
9
|
+
expiresAt: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class LeaseRepository {
|
|
12
|
+
private readonly database;
|
|
13
|
+
private readonly writes;
|
|
14
|
+
constructor(database: DatabaseSync, writes?: SqliteWriteCoordinator);
|
|
15
|
+
acquire(resourceType: string, resourceKey: string, ownerId: string, acquiredAt: number, durationMs: number): ResourceLease | undefined;
|
|
16
|
+
renew(lease: ResourceLease, renewedAt: number, durationMs: number): ResourceLease | undefined;
|
|
17
|
+
isHeld(lease: ResourceLease, at: number): boolean;
|
|
18
|
+
release(lease: ResourceLease): boolean;
|
|
19
|
+
get(resourceType: string, resourceKey: string): ResourceLease | undefined;
|
|
20
|
+
deleteExpired(expiredAt: number): number;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=lease-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease-repository.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/lease-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAuCD,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,QAAQ,EAAE,YAAY,EACtB,MAAM,yBAAuC;IAGhE,OAAO,CACL,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,aAAa,GAAG,SAAS;IA2B5B,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAyB7F,MAAM,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAejD,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAUtC,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IASzE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;CAMzC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { SqliteWriteCoordinator } from "../write-coordinator.js";
|
|
2
|
+
function leaseFromRow(row) {
|
|
3
|
+
return {
|
|
4
|
+
resourceType: row.resource_type,
|
|
5
|
+
resourceKey: row.resource_key,
|
|
6
|
+
ownerId: row.owner_id,
|
|
7
|
+
fencingToken: row.fencing_token,
|
|
8
|
+
acquiredAt: row.acquired_at,
|
|
9
|
+
expiresAt: row.expires_at,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function validIdentity(value) {
|
|
13
|
+
return value.trim() === value && value.length > 0 && value.length <= 4_096;
|
|
14
|
+
}
|
|
15
|
+
function validateRequest(resourceType, resourceKey, ownerId, at) {
|
|
16
|
+
if (!validIdentity(resourceType))
|
|
17
|
+
throw new Error("Lease resourceType must be a non-empty trimmed string");
|
|
18
|
+
if (!validIdentity(resourceKey))
|
|
19
|
+
throw new Error("Lease resourceKey must be a non-empty trimmed string");
|
|
20
|
+
if (!validIdentity(ownerId))
|
|
21
|
+
throw new Error("Lease ownerId must be a non-empty trimmed string");
|
|
22
|
+
if (!Number.isSafeInteger(at) || at < 0)
|
|
23
|
+
throw new Error("Lease timestamp must be a non-negative integer");
|
|
24
|
+
}
|
|
25
|
+
function validateDuration(durationMs) {
|
|
26
|
+
if (!Number.isSafeInteger(durationMs) || durationMs < 1 || durationMs > 86_400_000) {
|
|
27
|
+
throw new Error("Lease durationMs must be an integer between 1 and 86400000");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class LeaseRepository {
|
|
31
|
+
database;
|
|
32
|
+
writes;
|
|
33
|
+
constructor(database, writes = new SqliteWriteCoordinator(database)) {
|
|
34
|
+
this.database = database;
|
|
35
|
+
this.writes = writes;
|
|
36
|
+
}
|
|
37
|
+
acquire(resourceType, resourceKey, ownerId, acquiredAt, durationMs) {
|
|
38
|
+
validateRequest(resourceType, resourceKey, ownerId, acquiredAt);
|
|
39
|
+
validateDuration(durationMs);
|
|
40
|
+
const expiresAt = acquiredAt + durationMs;
|
|
41
|
+
if (!Number.isSafeInteger(expiresAt))
|
|
42
|
+
throw new Error("Lease expiry exceeds the safe integer range");
|
|
43
|
+
return this.writes.execute("lease-acquire", () => {
|
|
44
|
+
const row = this.database.prepare(`
|
|
45
|
+
INSERT INTO resource_leases(
|
|
46
|
+
resource_type, resource_key, owner_id, fencing_token, acquired_at, expires_at
|
|
47
|
+
) VALUES (?, ?, ?, 1, ?, ?)
|
|
48
|
+
ON CONFLICT(resource_type, resource_key) DO UPDATE SET
|
|
49
|
+
owner_id = excluded.owner_id,
|
|
50
|
+
fencing_token = CASE
|
|
51
|
+
WHEN resource_leases.owner_id = excluded.owner_id THEN resource_leases.fencing_token
|
|
52
|
+
ELSE resource_leases.fencing_token + 1
|
|
53
|
+
END,
|
|
54
|
+
acquired_at = excluded.acquired_at,
|
|
55
|
+
expires_at = excluded.expires_at
|
|
56
|
+
WHERE resource_leases.owner_id = excluded.owner_id
|
|
57
|
+
OR resource_leases.expires_at <= excluded.acquired_at
|
|
58
|
+
RETURNING resource_type, resource_key, owner_id, fencing_token, acquired_at, expires_at
|
|
59
|
+
`).get(resourceType, resourceKey, ownerId, acquiredAt, expiresAt);
|
|
60
|
+
return row ? leaseFromRow(row) : undefined;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
renew(lease, renewedAt, durationMs) {
|
|
64
|
+
validateRequest(lease.resourceType, lease.resourceKey, lease.ownerId, renewedAt);
|
|
65
|
+
validateDuration(durationMs);
|
|
66
|
+
const expiresAt = Math.max(lease.expiresAt, renewedAt + durationMs);
|
|
67
|
+
if (!Number.isSafeInteger(expiresAt))
|
|
68
|
+
throw new Error("Lease expiry exceeds the safe integer range");
|
|
69
|
+
return this.writes.execute("lease-renew", () => {
|
|
70
|
+
const row = this.database.prepare(`
|
|
71
|
+
UPDATE resource_leases
|
|
72
|
+
SET expires_at = ?
|
|
73
|
+
WHERE resource_type = ? AND resource_key = ? AND owner_id = ?
|
|
74
|
+
AND fencing_token = ? AND expires_at > ?
|
|
75
|
+
RETURNING resource_type, resource_key, owner_id, fencing_token, acquired_at, expires_at
|
|
76
|
+
`).get(expiresAt, lease.resourceType, lease.resourceKey, lease.ownerId, lease.fencingToken, renewedAt);
|
|
77
|
+
return row ? leaseFromRow(row) : undefined;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
isHeld(lease, at) {
|
|
81
|
+
if (!Number.isSafeInteger(at) || at < 0)
|
|
82
|
+
return false;
|
|
83
|
+
return this.database.prepare(`
|
|
84
|
+
SELECT 1 AS held FROM resource_leases
|
|
85
|
+
WHERE resource_type = ? AND resource_key = ? AND owner_id = ?
|
|
86
|
+
AND fencing_token = ? AND expires_at > ?
|
|
87
|
+
`).get(lease.resourceType, lease.resourceKey, lease.ownerId, lease.fencingToken, at) !== undefined;
|
|
88
|
+
}
|
|
89
|
+
release(lease) {
|
|
90
|
+
return this.writes.execute("lease-release", () => {
|
|
91
|
+
const result = this.database.prepare(`
|
|
92
|
+
DELETE FROM resource_leases
|
|
93
|
+
WHERE resource_type = ? AND resource_key = ? AND owner_id = ? AND fencing_token = ?
|
|
94
|
+
`).run(lease.resourceType, lease.resourceKey, lease.ownerId, lease.fencingToken);
|
|
95
|
+
return result.changes === 1;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
get(resourceType, resourceKey) {
|
|
99
|
+
if (!validIdentity(resourceType) || !validIdentity(resourceKey))
|
|
100
|
+
return undefined;
|
|
101
|
+
const row = this.database.prepare(`
|
|
102
|
+
SELECT resource_type, resource_key, owner_id, fencing_token, acquired_at, expires_at
|
|
103
|
+
FROM resource_leases WHERE resource_type = ? AND resource_key = ?
|
|
104
|
+
`).get(resourceType, resourceKey);
|
|
105
|
+
return row ? leaseFromRow(row) : undefined;
|
|
106
|
+
}
|
|
107
|
+
deleteExpired(expiredAt) {
|
|
108
|
+
if (!Number.isSafeInteger(expiredAt) || expiredAt < 0)
|
|
109
|
+
return 0;
|
|
110
|
+
return this.writes.execute("lease-delete-expired", () => Number(this.database.prepare("DELETE FROM resource_leases WHERE expires_at <= ?").run(expiredAt).changes));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=lease-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lease-repository.js","sourceRoot":"","sources":["../../../src/persistence/repositories/lease-repository.ts"],"names":[],"mappings":"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"}
|