@farukada/aws-langgraph-dynamodb-ts 0.8.0 → 0.9.0
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 +1 -1
- package/dist/checkpointer/actions/list.d.ts.map +1 -1
- package/dist/checkpointer/actions/list.js +7 -0
- package/dist/checkpointer/actions/list.js.map +1 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
- package/dist/checkpointer/actions/put-writes.js +2 -1
- package/dist/checkpointer/actions/put-writes.js.map +1 -1
- package/dist/checkpointer/internal/item-reader.d.ts +14 -0
- package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-reader.js +15 -1
- package/dist/checkpointer/internal/item-reader.js.map +1 -1
- package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-writer.js +2 -1
- package/dist/checkpointer/internal/item-writer.js.map +1 -1
- package/dist/checkpointer/internal/special-write-cas.d.ts +30 -0
- package/dist/checkpointer/internal/special-write-cas.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-cas.js +104 -0
- package/dist/checkpointer/internal/special-write-cas.js.map +1 -0
- package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -12
- package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -1
- package/dist/checkpointer/internal/special-write-cleanup.js +31 -86
- package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -1
- package/dist/checkpointer/internal/special-write-verify.d.ts +54 -0
- package/dist/checkpointer/internal/special-write-verify.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-verify.js +65 -0
- package/dist/checkpointer/internal/special-write-verify.js.map +1 -0
- package/dist/checkpointer/internal/write-guard.d.ts +0 -8
- package/dist/checkpointer/internal/write-guard.d.ts.map +1 -1
- package/dist/checkpointer/internal/write-guard.js +0 -9
- package/dist/checkpointer/internal/write-guard.js.map +1 -1
- package/dist/checkpointer/internal/write-index.d.ts +8 -0
- package/dist/checkpointer/internal/write-index.d.ts.map +1 -1
- package/dist/checkpointer/internal/write-index.js +6 -2
- package/dist/checkpointer/internal/write-index.js.map +1 -1
- package/dist/checkpointer/types.d.ts +6 -0
- package/dist/checkpointer/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +15 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +16 -1
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/dynamodb/conditional-put.d.ts +51 -0
- package/dist/shared/dynamodb/conditional-put.d.ts.map +1 -0
- package/dist/shared/dynamodb/conditional-put.js +59 -0
- package/dist/shared/dynamodb/conditional-put.js.map +1 -0
- package/dist/shared/logging/redaction.d.ts +5 -1
- package/dist/shared/logging/redaction.d.ts.map +1 -1
- package/dist/shared/logging/redaction.js.map +1 -1
- package/dist/shared/logging/secret-patterns.d.ts +33 -0
- package/dist/shared/logging/secret-patterns.d.ts.map +1 -1
- package/dist/shared/logging/secret-patterns.js +35 -2
- package/dist/shared/logging/secret-patterns.js.map +1 -1
- package/dist/store/actions/put.d.ts.map +1 -1
- package/dist/store/actions/put.js +4 -16
- package/dist/store/actions/put.js.map +1 -1
- package/dist/store/internal/backend-search.d.ts.map +1 -1
- package/dist/store/internal/backend-search.js +2 -1
- package/dist/store/internal/backend-search.js.map +1 -1
- package/dist/store/internal/filter.d.ts.map +1 -1
- package/dist/store/internal/filter.js +25 -5
- package/dist/store/internal/filter.js.map +1 -1
- package/dist/store/internal/index-reconcile.d.ts.map +1 -1
- package/dist/store/internal/index-reconcile.js +8 -2
- package/dist/store/internal/index-reconcile.js.map +1 -1
- package/dist/store/internal/item-mapper.d.ts +1 -0
- package/dist/store/internal/item-mapper.d.ts.map +1 -1
- package/dist/store/internal/item-mapper.js +2 -0
- package/dist/store/internal/item-mapper.js.map +1 -1
- package/dist/store/internal/overwrite-swap.d.ts +33 -0
- package/dist/store/internal/overwrite-swap.d.ts.map +1 -0
- package/dist/store/internal/overwrite-swap.js +62 -0
- package/dist/store/internal/overwrite-swap.js.map +1 -0
- package/dist/store/internal/persist.d.ts +22 -6
- package/dist/store/internal/persist.d.ts.map +1 -1
- package/dist/store/internal/persist.js +42 -17
- package/dist/store/internal/persist.js.map +1 -1
- package/dist/store/internal/read-existing.d.ts +19 -0
- package/dist/store/internal/read-existing.d.ts.map +1 -0
- package/dist/store/internal/read-existing.js +29 -0
- package/dist/store/internal/read-existing.js.map +1 -0
- package/dist/store/internal/score-direction.d.ts +32 -0
- package/dist/store/internal/score-direction.d.ts.map +1 -0
- package/dist/store/internal/score-direction.js +39 -0
- package/dist/store/internal/score-direction.js.map +1 -0
- package/dist/store/internal/setup.d.ts +2 -0
- package/dist/store/internal/setup.d.ts.map +1 -1
- package/dist/store/internal/setup.js +41 -1
- package/dist/store/internal/setup.js.map +1 -1
- package/dist/store/internal/write-verify.d.ts +20 -7
- package/dist/store/internal/write-verify.d.ts.map +1 -1
- package/dist/store/internal/write-verify.js +22 -9
- package/dist/store/internal/write-verify.js.map +1 -1
- package/dist/store/types.d.ts +16 -0
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/vector-backend.d.ts +6 -6
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VECTOR_SCORE_DIRECTIONS = void 0;
|
|
4
|
+
exports.toRelevanceScores = toRelevanceScores;
|
|
5
|
+
/** Every direction {@link toRelevanceScores} recognises, for validating input. */
|
|
6
|
+
exports.VECTOR_SCORE_DIRECTIONS = ['relevance', 'distance'];
|
|
7
|
+
/**
|
|
8
|
+
* Normalise a backend's matches to the relevance direction upstream
|
|
9
|
+
* `SearchItem.score` documents — "higher scores indicate better matches",
|
|
10
|
+
* typically a cosine similarity between -1 and 1.
|
|
11
|
+
*
|
|
12
|
+
* A distance is converted by **negation** rather than `1 / (1 + d)`: negation
|
|
13
|
+
* is monotone over the whole real line, needs no non-negativity precondition,
|
|
14
|
+
* and is exactly invertible, so the original distance is simply `-score`. A
|
|
15
|
+
* reciprocal would silently imply a different `(0, 1]` scale and is undefined
|
|
16
|
+
* at `d === -1`. Negative scores are already in range for this contract, so
|
|
17
|
+
* nothing is lost by producing them.
|
|
18
|
+
*
|
|
19
|
+
* Results are re-sorted after conversion so a backend that returns its matches
|
|
20
|
+
* in some other order still ranks correctly. Anything that is not `'distance'`
|
|
21
|
+
* is returned untouched — its own order stays authoritative, exactly as before.
|
|
22
|
+
*
|
|
23
|
+
* The test is for `'distance'` rather than against `'relevance'` on purpose:
|
|
24
|
+
* converting is the destructive branch, so only the exact value that asks for
|
|
25
|
+
* it may reach it. Testing the other way round made every unrecognised
|
|
26
|
+
* string — `'Distance'`, a typo, a value read from a config file — reverse the
|
|
27
|
+
* ranking silently, with no error and no warning (the ascending-score warning
|
|
28
|
+
* lives downstream of this conversion, so it could never fire). `setUpStore`
|
|
29
|
+
* rejects such a value outright; this keeps the failure harmless for any
|
|
30
|
+
* caller that reaches the function some other way.
|
|
31
|
+
*/
|
|
32
|
+
function toRelevanceScores(matches, direction) {
|
|
33
|
+
if (direction !== 'distance')
|
|
34
|
+
return matches;
|
|
35
|
+
return matches
|
|
36
|
+
.map((match) => ({ ...match, score: -match.score }))
|
|
37
|
+
.sort((left, right) => right.score - left.score);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=score-direction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score-direction.js","sourceRoot":"","sources":["../../../src/store/internal/score-direction.ts"],"names":[],"mappings":";;;;AAKA,kFAAkF;AACrE,QAAA,uBAAuB,GAAoC,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,2BACE,OAAsB,EACtB,SAA+B;IAE/B,IAAI,SAAS,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC;IAC7C,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SACnD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -7,6 +7,7 @@ import { type Logger } from '../../shared/logging/logger';
|
|
|
7
7
|
import type { TtlOption } from '../../shared/validation/ttl';
|
|
8
8
|
import type { DynamoDBStoreOptions } from '../types';
|
|
9
9
|
import type { VectorBackend } from '../vector-backend';
|
|
10
|
+
import { type VectorScoreDirection } from './score-direction';
|
|
10
11
|
/** Resolved collaborators shared by every store action. */
|
|
11
12
|
export interface StoreContext {
|
|
12
13
|
client: DynamoDBDocument;
|
|
@@ -18,6 +19,7 @@ export interface StoreContext {
|
|
|
18
19
|
logger: Logger;
|
|
19
20
|
index?: IndexConfig;
|
|
20
21
|
vectorBackend?: VectorBackend;
|
|
22
|
+
vectorScoreDirection: VectorScoreDirection;
|
|
21
23
|
maxSearchCandidates: number;
|
|
22
24
|
maxScanItems: number;
|
|
23
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAQ9D,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAA2B,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEvF,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,OAAO,CAAC;CACrB;AA4CD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,UAAU,CAoCpE"}
|
|
@@ -8,6 +8,43 @@ const constants_1 = require("../../shared/constants");
|
|
|
8
8
|
const client_1 = require("../../shared/dynamodb/client");
|
|
9
9
|
const errors_1 = require("../../shared/errors/errors");
|
|
10
10
|
const logger_1 = require("../../shared/logging/logger");
|
|
11
|
+
const score_direction_1 = require("./score-direction");
|
|
12
|
+
/**
|
|
13
|
+
* Reject an `index` that cannot actually embed. `IndexConfig` mandates
|
|
14
|
+
* `embeddings`, but a JavaScript caller can omit it or pass the wrong shape,
|
|
15
|
+
* and the failure then surfaced as a raw `TypeError` deep inside the first
|
|
16
|
+
* `put()`/`search()` rather than this library's typed error at construction.
|
|
17
|
+
*
|
|
18
|
+
* Only `embeddings` is checked. `dims` is part of the upstream type but is
|
|
19
|
+
* never read anywhere in this package, so rejecting a configuration over it
|
|
20
|
+
* would break working callers for no benefit.
|
|
21
|
+
*/
|
|
22
|
+
function assertUsableIndex(index) {
|
|
23
|
+
if (!index)
|
|
24
|
+
return;
|
|
25
|
+
const embeddings = index.embeddings;
|
|
26
|
+
if (typeof embeddings?.embedQuery !== 'function') {
|
|
27
|
+
throw new errors_1.ValidationError('`index.embeddings` must be an Embeddings implementation exposing embedQuery(); ' +
|
|
28
|
+
'without one no embedding can be computed for put() or search()', 'index');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Reject a `vectorScoreDirection` outside the declared union.
|
|
33
|
+
*
|
|
34
|
+
* {@link toRelevanceScores} treats anything it does not recognise as a no-op —
|
|
35
|
+
* the only safe default, since guessing would invert a ranking — so a mistyped
|
|
36
|
+
* or config-file-sourced value would otherwise leave a distance backend ranked
|
|
37
|
+
* backwards with no error and no warning anywhere. Same premise as
|
|
38
|
+
* {@link assertUsableIndex}: a JavaScript caller can pass a string the type
|
|
39
|
+
* never admits.
|
|
40
|
+
*/
|
|
41
|
+
function assertScoreDirection(direction) {
|
|
42
|
+
if (direction === undefined || score_direction_1.VECTOR_SCORE_DIRECTIONS.includes(direction))
|
|
43
|
+
return;
|
|
44
|
+
throw new errors_1.ValidationError(`vectorScoreDirection must be one of ${score_direction_1.VECTOR_SCORE_DIRECTIONS.join(' | ')}; received ` +
|
|
45
|
+
`${JSON.stringify(direction)}, which would be left in the backend's own direction and ` +
|
|
46
|
+
'could rank a distance backend backwards', 'vectorScoreDirection');
|
|
47
|
+
}
|
|
11
48
|
/**
|
|
12
49
|
* Resolve the client, optional S3 offloader, serializer, and index config.
|
|
13
50
|
*
|
|
@@ -21,10 +58,12 @@ const logger_1 = require("../../shared/logging/logger");
|
|
|
21
58
|
*/
|
|
22
59
|
function setUpStore(options) {
|
|
23
60
|
if (options.vectorBackend && !options.index) {
|
|
24
|
-
throw new errors_1.ValidationError('vectorBackend requires a configured `index` (
|
|
61
|
+
throw new errors_1.ValidationError('vectorBackend requires a configured `index` (embeddings); without one no embedding ' +
|
|
25
62
|
'is computed, every put would clear the item vector, and search would silently return ' +
|
|
26
63
|
'unranked, score-less results', 'vectorBackend');
|
|
27
64
|
}
|
|
65
|
+
assertUsableIndex(options.index);
|
|
66
|
+
assertScoreDirection(options.vectorScoreDirection);
|
|
28
67
|
const resolved = (0, client_1.resolveDynamoDBClient)(options);
|
|
29
68
|
return {
|
|
30
69
|
context: {
|
|
@@ -42,6 +81,7 @@ function setUpStore(options) {
|
|
|
42
81
|
logger: (0, logger_1.resolveLogger)(options.logger),
|
|
43
82
|
index: options.index,
|
|
44
83
|
vectorBackend: options.vectorBackend,
|
|
84
|
+
vectorScoreDirection: options.vectorScoreDirection ?? 'relevance',
|
|
45
85
|
maxSearchCandidates: options.maxSearchCandidates ?? constants_1.DEFAULT_MAX_SEARCH_CANDIDATES,
|
|
46
86
|
maxScanItems: options.maxScanItems ?? constants_1.MAX_TOTAL_ITEMS_IN_MEMORY,
|
|
47
87
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/store/internal/setup.ts"],"names":[],"mappings":";;;AAMA,8DAA2D;AAC3D,yDAAuE;AACvE,+DAA8D;AAC9D,sDAIgC;AAChC,yDAAqE;AACrE,uDAA6D;AAC7D,wDAAyE;AAIzE,uDAAuF;AAyBvF;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,KAAmB;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,MAAM,UAAU,GAAoC,KAAK,CAAC,UAAU,CAAC;IACrE,IAAI,OAAO,UAAU,EAAE,UAAU,KAAK,UAAU,EAAE,CAAC;QACjD,MAAM,IAAI,wBAAe,CACvB,iFAAiF;YAC/E,gEAAgE,EAClE,OAAO,CACR,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,oBAAoB,CAAC,SAAgC;IAC5D,IAAI,SAAS,KAAK,SAAS,IAAI,yCAAuB,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO;IACnF,MAAM,IAAI,wBAAe,CACvB,uCAAuC,yCAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa;QACrF,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,2DAA2D;QACvF,yCAAyC,EAC3C,sBAAsB,CACvB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,oBAA2B,OAA6B;IACtD,IAAI,OAAO,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5C,MAAM,IAAI,wBAAe,CACvB,qFAAqF;YACnF,uFAAuF;YACvF,8BAA8B,EAChC,eAAe,CAChB,CAAC;IACJ,CAAC;IACD,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACjC,oBAAoB,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAChD,OAAO;QACL,OAAO,EAAE;YACP,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,uBAAU;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,SAAS,EAAE,OAAO,CAAC,EAAE;gBACnB,CAAC,CAAC,IAAI,uBAAW,CAAC;oBACd,GAAG,OAAO,CAAC,EAAE;oBACb,SAAS,EACP,OAAO,CAAC,EAAE,CAAC,SAAS,IAAI,IAAA,gCAAuB,EAAC,iCAAqB,EAAE,OAAO,CAAC;iBAClF,CAAC;gBACJ,CAAC,CAAC,SAAS;YACb,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,IAAA,sBAAa,EAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,IAAI,WAAW;YACjE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,IAAI,yCAA6B;YACjF,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,qCAAyB;SAChE;QACD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC"}
|
|
@@ -5,20 +5,33 @@ export declare function isRetryExhausted(error: Error): boolean;
|
|
|
5
5
|
* True when the row is confirmed absent — used to resolve an ambiguous
|
|
6
6
|
* retry-exhausted *delete*, where the delete may well have landed server-side
|
|
7
7
|
* and only its acknowledgement was lost. A failure reading this is not treated
|
|
8
|
-
* as confirmation
|
|
8
|
+
* as confirmation, which is fail-safe here: the caller only rethrows, and
|
|
9
|
+
* nothing is deleted on the strength of a `false`.
|
|
9
10
|
*/
|
|
10
11
|
export declare function rowIsAbsent(context: StoreContext, key: {
|
|
11
12
|
PK: string;
|
|
12
13
|
SK: string;
|
|
13
14
|
}): Promise<boolean>;
|
|
15
|
+
/** What a post-failure verification read could establish about a write. */
|
|
16
|
+
export type WriteVerdict = 'landed' | 'not-landed' | 'unverified';
|
|
14
17
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* Read `record`'s row back to establish what an ambiguous write actually did.
|
|
19
|
+
*
|
|
20
|
+
* - `'landed'`: the row holds `expectedS3Key`, so the write committed
|
|
21
|
+
* server-side and only its acknowledgement was lost.
|
|
22
|
+
* - `'not-landed'`: the row was read and holds something else, or nothing, so
|
|
23
|
+
* this call's own nonced upload is dead and safe to delete.
|
|
24
|
+
* - `'unverified'`: the read itself failed, so nothing is established.
|
|
25
|
+
*
|
|
26
|
+
* The third answer used to be folded into the second as a plain `false`, which
|
|
27
|
+
* made a partition that blocked both the put and this read delete the object a
|
|
28
|
+
* possibly-live row points at — permanently breaking every later read of that
|
|
29
|
+
* item. Only a *confirmed* non-commit may delete the new upload: leaking one
|
|
30
|
+
* object (reclaimed by `ensureS3LifecycleRule`) is recoverable, stranding a
|
|
31
|
+
* live row is not.
|
|
19
32
|
*/
|
|
20
|
-
export declare function
|
|
33
|
+
export declare function verifyWriteLanded(context: StoreContext, record: {
|
|
21
34
|
PK: string;
|
|
22
35
|
SK: string;
|
|
23
|
-
}, expectedS3Key: string): Promise<
|
|
36
|
+
}, expectedS3Key: string): Promise<WriteVerdict>;
|
|
24
37
|
//# sourceMappingURL=write-verify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-verify.d.ts","sourceRoot":"","sources":["../../../src/store/internal/write-verify.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAEtD;AAED
|
|
1
|
+
{"version":3,"file":"write-verify.d.ts","sourceRoot":"","sources":["../../../src/store/internal/write-verify.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,YAAY,EACrB,GAAG,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,OAAO,CAAC,OAAO,CAAC,CAelB;AAED,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAClC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,CAkBvB"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isRetryExhausted = isRetryExhausted;
|
|
4
4
|
exports.rowIsAbsent = rowIsAbsent;
|
|
5
|
-
exports.
|
|
5
|
+
exports.verifyWriteLanded = verifyWriteLanded;
|
|
6
6
|
const codec_1 = require("../../shared/codec/codec");
|
|
7
7
|
const retry_1 = require("../../shared/dynamodb/retry");
|
|
8
8
|
/** True when `error` is a {@link RetryExhaustedError} — by name, not `instanceof` (banned repo-wide). */
|
|
@@ -13,7 +13,8 @@ function isRetryExhausted(error) {
|
|
|
13
13
|
* True when the row is confirmed absent — used to resolve an ambiguous
|
|
14
14
|
* retry-exhausted *delete*, where the delete may well have landed server-side
|
|
15
15
|
* and only its acknowledgement was lost. A failure reading this is not treated
|
|
16
|
-
* as confirmation
|
|
16
|
+
* as confirmation, which is fail-safe here: the caller only rethrows, and
|
|
17
|
+
* nothing is deleted on the strength of a `false`.
|
|
17
18
|
*/
|
|
18
19
|
async function rowIsAbsent(context, key) {
|
|
19
20
|
try {
|
|
@@ -31,12 +32,22 @@ async function rowIsAbsent(context, key) {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Read `record`'s row back to establish what an ambiguous write actually did.
|
|
36
|
+
*
|
|
37
|
+
* - `'landed'`: the row holds `expectedS3Key`, so the write committed
|
|
38
|
+
* server-side and only its acknowledgement was lost.
|
|
39
|
+
* - `'not-landed'`: the row was read and holds something else, or nothing, so
|
|
40
|
+
* this call's own nonced upload is dead and safe to delete.
|
|
41
|
+
* - `'unverified'`: the read itself failed, so nothing is established.
|
|
42
|
+
*
|
|
43
|
+
* The third answer used to be folded into the second as a plain `false`, which
|
|
44
|
+
* made a partition that blocked both the put and this read delete the object a
|
|
45
|
+
* possibly-live row points at — permanently breaking every later read of that
|
|
46
|
+
* item. Only a *confirmed* non-commit may delete the new upload: leaking one
|
|
47
|
+
* object (reclaimed by `ensureS3LifecycleRule`) is recoverable, stranding a
|
|
48
|
+
* live row is not.
|
|
38
49
|
*/
|
|
39
|
-
async function
|
|
50
|
+
async function verifyWriteLanded(context, record, expectedS3Key) {
|
|
40
51
|
try {
|
|
41
52
|
const result = await (0, retry_1.withDynamoDBRetry)(() => context.client.get({
|
|
42
53
|
TableName: context.tableName,
|
|
@@ -46,10 +57,12 @@ async function writeLandedAt(context, record, expectedS3Key) {
|
|
|
46
57
|
ExpressionAttributeNames: { '#v': 'value' },
|
|
47
58
|
}));
|
|
48
59
|
const value = result.Item?.value;
|
|
49
|
-
return value?.location === codec_1.PayloadLocation.S3 && value.s3Key === expectedS3Key
|
|
60
|
+
return value?.location === codec_1.PayloadLocation.S3 && value.s3Key === expectedS3Key
|
|
61
|
+
? 'landed'
|
|
62
|
+
: 'not-landed';
|
|
50
63
|
}
|
|
51
64
|
catch {
|
|
52
|
-
return
|
|
65
|
+
return 'unverified';
|
|
53
66
|
}
|
|
54
67
|
}
|
|
55
68
|
//# sourceMappingURL=write-verify.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-verify.js","sourceRoot":"","sources":["../../../src/store/internal/write-verify.ts"],"names":[],"mappings":";;;;;AAAA,oDAAmF;AACnF,uDAAgE;AAGhE,yGAAyG;AACzG,0BAAiC,KAAY;IAC3C,OAAO,KAAK,CAAC,IAAI,KAAK,qBAAqB,CAAC;AAC9C,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"write-verify.js","sourceRoot":"","sources":["../../../src/store/internal/write-verify.ts"],"names":[],"mappings":";;;;;AAAA,oDAAmF;AACnF,uDAAgE;AAGhE,yGAAyG;AACzG,0BAAiC,KAAY;IAC3C,OAAO,KAAK,CAAC,IAAI,KAAK,qBAAqB,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACI,KAAK,sBACV,OAAqB,EACrB,GAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC1C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;YACjB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,GAAG,EAAE,GAAG;YACR,cAAc,EAAE,IAAI;YACpB,oBAAoB,EAAE,IAAI;YAC1B,wBAAwB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;SAC5C,CAAC,CACH,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD;;;;;;;;;;;;;;;GAeG;AACI,KAAK,4BACV,OAAqB,EACrB,MAAkC,EAClC,aAAqB;IAErB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC1C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;YACjB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;YACrC,cAAc,EAAE,IAAI;YACpB,oBAAoB,EAAE,IAAI;YAC1B,wBAAwB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;SAC5C,CAAC,CACH,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,KAAsC,CAAC;QAClE,OAAO,KAAK,EAAE,QAAQ,KAAK,uBAAe,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,KAAK,aAAa;YAC5E,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,YAAY,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,YAAY,CAAC;IACtB,CAAC;AACH,CAAC"}
|
package/dist/store/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IndexConfig, SerializerProtocol } from '@langchain/langgraph-checkpoint';
|
|
2
2
|
import type { PayloadDescriptor } from '../shared/codec/codec';
|
|
3
3
|
import type { BaseAdapterOptions, CodecOptions } from '../shared/options';
|
|
4
|
+
import type { VectorScoreDirection } from './internal/score-direction';
|
|
4
5
|
import type { VectorBackend } from './vector-backend';
|
|
5
6
|
/** Options for {@link DynamoDBStore}. */
|
|
6
7
|
export type DynamoDBStoreOptions = BaseAdapterOptions & CodecOptions & {
|
|
@@ -14,6 +15,15 @@ export type DynamoDBStoreOptions = BaseAdapterOptions & CodecOptions & {
|
|
|
14
15
|
maxSearchCandidates?: number;
|
|
15
16
|
/** Cap on items scanned into memory during a plain (non-semantic) search before ResultTruncatedError. Defaults to MAX_TOTAL_ITEMS_IN_MEMORY. */
|
|
16
17
|
maxScanItems?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Direction of the score a `vectorBackend` returns. `'relevance'` (the
|
|
20
|
+
* default) forwards it unchanged; `'distance'` negates and re-sorts, so a
|
|
21
|
+
* distance-native backend (S3 Vectors, FAISS L2, pgvector `<->`) satisfies
|
|
22
|
+
* the higher-is-better contract without the caller wrapping it. Any other
|
|
23
|
+
* value is rejected at construction with a `ValidationError` rather than
|
|
24
|
+
* silently ranking one direction as the other.
|
|
25
|
+
*/
|
|
26
|
+
vectorScoreDirection?: VectorScoreDirection;
|
|
17
27
|
};
|
|
18
28
|
/** The DynamoDB item backing a single stored value. */
|
|
19
29
|
export interface StoreItemRecord {
|
|
@@ -26,5 +36,11 @@ export interface StoreItemRecord {
|
|
|
26
36
|
updatedAt: string;
|
|
27
37
|
embedding?: number[];
|
|
28
38
|
ttl?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Revision token, rewritten on every put. Pins the compare-and-swap that
|
|
41
|
+
* keeps two concurrent overwrites from both deleting the same superseded S3
|
|
42
|
+
* object. Optional: rows written before 0.9.0 carry none.
|
|
43
|
+
*/
|
|
44
|
+
rev?: string;
|
|
29
45
|
}
|
|
30
46
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GACnD,YAAY,GAAG;IACb,0EAA0E;IAC1E,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,sEAAsE;IACtE,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,mFAAmF;IACnF,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gJAAgJ;IAChJ,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GACnD,YAAY,GAAG;IACb,0EAA0E;IAC1E,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,sEAAsE;IACtE,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,mFAAmF;IACnF,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gJAAgJ;IAChJ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,CAAC;AAEJ,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -7,12 +7,12 @@ export interface VectorMatch {
|
|
|
7
7
|
* upstream `SearchItem.score`, which this value is forwarded to verbatim.
|
|
8
8
|
*
|
|
9
9
|
* A backend whose native output is a *distance* (S3 Vectors, FAISS L2,
|
|
10
|
-
* pgvector's `<->`)
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* pgvector's `<->`) has two options: convert before returning, or declare
|
|
11
|
+
* `vectorScoreDirection: 'distance'` on the store, which negates and
|
|
12
|
+
* re-sorts for you. Forwarding an unconverted distance without declaring it
|
|
13
|
+
* yields results ordered correctly but scored backwards, which silently
|
|
14
|
+
* breaks any caller that thresholds or displays the number; that case is
|
|
15
|
+
* warned about but never reordered.
|
|
16
16
|
*/
|
|
17
17
|
score: number;
|
|
18
18
|
}
|
package/package.json
CHANGED