@mcp-abap-adt/llm-agent-libs 25.0.0 → 27.0.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 +11 -14
- package/dist/agent.d.ts +2 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +6 -5
- package/dist/agent.js.map +1 -1
- package/dist/builder.d.ts +36 -14
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +472 -348
- package/dist/builder.js.map +1 -1
- package/dist/config/config-watcher.d.ts.map +1 -1
- package/dist/config/config-watcher.js +9 -5
- package/dist/config/config-watcher.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/vectorize-mcp-tools.d.ts.map +1 -1
- package/dist/mcp/vectorize-mcp-tools.js +21 -2
- package/dist/mcp/vectorize-mcp-tools.js.map +1 -1
- package/dist/pipeline/handlers/history-upsert.d.ts.map +1 -1
- package/dist/pipeline/handlers/history-upsert.js +8 -1
- package/dist/pipeline/handlers/history-upsert.js.map +1 -1
- package/dist/pipeline/handlers/rag-query.d.ts +1 -1
- package/dist/pipeline/handlers/rag-query.d.ts.map +1 -1
- package/dist/pipeline/handlers/rag-query.js +10 -2
- package/dist/pipeline/handlers/rag-query.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/types.d.ts +7 -0
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/types.js +85 -14
- package/dist/plugins/types.js.map +1 -1
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +8 -5
- package/dist/providers.js.map +1 -1
- package/dist/session/session-graph-factory.d.ts +49 -16
- package/dist/session/session-graph-factory.d.ts.map +1 -1
- package/dist/session/session-graph-factory.js +140 -62
- package/dist/session/session-graph-factory.js.map +1 -1
- package/dist/skills/plugin-host/qdrant-store.d.ts +6 -1
- package/dist/skills/plugin-host/qdrant-store.d.ts.map +1 -1
- package/dist/skills/plugin-host/qdrant-store.js +7 -7
- package/dist/skills/plugin-host/qdrant-store.js.map +1 -1
- package/dist/util/stop-all.d.ts +19 -0
- package/dist/util/stop-all.d.ts.map +1 -0
- package/dist/util/stop-all.js +30 -0
- package/dist/util/stop-all.js.map +1 -0
- package/package.json +5 -35
- package/dist/coordinator/dag/reviewer-error-strategy.d.ts +0 -15
- package/dist/coordinator/dag/reviewer-error-strategy.d.ts.map +0 -1
- package/dist/coordinator/dag/reviewer-error-strategy.js +0 -38
- package/dist/coordinator/dag/reviewer-error-strategy.js.map +0 -1
- package/dist/skills/plugin-host/chunker.test.d.ts +0 -2
- package/dist/skills/plugin-host/chunker.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/chunker.test.js +0 -31
- package/dist/skills/plugin-host/chunker.test.js.map +0 -1
- package/dist/skills/plugin-host/compatible-skills-rag.test.d.ts +0 -2
- package/dist/skills/plugin-host/compatible-skills-rag.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/compatible-skills-rag.test.js +0 -174
- package/dist/skills/plugin-host/compatible-skills-rag.test.js.map +0 -1
- package/dist/skills/plugin-host/http-marketplace-source.test.d.ts +0 -2
- package/dist/skills/plugin-host/http-marketplace-source.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/http-marketplace-source.test.js +0 -71
- package/dist/skills/plugin-host/http-marketplace-source.test.js.map +0 -1
- package/dist/skills/plugin-host/in-memory-store.test.d.ts +0 -2
- package/dist/skills/plugin-host/in-memory-store.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/in-memory-store.test.js +0 -100
- package/dist/skills/plugin-host/in-memory-store.test.js.map +0 -1
- package/dist/skills/plugin-host/marketplace-adapter.test.d.ts +0 -2
- package/dist/skills/plugin-host/marketplace-adapter.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/marketplace-adapter.test.js +0 -46
- package/dist/skills/plugin-host/marketplace-adapter.test.js.map +0 -1
- package/dist/skills/plugin-host/qdrant-store.test.d.ts +0 -2
- package/dist/skills/plugin-host/qdrant-store.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/qdrant-store.test.js +0 -396
- package/dist/skills/plugin-host/qdrant-store.test.js.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.d.ts +0 -2
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.js +0 -520
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.js.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.d.ts +0 -2
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.js +0 -328
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.js.map +0 -1
- package/dist/skills/plugin-host/skills-rag-source.test.d.ts +0 -2
- package/dist/skills/plugin-host/skills-rag-source.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/skills-rag-source.test.js +0 -69
- package/dist/skills/plugin-host/skills-rag-source.test.js.map +0 -1
- package/dist/skills/plugin-host/source-strategies.test.d.ts +0 -2
- package/dist/skills/plugin-host/source-strategies.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/source-strategies.test.js +0 -52
- package/dist/skills/plugin-host/source-strategies.test.js.map +0 -1
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { test } from 'node:test';
|
|
3
|
-
import { makeHttpMarketplaceSource } from './http-marketplace-source.js';
|
|
4
|
-
const SKILL_MD = `---\nname: s\ndescription: d\n---\n## H\nbody`;
|
|
5
|
-
test('fetches manifest + SKILL.md via injected transport, builds ingest result, zero FS', async () => {
|
|
6
|
-
const src = makeHttpMarketplaceSource({
|
|
7
|
-
source: 'vendor',
|
|
8
|
-
enabled: ['p1'],
|
|
9
|
-
transport: {
|
|
10
|
-
async listPlugins() {
|
|
11
|
-
return [{ plugin: 'p1', version: '1', skills: ['s'] }];
|
|
12
|
-
},
|
|
13
|
-
async fetchSkillMd(plugin, skill) {
|
|
14
|
-
assert.equal(plugin, 'p1');
|
|
15
|
-
assert.equal(skill, 's');
|
|
16
|
-
return SKILL_MD;
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
chunk: { maxChars: 1000 },
|
|
20
|
-
});
|
|
21
|
-
const res = await src.acquire();
|
|
22
|
-
assert.deepEqual(res.collections.map((c) => c.group), ['p1']);
|
|
23
|
-
assert.ok(res.records.length >= 1);
|
|
24
|
-
});
|
|
25
|
-
test('enabled "*" loads every offered plugin; empty enabled is a caller error', async () => {
|
|
26
|
-
const transport = {
|
|
27
|
-
async listPlugins() {
|
|
28
|
-
return [
|
|
29
|
-
{ plugin: 'a', version: '1', skills: ['s'] },
|
|
30
|
-
{ plugin: 'b', version: '1', skills: ['s'] },
|
|
31
|
-
];
|
|
32
|
-
},
|
|
33
|
-
async fetchSkillMd() {
|
|
34
|
-
return SKILL_MD;
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
const all = makeHttpMarketplaceSource({
|
|
38
|
-
source: 'v',
|
|
39
|
-
enabled: ['*'],
|
|
40
|
-
transport,
|
|
41
|
-
chunk: { maxChars: 1000 },
|
|
42
|
-
});
|
|
43
|
-
assert.equal((await all.acquire()).collections.length, 2);
|
|
44
|
-
assert.throws(() => makeHttpMarketplaceSource({
|
|
45
|
-
source: 'v',
|
|
46
|
-
enabled: [],
|
|
47
|
-
transport,
|
|
48
|
-
chunk: { maxChars: 1000 },
|
|
49
|
-
}), /enabled/);
|
|
50
|
-
});
|
|
51
|
-
test('only the enabled subset of offered plugins is acquired', async () => {
|
|
52
|
-
const src = makeHttpMarketplaceSource({
|
|
53
|
-
source: 'v',
|
|
54
|
-
enabled: ['b'],
|
|
55
|
-
transport: {
|
|
56
|
-
async listPlugins() {
|
|
57
|
-
return [
|
|
58
|
-
{ plugin: 'a', version: '1', skills: ['s'] },
|
|
59
|
-
{ plugin: 'b', version: '1', skills: ['s'] },
|
|
60
|
-
];
|
|
61
|
-
},
|
|
62
|
-
async fetchSkillMd() {
|
|
63
|
-
return SKILL_MD;
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
chunk: { maxChars: 1000 },
|
|
67
|
-
});
|
|
68
|
-
const res = await src.acquire();
|
|
69
|
-
assert.deepEqual(res.collections.map((c) => c.group), ['b']);
|
|
70
|
-
});
|
|
71
|
-
//# sourceMappingURL=http-marketplace-source.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http-marketplace-source.test.js","sourceRoot":"","sources":["../../../src/skills/plugin-host/http-marketplace-source.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAEzE,MAAM,QAAQ,GAAG,+CAA+C,CAAC;AAEjE,IAAI,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;IACnG,MAAM,GAAG,GAAG,yBAAyB,CAAC;QACpC,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,CAAC,IAAI,CAAC;QACf,SAAS,EAAE;YACT,KAAK,CAAC,WAAW;gBACf,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK;gBAC9B,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBACzB,OAAO,QAAQ,CAAC;YAClB,CAAC;SACF;QACD,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC1B,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IAChC,MAAM,CAAC,SAAS,CACd,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EACnC,CAAC,IAAI,CAAC,CACP,CAAC;IACF,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;IACzF,MAAM,SAAS,GAAG;QAChB,KAAK,CAAC,WAAW;YACf,OAAO;gBACL,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE;gBAC5C,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE;aAC7C,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,YAAY;YAChB,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;IACF,MAAM,GAAG,GAAG,yBAAyB,CAAC;QACpC,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,CAAC,GAAG,CAAC;QACd,SAAS;QACT,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC1B,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CACH,yBAAyB,CAAC;QACxB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE;QACX,SAAS;QACT,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC1B,CAAC,EACJ,SAAS,CACV,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;IACxE,MAAM,GAAG,GAAG,yBAAyB,CAAC;QACpC,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,CAAC,GAAG,CAAC;QACd,SAAS,EAAE;YACT,KAAK,CAAC,WAAW;gBACf,OAAO;oBACL,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE;oBAC5C,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE;iBAC7C,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,YAAY;gBAChB,OAAO,QAAQ,CAAC;YAClB,CAAC;SACF;QACD,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC1B,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IAChC,MAAM,CAAC,SAAS,CACd,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EACnC,CAAC,GAAG,CAAC,CACN,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-store.test.d.ts","sourceRoot":"","sources":["../../../src/skills/plugin-host/in-memory-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { test } from 'node:test';
|
|
3
|
-
import { CatalogCasError } from '@mcp-abap-adt/llm-agent';
|
|
4
|
-
import { makeInMemoryStoreProvider } from './in-memory-store.js';
|
|
5
|
-
const MANIFEST = {
|
|
6
|
-
embeddingSpaceId: 'sp',
|
|
7
|
-
dimension: 3,
|
|
8
|
-
retrievalSchemaVersion: 1,
|
|
9
|
-
};
|
|
10
|
-
const rec = (id, group, vec) => ({
|
|
11
|
-
record: {
|
|
12
|
-
id,
|
|
13
|
-
sourceId: 's',
|
|
14
|
-
group,
|
|
15
|
-
name: id,
|
|
16
|
-
retrievalText: id,
|
|
17
|
-
content: `c-${id}`,
|
|
18
|
-
provenance: id,
|
|
19
|
-
},
|
|
20
|
-
vector: vec,
|
|
21
|
-
});
|
|
22
|
-
test('build inactive → publishCatalog activates → query + activeSnapshot from catalog', async () => {
|
|
23
|
-
const p = makeInMemoryStoreProvider();
|
|
24
|
-
const store = p.forGroup('g1');
|
|
25
|
-
const { generation } = await store.beginGeneration();
|
|
26
|
-
// upsert is embedding-agnostic here: the in-memory store accepts pre-vectorised rows via a test seam
|
|
27
|
-
await p._seed(generation, [
|
|
28
|
-
rec('a', 'g1', [1, 0, 0]),
|
|
29
|
-
rec('b', 'g1', [0, 1, 0]),
|
|
30
|
-
]);
|
|
31
|
-
// nothing serves yet
|
|
32
|
-
assert.equal(await store.activeSnapshot(), null);
|
|
33
|
-
const before = await p.readCatalog();
|
|
34
|
-
await p.publishCatalog(before.catalogRevision, [
|
|
35
|
-
{
|
|
36
|
-
collection: { group: 'g1', description: 'd', collection: 'g1' },
|
|
37
|
-
sources: ['s'],
|
|
38
|
-
generation,
|
|
39
|
-
manifest: MANIFEST,
|
|
40
|
-
},
|
|
41
|
-
]);
|
|
42
|
-
const snap = await store.activeSnapshot();
|
|
43
|
-
assert.equal(snap?.revision, generation);
|
|
44
|
-
assert.deepEqual(snap?.manifest, MANIFEST);
|
|
45
|
-
const hits = await store.queryRevision(generation, [1, 0, 0], 1);
|
|
46
|
-
assert.equal(hits[0].record.id, 'a');
|
|
47
|
-
});
|
|
48
|
-
test('publishCatalog CAS rejects a stale expectedRevision with CatalogCasError', async () => {
|
|
49
|
-
const p = makeInMemoryStoreProvider();
|
|
50
|
-
const r0 = (await p.readCatalog()).catalogRevision;
|
|
51
|
-
await p.publishCatalog(r0, []); // bumps to r1
|
|
52
|
-
await assert.rejects(() => p.publishCatalog(r0, []), (e) => e instanceof CatalogCasError);
|
|
53
|
-
});
|
|
54
|
-
test('EXACT retention via lease: a generation pinned by activeSnapshot survives a concurrent reclaim until release', async () => {
|
|
55
|
-
const p = makeInMemoryStoreProvider();
|
|
56
|
-
const backend = p.forGroup('g1');
|
|
57
|
-
const g = await backend.beginGeneration();
|
|
58
|
-
await p._seed(g.generation, [rec('a', 'g1', [1, 0, 0])]);
|
|
59
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, [
|
|
60
|
-
{
|
|
61
|
-
collection: { group: 'g1', description: 'd', collection: 'g1' },
|
|
62
|
-
sources: ['s'],
|
|
63
|
-
generation: g.generation,
|
|
64
|
-
manifest: MANIFEST,
|
|
65
|
-
},
|
|
66
|
-
]);
|
|
67
|
-
// a reader resolves (PINS) the generation...
|
|
68
|
-
const snap = await backend.activeSnapshot();
|
|
69
|
-
// ...then the catalog rotates it out AND a reclaim is attempted (simulating a concurrent load)
|
|
70
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, []); // retire g1
|
|
71
|
-
await p.forGroup('g1').discardGeneration(snap.revision); // reclaim attempt — DEFERRED (leased)
|
|
72
|
-
// the in-flight reader's query STILL succeeds (no read-under-delete):
|
|
73
|
-
assert.equal((await backend.queryRevision(snap.revision, [1, 0, 0], 1)).length, 1);
|
|
74
|
-
// release the lease → the deferred reclaim now runs
|
|
75
|
-
backend.release(snap.revision);
|
|
76
|
-
await assert.rejects(() => backend.queryRevision(snap.revision, [1, 0, 0], 1), /unknown generation/i);
|
|
77
|
-
});
|
|
78
|
-
test('an ACTIVE generation is retained; dropCollection only reclaims a NON-active one', async () => {
|
|
79
|
-
const p = makeInMemoryStoreProvider();
|
|
80
|
-
const g = await p.forGroup('g1').beginGeneration();
|
|
81
|
-
await p._seed(g.generation, [rec('a', 'g1', [1, 0, 0])]);
|
|
82
|
-
const r0 = (await p.readCatalog()).catalogRevision;
|
|
83
|
-
await p.publishCatalog(r0, [
|
|
84
|
-
{
|
|
85
|
-
collection: { group: 'g1', description: 'd', collection: 'g1' },
|
|
86
|
-
sources: ['s'],
|
|
87
|
-
generation: g.generation,
|
|
88
|
-
manifest: MANIFEST,
|
|
89
|
-
},
|
|
90
|
-
]);
|
|
91
|
-
// dropCollection on an ACTIVE collection must NOT delete its served generation
|
|
92
|
-
await p.dropCollection('g1');
|
|
93
|
-
assert.equal((await p.forGroup('g1').queryRevision(g.generation, [1, 0, 0], 1)).length, 1);
|
|
94
|
-
// now retire g1 from the catalog (publish WITHOUT it) → the generation becomes non-active
|
|
95
|
-
const r1 = (await p.readCatalog()).catalogRevision;
|
|
96
|
-
await p.publishCatalog(r1, []);
|
|
97
|
-
await p.dropCollection('g1'); // reclaims the now-non-active generation
|
|
98
|
-
await assert.rejects(() => p.forGroup('g1').queryRevision(g.generation, [1, 0, 0], 1), /unknown generation|not found/i);
|
|
99
|
-
});
|
|
100
|
-
//# sourceMappingURL=in-memory-store.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-store.test.js","sourceRoot":"","sources":["../../../src/skills/plugin-host/in-memory-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace-adapter.test.d.ts","sourceRoot":"","sources":["../../../src/skills/plugin-host/marketplace-adapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { test } from 'node:test';
|
|
3
|
-
import { buildIngestResult } from './marketplace-adapter.js';
|
|
4
|
-
const SKILL_MD = `---\nname: do-thing\ndescription: How to do the thing\n---\n# Do Thing\n## Step\nrun it`;
|
|
5
|
-
test('manifest + SKILL.md → records placed one-group-per-plugin + catalog', () => {
|
|
6
|
-
const res = buildIngestResult({
|
|
7
|
-
source: 'vendor',
|
|
8
|
-
plugins: [
|
|
9
|
-
{
|
|
10
|
-
plugin: 'p1',
|
|
11
|
-
version: '1',
|
|
12
|
-
skills: [{ skill: 'do-thing', skillMd: SKILL_MD }],
|
|
13
|
-
},
|
|
14
|
-
],
|
|
15
|
-
chunk: { maxChars: 1000 },
|
|
16
|
-
placement: (plugin) => ({ group: plugin, description: `plugin ${plugin}` }),
|
|
17
|
-
});
|
|
18
|
-
assert.deepEqual(res.collections.map((c) => c.group), ['p1']);
|
|
19
|
-
assert.equal(res.collections[0].description, 'plugin p1');
|
|
20
|
-
assert.ok(res.records.length >= 1);
|
|
21
|
-
assert.ok(res.records.every((r) => r.group === 'p1'));
|
|
22
|
-
const groups = new Set(res.collections.map((c) => c.group));
|
|
23
|
-
assert.ok(res.records.every((r) => groups.has(r.group)));
|
|
24
|
-
});
|
|
25
|
-
test('a strategy may bundle plugins into one group', () => {
|
|
26
|
-
const res = buildIngestResult({
|
|
27
|
-
source: 'vendor',
|
|
28
|
-
plugins: [
|
|
29
|
-
{
|
|
30
|
-
plugin: 'a',
|
|
31
|
-
version: '1',
|
|
32
|
-
skills: [{ skill: 's', skillMd: SKILL_MD }],
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
plugin: 'b',
|
|
36
|
-
version: '1',
|
|
37
|
-
skills: [{ skill: 's', skillMd: SKILL_MD }],
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
chunk: { maxChars: 1000 },
|
|
41
|
-
placement: () => ({ group: 'bundle', description: 'bundle' }),
|
|
42
|
-
});
|
|
43
|
-
assert.deepEqual(res.collections.map((c) => c.group), ['bundle']);
|
|
44
|
-
assert.ok(res.records.every((r) => r.group === 'bundle'));
|
|
45
|
-
});
|
|
46
|
-
//# sourceMappingURL=marketplace-adapter.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace-adapter.test.js","sourceRoot":"","sources":["../../../src/skills/plugin-host/marketplace-adapter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"qdrant-store.test.d.ts","sourceRoot":"","sources":["../../../src/skills/plugin-host/qdrant-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { test } from 'node:test';
|
|
3
|
-
import { CatalogCasError } from '@mcp-abap-adt/llm-agent';
|
|
4
|
-
import { makeInProcessCatalogStore, makePgCatalogStore, makeQdrantBackendProvider, makeQdrantStoreProvider, pointId, } from './qdrant-store.js';
|
|
5
|
-
const MANIFEST = {
|
|
6
|
-
embeddingSpaceId: 'sp',
|
|
7
|
-
dimension: 3,
|
|
8
|
-
retrievalSchemaVersion: 1,
|
|
9
|
-
};
|
|
10
|
-
// deterministic 3-dim embed (like the other tests): hash text → unit-ish vector
|
|
11
|
-
const embed = async (text) => {
|
|
12
|
-
let a = 0;
|
|
13
|
-
let b = 0;
|
|
14
|
-
let c = 0;
|
|
15
|
-
for (let i = 0; i < text.length; i++) {
|
|
16
|
-
const code = text.charCodeAt(i);
|
|
17
|
-
if (i % 3 === 0)
|
|
18
|
-
a += code;
|
|
19
|
-
else if (i % 3 === 1)
|
|
20
|
-
b += code;
|
|
21
|
-
else
|
|
22
|
-
c += code;
|
|
23
|
-
}
|
|
24
|
-
return [a, b, c];
|
|
25
|
-
};
|
|
26
|
-
const rec = (id, group = 'g1', sourceId = 's') => ({
|
|
27
|
-
id,
|
|
28
|
-
sourceId,
|
|
29
|
-
group,
|
|
30
|
-
name: id,
|
|
31
|
-
retrievalText: id,
|
|
32
|
-
content: `c-${id}`,
|
|
33
|
-
provenance: id,
|
|
34
|
-
});
|
|
35
|
-
function eq(a, b) {
|
|
36
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
37
|
-
}
|
|
38
|
-
function makeMockClient() {
|
|
39
|
-
const points = new Map();
|
|
40
|
-
const matches = (p, filter) => {
|
|
41
|
-
if (filter.generation !== undefined &&
|
|
42
|
-
p.payload.generation !== filter.generation)
|
|
43
|
-
return false;
|
|
44
|
-
if (Array.isArray(filter.sourceId)) {
|
|
45
|
-
if (!filter.sourceId.includes(p.payload.sourceId))
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
return true;
|
|
49
|
-
};
|
|
50
|
-
const cosine = (x, y) => {
|
|
51
|
-
let dot = 0;
|
|
52
|
-
let nx = 0;
|
|
53
|
-
let ny = 0;
|
|
54
|
-
for (let i = 0; i < x.length; i++) {
|
|
55
|
-
dot += x[i] * y[i];
|
|
56
|
-
nx += x[i] * x[i];
|
|
57
|
-
ny += y[i] * y[i];
|
|
58
|
-
}
|
|
59
|
-
return nx && ny ? dot / (Math.sqrt(nx) * Math.sqrt(ny)) : 0;
|
|
60
|
-
};
|
|
61
|
-
return {
|
|
62
|
-
_points: points,
|
|
63
|
-
async upsertPoints(pts) {
|
|
64
|
-
for (const p of pts)
|
|
65
|
-
points.set(p.id, { ...p });
|
|
66
|
-
},
|
|
67
|
-
async deleteByFilter(filter) {
|
|
68
|
-
for (const [id, p] of [...points.entries()]) {
|
|
69
|
-
if (matches(p, filter))
|
|
70
|
-
points.delete(id);
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
async search(filter, vector, k) {
|
|
74
|
-
return [...points.values()]
|
|
75
|
-
.filter((p) => matches(p, filter))
|
|
76
|
-
.map((p) => ({ payload: p.payload, score: cosine(vector, p.vector) }))
|
|
77
|
-
.sort((a, b) => b.score - a.score)
|
|
78
|
-
.slice(0, k);
|
|
79
|
-
},
|
|
80
|
-
async scroll(filter, cursor) {
|
|
81
|
-
const all = [...points.values()].filter((p) => matches(p, filter));
|
|
82
|
-
// paginate in pages of 2 to exercise the cursor loop
|
|
83
|
-
const start = cursor ? Number(cursor) : 0;
|
|
84
|
-
const page = all.slice(start, start + 2);
|
|
85
|
-
const nextStart = start + 2;
|
|
86
|
-
return {
|
|
87
|
-
points: page.map((p) => ({
|
|
88
|
-
id: p.id,
|
|
89
|
-
vector: p.vector,
|
|
90
|
-
payload: p.payload,
|
|
91
|
-
})),
|
|
92
|
-
next: nextStart < all.length ? String(nextStart) : undefined,
|
|
93
|
-
};
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
const fixedNow = () => 1_000_000;
|
|
98
|
-
function makeProvider(client, opts = {}) {
|
|
99
|
-
return makeQdrantStoreProvider({
|
|
100
|
-
client,
|
|
101
|
-
collection: 'skills',
|
|
102
|
-
catalogStore: opts.catalogStore ?? makeInProcessCatalogStore(),
|
|
103
|
-
embed,
|
|
104
|
-
pointId,
|
|
105
|
-
now: opts.now ?? fixedNow,
|
|
106
|
-
retiredGraceMs: opts.retiredGraceMs ?? 30_000,
|
|
107
|
-
orphanGraceMs: opts.orphanGraceMs ?? 3_600_000,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
test('point ids are deterministic UUIDv5 of generation:recordId', () => {
|
|
111
|
-
const a = pointId('g0', 'r1');
|
|
112
|
-
const b = pointId('g0', 'r1');
|
|
113
|
-
const c = pointId('g1', 'r1');
|
|
114
|
-
assert.equal(a, b);
|
|
115
|
-
assert.notEqual(a, c);
|
|
116
|
-
assert.match(a, /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
|
117
|
-
});
|
|
118
|
-
test('build inactive → publishCatalog (store-generated rev) → queryRevision by gen → activeSnapshot from catalog', async () => {
|
|
119
|
-
const client = makeMockClient();
|
|
120
|
-
const p = makeProvider(client);
|
|
121
|
-
const store = p.forGroup('g1');
|
|
122
|
-
const { generation } = await store.beginGeneration();
|
|
123
|
-
await store.upsert(generation, [rec('a'), rec('b')]);
|
|
124
|
-
// nothing serves yet
|
|
125
|
-
assert.equal(await store.activeSnapshot(), null);
|
|
126
|
-
const before = await p.readCatalog();
|
|
127
|
-
const entry = {
|
|
128
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
129
|
-
sources: ['s'],
|
|
130
|
-
generation,
|
|
131
|
-
manifest: MANIFEST,
|
|
132
|
-
};
|
|
133
|
-
const snap = await p.publishCatalog(before.catalogRevision, [entry]);
|
|
134
|
-
// store generated a (different) revision
|
|
135
|
-
assert.notEqual(snap.catalogRevision, before.catalogRevision);
|
|
136
|
-
const active = await store.activeSnapshot();
|
|
137
|
-
assert.equal(active?.revision, generation);
|
|
138
|
-
assert.deepEqual(active?.manifest, MANIFEST);
|
|
139
|
-
const hits = await store.queryRevision(generation, await embed('a'), 1);
|
|
140
|
-
assert.equal(hits[0].record.id, 'a');
|
|
141
|
-
assert.equal(hits[0].record.content, 'c-a');
|
|
142
|
-
assert.equal(hits[0].record.group, 'g1');
|
|
143
|
-
});
|
|
144
|
-
test('publishCatalog stale expected → CatalogCasError (in-process)', async () => {
|
|
145
|
-
const client = makeMockClient();
|
|
146
|
-
const p = makeProvider(client);
|
|
147
|
-
const r0 = (await p.readCatalog()).catalogRevision;
|
|
148
|
-
await p.publishCatalog(r0, []); // advances
|
|
149
|
-
await assert.rejects(() => p.publishCatalog(r0, []), (e) => e instanceof CatalogCasError);
|
|
150
|
-
});
|
|
151
|
-
test('makePgCatalogStore: stale expected (fake pool UPDATE rowCount 0) → CatalogCasError', async () => {
|
|
152
|
-
// fake pool: read returns empty initial row; UPDATE always rowCount 0 → stale
|
|
153
|
-
const pool = {
|
|
154
|
-
async query(sql) {
|
|
155
|
-
if (/^select/i.test(sql.trim()))
|
|
156
|
-
return { rows: [], rowCount: 0 };
|
|
157
|
-
if (/^insert/i.test(sql.trim()))
|
|
158
|
-
return { rows: [], rowCount: 1 };
|
|
159
|
-
// UPDATE ... WHERE revision=$expected → no match
|
|
160
|
-
return { rows: [], rowCount: 0 };
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
const store = makePgCatalogStore({ pool, table: 'skills_catalog' });
|
|
164
|
-
await assert.rejects(() => store.casPublish('whatever', [], 123), (e) => e instanceof CatalogCasError);
|
|
165
|
-
});
|
|
166
|
-
test('makePgCatalogStore: successful casPublish (fake pool UPDATE rowCount 1) returns committed snapshot', async () => {
|
|
167
|
-
let stored = {
|
|
168
|
-
revision: 'r-init',
|
|
169
|
-
snapshot: { catalogRevision: 'r-init', entries: [], retired: [] },
|
|
170
|
-
};
|
|
171
|
-
const pool = {
|
|
172
|
-
async query(sql, params) {
|
|
173
|
-
const s = sql.trim();
|
|
174
|
-
if (/^select/i.test(s)) {
|
|
175
|
-
return stored
|
|
176
|
-
? {
|
|
177
|
-
rows: [{ revision: stored.revision, snapshot: stored.snapshot }],
|
|
178
|
-
rowCount: 1,
|
|
179
|
-
}
|
|
180
|
-
: { rows: [], rowCount: 0 };
|
|
181
|
-
}
|
|
182
|
-
if (/^update/i.test(s)) {
|
|
183
|
-
const expected = params?.[2];
|
|
184
|
-
if (stored && stored.revision === expected) {
|
|
185
|
-
stored = {
|
|
186
|
-
revision: params?.[0],
|
|
187
|
-
snapshot: params?.[1],
|
|
188
|
-
};
|
|
189
|
-
return { rows: [], rowCount: 1 };
|
|
190
|
-
}
|
|
191
|
-
return { rows: [], rowCount: 0 };
|
|
192
|
-
}
|
|
193
|
-
return { rows: [], rowCount: 1 };
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
const store = makePgCatalogStore({ pool, table: 'skills_catalog' });
|
|
197
|
-
const entry = {
|
|
198
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
199
|
-
sources: ['s'],
|
|
200
|
-
generation: 'g-1',
|
|
201
|
-
manifest: MANIFEST,
|
|
202
|
-
};
|
|
203
|
-
const snap = await store.casPublish('r-init', [entry], 555);
|
|
204
|
-
assert.notEqual(snap.catalogRevision, 'r-init');
|
|
205
|
-
assert.equal(snap.entries.length, 1);
|
|
206
|
-
const re = await store.read();
|
|
207
|
-
assert.equal(re.catalogRevision, snap.catalogRevision);
|
|
208
|
-
assert.equal(re.entries.length, 1);
|
|
209
|
-
});
|
|
210
|
-
test('carryForward SCROLLS the live generation by sourceId and re-ids with pointId(newGen, recordId)', async () => {
|
|
211
|
-
const client = makeMockClient();
|
|
212
|
-
const p = makeProvider(client);
|
|
213
|
-
const store = p.forGroup('g1');
|
|
214
|
-
const g0 = (await store.beginGeneration()).generation;
|
|
215
|
-
await store.upsert(g0, [
|
|
216
|
-
rec('a', 'g1', 's1'),
|
|
217
|
-
rec('b', 'g1', 's2'),
|
|
218
|
-
rec('c', 'g1', 's3'),
|
|
219
|
-
]);
|
|
220
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, [
|
|
221
|
-
{
|
|
222
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
223
|
-
sources: ['s1', 's2', 's3'],
|
|
224
|
-
generation: g0,
|
|
225
|
-
manifest: MANIFEST,
|
|
226
|
-
},
|
|
227
|
-
]);
|
|
228
|
-
// new generation carries forward only s1 and s2
|
|
229
|
-
const g1 = (await store.beginGeneration()).generation;
|
|
230
|
-
await store.carryForward(g1, ['s1', 's2']);
|
|
231
|
-
// points for g1 must exist under the NEW deterministic ids
|
|
232
|
-
assert.ok(client._points.has(pointId(g1, 'a')));
|
|
233
|
-
assert.ok(client._points.has(pointId(g1, 'b')));
|
|
234
|
-
assert.ok(!client._points.has(pointId(g1, 'c')));
|
|
235
|
-
// and they carry the new generation tag
|
|
236
|
-
assert.equal(client._points.get(pointId(g1, 'a'))?.payload.generation, g1);
|
|
237
|
-
const hits = await store.queryRevision(g1, await embed('a'), 5);
|
|
238
|
-
const ids = hits.map((h) => h.record.id).sort();
|
|
239
|
-
assert.deepEqual(ids, ['a', 'b']);
|
|
240
|
-
});
|
|
241
|
-
test('durable retirement: a commit dropping G stamps retired[{G, retiredAt}] readable via catalog.read()', async () => {
|
|
242
|
-
const client = makeMockClient();
|
|
243
|
-
const catalogStore = makeInProcessCatalogStore();
|
|
244
|
-
const p = makeProvider(client, { catalogStore, now: () => 7777 });
|
|
245
|
-
const store = p.forGroup('g1');
|
|
246
|
-
const g0 = (await store.beginGeneration()).generation;
|
|
247
|
-
await store.upsert(g0, [rec('a')]);
|
|
248
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, [
|
|
249
|
-
{
|
|
250
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
251
|
-
sources: ['s'],
|
|
252
|
-
generation: g0,
|
|
253
|
-
manifest: MANIFEST,
|
|
254
|
-
},
|
|
255
|
-
]);
|
|
256
|
-
// now publish WITHOUT g0 → it is retired
|
|
257
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, []);
|
|
258
|
-
const snap = await catalogStore.read();
|
|
259
|
-
assert.ok(snap.retired?.some((r) => r.generation === g0 && r.retiredAt === 7777));
|
|
260
|
-
assert.equal(snap.retired?.find((r) => r.generation === g0)?.group, 'g1');
|
|
261
|
-
});
|
|
262
|
-
test('sweep + retiredGraceMs: before grace no delete; at/after grace deletes points + pruneRetired', async () => {
|
|
263
|
-
const client = makeMockClient();
|
|
264
|
-
const catalogStore = makeInProcessCatalogStore();
|
|
265
|
-
const clock = 1000;
|
|
266
|
-
const p = makeProvider(client, {
|
|
267
|
-
catalogStore,
|
|
268
|
-
now: () => clock,
|
|
269
|
-
retiredGraceMs: 30_000,
|
|
270
|
-
});
|
|
271
|
-
const store = p.forGroup('g1');
|
|
272
|
-
const g0 = (await store.beginGeneration()).generation;
|
|
273
|
-
await store.upsert(g0, [rec('a')]);
|
|
274
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, [
|
|
275
|
-
{
|
|
276
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
277
|
-
sources: ['s'],
|
|
278
|
-
generation: g0,
|
|
279
|
-
manifest: MANIFEST,
|
|
280
|
-
},
|
|
281
|
-
]);
|
|
282
|
-
// retire g0 at clock=1000
|
|
283
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, []);
|
|
284
|
-
assert.ok(client._points.size > 0);
|
|
285
|
-
// sweep BEFORE grace → no delete, retired still present
|
|
286
|
-
await p.sweep?.(1000 + 29_999);
|
|
287
|
-
assert.ok(client._points.size > 0);
|
|
288
|
-
assert.ok((await catalogStore.read()).retired?.some((r) => r.generation === g0));
|
|
289
|
-
// sweep AT/after grace → delete + prune
|
|
290
|
-
await p.sweep?.(1000 + 30_000);
|
|
291
|
-
assert.equal([...client._points.values()].filter((pt) => pt.payload.generation === g0)
|
|
292
|
-
.length, 0);
|
|
293
|
-
assert.ok(!(await catalogStore.read()).retired?.some((r) => r.generation === g0));
|
|
294
|
-
});
|
|
295
|
-
test('orphan reconcile age-protected: old orphan swept, young (in-build) orphan kept', async () => {
|
|
296
|
-
const client = makeMockClient();
|
|
297
|
-
const catalogStore = makeInProcessCatalogStore();
|
|
298
|
-
const NOW = 10_000_000;
|
|
299
|
-
const p = makeProvider(client, {
|
|
300
|
-
catalogStore,
|
|
301
|
-
now: () => NOW,
|
|
302
|
-
orphanGraceMs: 3_600_000, // 1h
|
|
303
|
-
});
|
|
304
|
-
// Inject an OLD orphan generation (createdAt far in the past) — neither active nor retired.
|
|
305
|
-
await client.upsertPoints([
|
|
306
|
-
{
|
|
307
|
-
id: pointId('orphan-old', 'x'),
|
|
308
|
-
vector: [1, 0, 0],
|
|
309
|
-
payload: {
|
|
310
|
-
generation: 'orphan-old',
|
|
311
|
-
group: 'g1',
|
|
312
|
-
recordId: 'x',
|
|
313
|
-
content: 'cx',
|
|
314
|
-
name: 'x',
|
|
315
|
-
provenance: 'x',
|
|
316
|
-
sourceId: 's',
|
|
317
|
-
createdAt: NOW - 3_600_001, // older than grace
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
]);
|
|
321
|
-
// Inject a YOUNG orphan (concurrent in-build loader) — createdAt within grace.
|
|
322
|
-
await client.upsertPoints([
|
|
323
|
-
{
|
|
324
|
-
id: pointId('orphan-young', 'y'),
|
|
325
|
-
vector: [0, 1, 0],
|
|
326
|
-
payload: {
|
|
327
|
-
generation: 'orphan-young',
|
|
328
|
-
group: 'g1',
|
|
329
|
-
recordId: 'y',
|
|
330
|
-
content: 'cy',
|
|
331
|
-
name: 'y',
|
|
332
|
-
provenance: 'y',
|
|
333
|
-
sourceId: 's',
|
|
334
|
-
createdAt: NOW - 1000, // younger than grace
|
|
335
|
-
},
|
|
336
|
-
},
|
|
337
|
-
]);
|
|
338
|
-
await p.sweep?.(NOW);
|
|
339
|
-
// old orphan swept, young orphan retained
|
|
340
|
-
assert.equal([...client._points.values()].filter((pt) => pt.payload.generation === 'orphan-old').length, 0);
|
|
341
|
-
assert.equal([...client._points.values()].filter((pt) => pt.payload.generation === 'orphan-young').length, 1);
|
|
342
|
-
});
|
|
343
|
-
test('makeQdrantBackendProvider over IQdrantReader+ICatalogReader is read-only and serves query/activeSnapshot/readCatalog', async () => {
|
|
344
|
-
const client = makeMockClient();
|
|
345
|
-
const catalogStore = makeInProcessCatalogStore();
|
|
346
|
-
const p = makeProvider(client, { catalogStore });
|
|
347
|
-
const store = p.forGroup('g1');
|
|
348
|
-
const g0 = (await store.beginGeneration()).generation;
|
|
349
|
-
await store.upsert(g0, [rec('a'), rec('b')]);
|
|
350
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, [
|
|
351
|
-
{
|
|
352
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
353
|
-
sources: ['s'],
|
|
354
|
-
generation: g0,
|
|
355
|
-
manifest: MANIFEST,
|
|
356
|
-
},
|
|
357
|
-
]);
|
|
358
|
-
// build a READER-ONLY provider over reader interfaces (no write methods)
|
|
359
|
-
const reader = { search: client.search, scroll: client.scroll };
|
|
360
|
-
const catalogReader = { read: () => catalogStore.read() };
|
|
361
|
-
const backendProvider = makeQdrantBackendProvider({
|
|
362
|
-
reader,
|
|
363
|
-
catalogReader,
|
|
364
|
-
collection: 'skills',
|
|
365
|
-
});
|
|
366
|
-
// compile-time read-only: the reader object has no upsert/delete
|
|
367
|
-
assert.equal(reader.upsertPoints, undefined);
|
|
368
|
-
assert.equal(backendProvider.publishCatalog, undefined);
|
|
369
|
-
const cat = await backendProvider.readCatalog();
|
|
370
|
-
assert.equal(cat.entries.length, 1);
|
|
371
|
-
const backend = backendProvider.forGroup('g1');
|
|
372
|
-
const active = await backend.activeSnapshot();
|
|
373
|
-
assert.equal(active?.revision, g0);
|
|
374
|
-
const hits = await backend.queryRevision(g0, await embed('a'), 1);
|
|
375
|
-
assert.equal(hits[0].record.id, 'a');
|
|
376
|
-
});
|
|
377
|
-
test('asBackendProvider() gives an in-process read view', async () => {
|
|
378
|
-
const client = makeMockClient();
|
|
379
|
-
const p = makeProvider(client);
|
|
380
|
-
const store = p.forGroup('g1');
|
|
381
|
-
const g0 = (await store.beginGeneration()).generation;
|
|
382
|
-
await store.upsert(g0, [rec('a')]);
|
|
383
|
-
await p.publishCatalog((await p.readCatalog()).catalogRevision, [
|
|
384
|
-
{
|
|
385
|
-
collection: { group: 'g1', description: 'd', collection: 'skills' },
|
|
386
|
-
sources: ['s'],
|
|
387
|
-
generation: g0,
|
|
388
|
-
manifest: MANIFEST,
|
|
389
|
-
},
|
|
390
|
-
]);
|
|
391
|
-
const bp = p.asBackendProvider();
|
|
392
|
-
const active = await bp.forGroup('g1').activeSnapshot();
|
|
393
|
-
assert.equal(active?.revision, g0);
|
|
394
|
-
assert.ok(eq((await bp.readCatalog()).entries.length, 1));
|
|
395
|
-
});
|
|
396
|
-
//# sourceMappingURL=qdrant-store.test.js.map
|