UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

82 lines • 3.97 kB
/** Whether `store` implements {@link LexicalVectorStore}. */ export function isLexicalVectorStore(store) { return typeof store.searchText === 'function'; } /** * A destructive call was refused because its scope could not be trusted. Thrown by * {@link VectorStore.removeWhere} — never for a filter that merely matched nothing (that is a legitimate * result of `0`), only for one that would have deleted *more* than the caller plausibly meant. */ export class UnsafeRemovalError extends Error { reason; constructor( /** Why the removal was refused. */ reason, message) { super(message); this.reason = reason; this.name = 'UnsafeRemovalError'; } } /** * Guard for {@link VectorStore.removeWhere}: refuse a filter that scopes nothing. Shared so all three * stores refuse the same input with the same error, rather than each deciding for itself how much of the * corpus an empty object means. */ export function assertRemovalFilter(filter) { if (Object.keys(filter).length === 0) { throw new UnsafeRemovalError('empty-filter', 'removeWhere refused an empty filter: it would delete every chunk in the store, which is far ' + 'more likely to be a filter built wrong than a deliberate request. For a deliberate wipe, ' + 'loop `remove` over `listDocumentIds()`, or drop the table/collection.'); } } /** * Does this filter deny everything? True when any key carries an **empty array** — no value is a member * of the empty set, so nothing can match (see {@link import('./filter.js').matchesFilter}). * * Every filter builder in this package already encodes that (`matchesFilter` returns false, * `buildMetadataWhere` emits a `false` clause, `buildQdrantFilter` emits `any: []`), and the live specs * confirm the backends agree. {@link VectorStore.removeWhere} checks it anyway and short-circuits before * issuing anything: for the one operation where misreading a deny as "no filter" destroys the corpus, the * deny should not depend on a backend's interpretation of an empty set. */ export function filterDeniesAll(filter) { return Object.values(filter).some((value) => Array.isArray(value) && value.length === 0); } /** * The keys of a `patch` that will actually be written — every key whose value is not `undefined`. Shared * by all three stores so "an empty patch writes nothing and returns 0" means the same thing on each, and * so none of them issues a round trip for a no-op. */ export function effectivePatchKeys(patch) { return Object.keys(patch).filter((key) => patch[key] !== undefined); } /** * Apply a {@link MetadataPatch} to one chunk's metadata, returning the new metadata object. The single * definition of the merge, shared by every store that can do the merge in JS — so the semantics cannot * drift between adapters. Pure: the input object is never mutated. * * Always returns an object (never `undefined`), so a patched chunk has metadata even if it had none * before, and an all-deleting patch leaves `{}` rather than a hole. `{}` and absent metadata are * indistinguishable to {@link import('./filter.js').matchesFilter} and to pgvector's `@>`, so this costs * a caller nothing. */ export function applyMetadataPatch(metadata, patch) { const next = { ...metadata }; for (const key of Object.keys(patch)) { const value = patch[key]; if (value === undefined) { continue; // JSON has no `undefined`; treat it as "key not present". } if (value === null) { delete next[key]; // JSON Merge Patch: null REMOVES the key. continue; } next[key] = value; // Replaced wholesale — shallow, so no recursive merge. } return next; } /** Collapse a chunk id (`${documentId}#<n>`) back to its source document id. */ export function documentIdOf(chunkId) { return chunkId.replace(/#\d+$/, ''); } //# sourceMappingURL=vector-store.js.map