mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-30 19:49:14 +00:00
Wires edge-derived candidates into bare hybridSearch as a FOURTH RRF arm (relational-recall.ts): parse the original query -> scope-aware, confidence-gated seed resolution (drops fallback_slugify; never traverses from a guess) -> relationalFanout -> batch-hydrate, reinforcing each page's REAL canonical chunk (page-level key for chunkless entity pages) -> --explain attribution + fail-open audit row. Text-only (no-op in image mode); pure no-op for non-relational queries; rides every downstream stage (cosine, post-fusion boosts, dedup, reranker, autocut, token budget). Mode wiring: relationalRetrieval + relational_retrieval_depth knobs (conservative off; balanced/tokenmax on; depth 2), per-call thread-through in both bare + cached paths, KNOBS_HASH_VERSION 9->10 (rel=/reld=), config keys, modes-dashboard descriptions, and a `relational` param on the query op. Federation hardening (structural, engine-wide): the RRF/dedup key now carries source_id via a shared rrfKey() (fixes a latent cross-source collapse where same-slug pages in different sources merged), and the query cache scopes by a canonical source-set key (cacheScopeKey) so a federated read can't be served a single-source row. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
/**
|
|
* Query-cache scope key (federation hardening).
|
|
*
|
|
* A federated search reads a different graph than a single-source one, so
|
|
* the semantic cache must key them apart. `cacheScopeKey` produces an
|
|
* order-independent key for federated scopes and leaves single-source
|
|
* brains on their existing key (scalar id or 'default'), so single-source
|
|
* cache hit-rate is unchanged.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { cacheScopeKey } from '../src/core/search/hybrid.ts';
|
|
|
|
describe('cacheScopeKey', () => {
|
|
test('unscoped → default (single-source unchanged)', () => {
|
|
expect(cacheScopeKey(undefined)).toBe('default');
|
|
expect(cacheScopeKey({})).toBe('default');
|
|
});
|
|
|
|
test('scalar sourceId → itself (single-source unchanged)', () => {
|
|
expect(cacheScopeKey({ sourceId: 'host' })).toBe('host');
|
|
});
|
|
|
|
test('federated sourceIds → order-independent set key', () => {
|
|
const k1 = cacheScopeKey({ sourceIds: ['team-b', 'team-a', 'host'] });
|
|
const k2 = cacheScopeKey({ sourceIds: ['host', 'team-a', 'team-b'] });
|
|
expect(k1).toBe(k2); // order does not matter
|
|
expect(k1).toBe('__set__:host,team-a,team-b');
|
|
});
|
|
|
|
test('different source-sets do NOT share a key', () => {
|
|
const a = cacheScopeKey({ sourceIds: ['host', 'team-a'] });
|
|
const b = cacheScopeKey({ sourceIds: ['host', 'team-b'] });
|
|
expect(a).not.toBe(b);
|
|
});
|
|
|
|
test('federated set key is distinct from any single scalar key', () => {
|
|
const set = cacheScopeKey({ sourceIds: ['host'] });
|
|
const scalar = cacheScopeKey({ sourceId: 'host' });
|
|
expect(set).not.toBe(scalar); // a 1-element set still cannot serve a scalar read
|
|
});
|
|
});
|