agentfootprint 6.23.0 → 6.24.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/dist/esm/lib/influence-core/cache.js +149 -0
- package/dist/esm/lib/influence-core/cache.js.map +1 -0
- package/dist/esm/lib/influence-core/index.js +32 -0
- package/dist/esm/lib/influence-core/index.js.map +1 -0
- package/dist/esm/lib/influence-core/margin.js +110 -0
- package/dist/esm/lib/influence-core/margin.js.map +1 -0
- package/dist/esm/lib/influence-core/signals.js +232 -0
- package/dist/esm/lib/influence-core/signals.js.map +1 -0
- package/dist/esm/lib/influence-core/similarity.js +79 -0
- package/dist/esm/lib/influence-core/similarity.js.map +1 -0
- package/dist/esm/lib/influence-core/types.js +35 -0
- package/dist/esm/lib/influence-core/types.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/bounded.js +76 -0
- package/dist/esm/lib/trace-toolpack/bounded.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/index.js +10 -0
- package/dist/esm/lib/trace-toolpack/index.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/traceToolpack.js +699 -0
- package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/types.js +24 -0
- package/dist/esm/lib/trace-toolpack/types.js.map +1 -0
- package/dist/esm/observe.js +12 -0
- package/dist/esm/observe.js.map +1 -1
- package/dist/lib/influence-core/cache.js +155 -0
- package/dist/lib/influence-core/cache.js.map +1 -0
- package/dist/lib/influence-core/index.js +50 -0
- package/dist/lib/influence-core/index.js.map +1 -0
- package/dist/lib/influence-core/margin.js +114 -0
- package/dist/lib/influence-core/margin.js.map +1 -0
- package/dist/lib/influence-core/signals.js +242 -0
- package/dist/lib/influence-core/signals.js.map +1 -0
- package/dist/lib/influence-core/similarity.js +83 -0
- package/dist/lib/influence-core/similarity.js.map +1 -0
- package/dist/lib/influence-core/types.js +38 -0
- package/dist/lib/influence-core/types.js.map +1 -0
- package/dist/lib/trace-toolpack/bounded.js +86 -0
- package/dist/lib/trace-toolpack/bounded.js.map +1 -0
- package/dist/lib/trace-toolpack/index.js +16 -0
- package/dist/lib/trace-toolpack/index.js.map +1 -0
- package/dist/lib/trace-toolpack/traceToolpack.js +704 -0
- package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -0
- package/dist/lib/trace-toolpack/types.js +28 -0
- package/dist/lib/trace-toolpack/types.js.map +1 -0
- package/dist/observe.js +31 -1
- package/dist/observe.js.map +1 -1
- package/dist/types/lib/influence-core/cache.d.ts +95 -0
- package/dist/types/lib/influence-core/cache.d.ts.map +1 -0
- package/dist/types/lib/influence-core/index.d.ts +33 -0
- package/dist/types/lib/influence-core/index.d.ts.map +1 -0
- package/dist/types/lib/influence-core/margin.d.ts +34 -0
- package/dist/types/lib/influence-core/margin.d.ts.map +1 -0
- package/dist/types/lib/influence-core/signals.d.ts +104 -0
- package/dist/types/lib/influence-core/signals.d.ts.map +1 -0
- package/dist/types/lib/influence-core/similarity.d.ts +26 -0
- package/dist/types/lib/influence-core/similarity.d.ts.map +1 -0
- package/dist/types/lib/influence-core/types.d.ts +158 -0
- package/dist/types/lib/influence-core/types.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/bounded.d.ts +48 -0
- package/dist/types/lib/trace-toolpack/bounded.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/index.d.ts +10 -0
- package/dist/types/lib/trace-toolpack/index.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/traceToolpack.d.ts +70 -0
- package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/types.d.ts +60 -0
- package/dist/types/lib/trace-toolpack/types.d.ts.map +1 -0
- package/dist/types/observe.d.ts +2 -0
- package/dist/types/observe.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast, deterministic, browser-safe content hash (FNV-1a, two 32-bit
|
|
3
|
+
* lanes + length qualifier → "len-xxxxxxxxyyyyyyyy"). Non-cryptographic
|
|
4
|
+
* — cache keying only.
|
|
5
|
+
*/
|
|
6
|
+
export function contentHash(text) {
|
|
7
|
+
let h1 = 0x811c9dc5; // FNV offset basis
|
|
8
|
+
let h2 = 0xcbf29ce4; // second lane, different seed
|
|
9
|
+
for (let i = 0; i < text.length; i++) {
|
|
10
|
+
const c = text.charCodeAt(i);
|
|
11
|
+
h1 = Math.imul(h1 ^ c, 0x01000193); // FNV prime
|
|
12
|
+
h2 = Math.imul(h2 ^ c, 0x01000197); // distinct odd prime
|
|
13
|
+
}
|
|
14
|
+
const lane1 = (h1 >>> 0).toString(16).padStart(8, '0');
|
|
15
|
+
const lane2 = (h2 >>> 0).toString(16).padStart(8, '0');
|
|
16
|
+
return `${text.length.toString(36)}-${lane1}${lane2}`;
|
|
17
|
+
}
|
|
18
|
+
const DEFAULT_MAX_ENTRIES = 1024;
|
|
19
|
+
/**
|
|
20
|
+
* Wrap an embedder with a bounded, content-hash-keyed LRU cache.
|
|
21
|
+
* See module docs for keying, bounds, and concurrency semantics.
|
|
22
|
+
*/
|
|
23
|
+
export class EmbeddingCache {
|
|
24
|
+
dimensions;
|
|
25
|
+
inner;
|
|
26
|
+
maxEntries;
|
|
27
|
+
/** LRU store — Map iteration order is recency (refreshed on hit). */
|
|
28
|
+
vectors = new Map();
|
|
29
|
+
/** Single-flight joins — promises live here until settled. */
|
|
30
|
+
inflight = new Map();
|
|
31
|
+
hits = 0;
|
|
32
|
+
misses = 0;
|
|
33
|
+
evictions = 0;
|
|
34
|
+
constructor(inner, options = {}) {
|
|
35
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
36
|
+
if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
|
|
37
|
+
throw new Error(`EmbeddingCache: maxEntries must be a positive integer (got ${maxEntries})`);
|
|
38
|
+
}
|
|
39
|
+
this.inner = inner;
|
|
40
|
+
this.maxEntries = maxEntries;
|
|
41
|
+
this.dimensions = inner.dimensions;
|
|
42
|
+
}
|
|
43
|
+
async embed(args) {
|
|
44
|
+
const key = contentHash(args.text);
|
|
45
|
+
const cached = this.vectors.get(key);
|
|
46
|
+
if (cached !== undefined) {
|
|
47
|
+
this.hits += 1;
|
|
48
|
+
this.refresh(key, cached);
|
|
49
|
+
return cached.slice();
|
|
50
|
+
}
|
|
51
|
+
const joined = this.inflight.get(key);
|
|
52
|
+
if (joined !== undefined) {
|
|
53
|
+
this.hits += 1; // coalesced — no extra inner call
|
|
54
|
+
return (await joined).slice();
|
|
55
|
+
}
|
|
56
|
+
this.misses += 1;
|
|
57
|
+
const promise = this.inner.embed(args);
|
|
58
|
+
this.inflight.set(key, promise);
|
|
59
|
+
try {
|
|
60
|
+
const vector = await promise;
|
|
61
|
+
this.store(key, vector);
|
|
62
|
+
return vector.slice();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
// Settled either way; rejections are never cached.
|
|
66
|
+
this.inflight.delete(key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async embedBatch(args) {
|
|
70
|
+
const { texts, signal } = args;
|
|
71
|
+
const out = new Array(texts.length);
|
|
72
|
+
// Partition into cache hits and misses (deduplicating within the
|
|
73
|
+
// batch — the same text twice embeds once).
|
|
74
|
+
const missTexts = [];
|
|
75
|
+
const missSlots = new Map(); // key → output indices
|
|
76
|
+
for (let i = 0; i < texts.length; i++) {
|
|
77
|
+
const key = contentHash(texts[i]);
|
|
78
|
+
const cached = this.vectors.get(key);
|
|
79
|
+
if (cached !== undefined) {
|
|
80
|
+
this.hits += 1;
|
|
81
|
+
this.refresh(key, cached);
|
|
82
|
+
out[i] = cached.slice();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const slots = missSlots.get(key);
|
|
86
|
+
if (slots !== undefined) {
|
|
87
|
+
this.hits += 1; // in-batch duplicate — one inner embed serves both
|
|
88
|
+
slots.push(i);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
this.misses += 1;
|
|
92
|
+
missSlots.set(key, [i]);
|
|
93
|
+
missTexts.push(texts[i]);
|
|
94
|
+
}
|
|
95
|
+
if (missTexts.length > 0) {
|
|
96
|
+
const vectors = this.inner.embedBatch
|
|
97
|
+
? await this.inner.embedBatch({ texts: missTexts, ...(signal ? { signal } : {}) })
|
|
98
|
+
: await this.embedSequential(missTexts, signal);
|
|
99
|
+
let v = 0;
|
|
100
|
+
for (const [key, slots] of missSlots) {
|
|
101
|
+
const vector = vectors[v++];
|
|
102
|
+
this.store(key, vector);
|
|
103
|
+
for (const slot of slots)
|
|
104
|
+
out[slot] = vector.slice();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/** Visible cache health (bounded honesty — see module docs). */
|
|
110
|
+
stats() {
|
|
111
|
+
return {
|
|
112
|
+
size: this.vectors.size,
|
|
113
|
+
maxEntries: this.maxEntries,
|
|
114
|
+
hits: this.hits,
|
|
115
|
+
misses: this.misses,
|
|
116
|
+
evictions: this.evictions,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Drop all cached vectors. Stats counters are preserved. */
|
|
120
|
+
clear() {
|
|
121
|
+
this.vectors.clear();
|
|
122
|
+
}
|
|
123
|
+
refresh(key, vector) {
|
|
124
|
+
// Map insertion order doubles as the LRU order.
|
|
125
|
+
this.vectors.delete(key);
|
|
126
|
+
this.vectors.set(key, vector);
|
|
127
|
+
}
|
|
128
|
+
store(key, vector) {
|
|
129
|
+
// Defensive copy in — callers can't mutate the cached vector.
|
|
130
|
+
this.vectors.set(key, vector.slice());
|
|
131
|
+
while (this.vectors.size > this.maxEntries) {
|
|
132
|
+
const oldest = this.vectors.keys().next().value;
|
|
133
|
+
this.vectors.delete(oldest);
|
|
134
|
+
this.evictions += 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async embedSequential(texts, signal) {
|
|
138
|
+
const vectors = [];
|
|
139
|
+
for (const text of texts) {
|
|
140
|
+
vectors.push(await this.inner.embed({ text, ...(signal ? { signal } : {}) }));
|
|
141
|
+
}
|
|
142
|
+
return vectors;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** Factory sugar — `embeddingCache(embedder)` reads like the built-ins. */
|
|
146
|
+
export function embeddingCache(inner, options = {}) {
|
|
147
|
+
return new EmbeddingCache(inner, options);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* influence-core — the ONE embedding-based scoring engine
|
|
3
|
+
* (RFC-002/003 block D6).
|
|
4
|
+
*
|
|
5
|
+
* Extracted from the Visible Reasoning paper's FDL influence pipeline
|
|
6
|
+
* (Eq. 1–6: four signals + adaptive weighted composite) so that three
|
|
7
|
+
* consumers share one engine and one embedding cache:
|
|
8
|
+
*
|
|
9
|
+
* a) RFC-002 — tool-catalog lint (C1 ← `pairwiseSimilarity`) and the
|
|
10
|
+
* margin recorder (C4/C5 ← `scoreMargin`),
|
|
11
|
+
* b) RFC-003 Part B — the LLM-edge weigher (D7 ← `scoreInfluence` /
|
|
12
|
+
* the signal scorers),
|
|
13
|
+
* c) the FDL paper pipeline itself (stages 4–6 ← `EmbeddingCache` +
|
|
14
|
+
* `scoreInfluence`).
|
|
15
|
+
*
|
|
16
|
+
* Leaf module: zero agent/runtime imports — the only dependency is the
|
|
17
|
+
* `Embedder` interface (re-exported from memory/embedding, the one
|
|
18
|
+
* existing contract) and the shared `cosineSimilarity`.
|
|
19
|
+
*
|
|
20
|
+
* Plug-and-play: the frame and formulas are the library's; the
|
|
21
|
+
* embedder, weights, and thresholds are consumer-injected.
|
|
22
|
+
*
|
|
23
|
+
* Honest claim (RFC-002 §2): every score here is a deterministic
|
|
24
|
+
* embedding-geometry PROXY — semantic alignment, never model internals
|
|
25
|
+
* and never causal attribution.
|
|
26
|
+
*/
|
|
27
|
+
export { DEFAULT_INFLUENCE_WEIGHTS, DEFAULT_MARGIN_THRESHOLD, DEFAULT_PERSISTENCE_THRESHOLD, } from './types.js';
|
|
28
|
+
export { contentHash, EmbeddingCache, embeddingCache, } from './cache.js';
|
|
29
|
+
export { adaptWeights, averageRelevancy, compositeScore, finalAnswerSimilarity, persistence, scoreInfluence, structuralProximity, } from './signals.js';
|
|
30
|
+
export { pairwiseSimilarity } from './similarity.js';
|
|
31
|
+
export { scoreMargin } from './margin.js';
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAiBH,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,GAGf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,cAAc,EACd,mBAAmB,GAEpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAA+B,MAAM,iBAAiB,CAAC;AAElF,OAAO,EAAE,WAAW,EAAwB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scoreMargin — choice-margin scoring over a candidate set
|
|
3
|
+
* (RFC-002 C4's core: (candidates, contextText, chosen) → scores +
|
|
4
|
+
* margin + flags).
|
|
5
|
+
*
|
|
6
|
+
* Pattern: pure async function, embedder-injected. NO recorder wiring
|
|
7
|
+
* — `toolChoiceRecorder` (C5) owns event ingestion and a
|
|
8
|
+
* KeyedStore, and calls this for the math.
|
|
9
|
+
* Role: `src/lib/influence-core/` leaf. No agent/runtime imports.
|
|
10
|
+
*
|
|
11
|
+
* The competition model (RFC-002 §4): embed the choice context (what
|
|
12
|
+
* the model saw — user message + latest reasoning), embed each offered
|
|
13
|
+
* candidate's text, rank candidates by similarity to the context.
|
|
14
|
+
* margin = score(best chosen) − score(best non-chosen)
|
|
15
|
+
* Small margin = fragile choice (`narrow`); top-scored candidate not
|
|
16
|
+
* among the chosen = `proxyDisagreement` (always worth surfacing:
|
|
17
|
+
* either a proxy miss or a genuinely surprising model choice).
|
|
18
|
+
*
|
|
19
|
+
* Honest claim: the scores are embedding geometry between context and
|
|
20
|
+
* descriptions — a proxy for the model's selection function, never
|
|
21
|
+
* "the model chose because". Margin is EVIDENCE of decisiveness, not
|
|
22
|
+
* proof; tier 3 (choice-entropy sampling) validates the proxy.
|
|
23
|
+
*/
|
|
24
|
+
import { cosineSimilarity } from '../../memory/embedding/cosine.js';
|
|
25
|
+
import { DEFAULT_MARGIN_THRESHOLD } from './types.js';
|
|
26
|
+
/**
|
|
27
|
+
* Rank candidates by cosine similarity to the choice context and
|
|
28
|
+
* measure how decisively the chosen one(s) won.
|
|
29
|
+
*
|
|
30
|
+
* Returns ranked `scores` (descending; ties keep candidate input
|
|
31
|
+
* order), the `topScored` name, the `margin` (undefined when every
|
|
32
|
+
* candidate was chosen — no competition to measure; `narrow` is false
|
|
33
|
+
* in that case), and the two flags.
|
|
34
|
+
*
|
|
35
|
+
* Fail-loud validation: empty candidates/chosen, duplicate candidate
|
|
36
|
+
* names, or a chosen name missing from the candidates throw — those
|
|
37
|
+
* are wiring bugs in the caller, not runtime conditions.
|
|
38
|
+
*/
|
|
39
|
+
export async function scoreMargin(args) {
|
|
40
|
+
const { candidates, chosen, embedder } = args;
|
|
41
|
+
const marginThreshold = args.marginThreshold ?? DEFAULT_MARGIN_THRESHOLD;
|
|
42
|
+
validate(candidates, chosen);
|
|
43
|
+
// One deduplicated embedding pass: context + distinct candidate texts.
|
|
44
|
+
const distinct = [...new Set([args.contextText, ...candidates.map((c) => c.text)])];
|
|
45
|
+
const vectors = embedder.embedBatch
|
|
46
|
+
? await embedder.embedBatch({
|
|
47
|
+
texts: distinct,
|
|
48
|
+
...(args.signal ? { signal: args.signal } : {}),
|
|
49
|
+
})
|
|
50
|
+
: await sequentialEmbed(embedder, distinct, args.signal);
|
|
51
|
+
const vectorByText = new Map();
|
|
52
|
+
for (let i = 0; i < distinct.length; i++)
|
|
53
|
+
vectorByText.set(distinct[i], vectors[i]);
|
|
54
|
+
const contextVec = vectorByText.get(args.contextText);
|
|
55
|
+
const scores = candidates.map((candidate) => ({
|
|
56
|
+
name: candidate.name,
|
|
57
|
+
score: cosineSimilarity(contextVec, vectorByText.get(candidate.text)),
|
|
58
|
+
}));
|
|
59
|
+
// Stable sort — ties keep candidate input order.
|
|
60
|
+
scores.sort((a, b) => b.score - a.score);
|
|
61
|
+
const chosenSet = new Set(chosen);
|
|
62
|
+
let bestChosen = -Infinity;
|
|
63
|
+
let bestOther = -Infinity;
|
|
64
|
+
for (const { name, score } of scores) {
|
|
65
|
+
if (chosenSet.has(name))
|
|
66
|
+
bestChosen = Math.max(bestChosen, score);
|
|
67
|
+
else
|
|
68
|
+
bestOther = Math.max(bestOther, score);
|
|
69
|
+
}
|
|
70
|
+
const margin = bestOther === -Infinity ? undefined : bestChosen - bestOther;
|
|
71
|
+
const topScored = scores[0].name;
|
|
72
|
+
return {
|
|
73
|
+
scores,
|
|
74
|
+
chosen: [...chosen],
|
|
75
|
+
topScored,
|
|
76
|
+
margin,
|
|
77
|
+
flags: {
|
|
78
|
+
narrow: margin !== undefined && margin < marginThreshold,
|
|
79
|
+
proxyDisagreement: !chosenSet.has(topScored),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async function sequentialEmbed(embedder, texts, signal) {
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const text of texts) {
|
|
86
|
+
out.push(await embedder.embed({ text, ...(signal ? { signal } : {}) }));
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function validate(candidates, chosen) {
|
|
91
|
+
if (candidates.length === 0) {
|
|
92
|
+
throw new Error('scoreMargin: candidates must be non-empty');
|
|
93
|
+
}
|
|
94
|
+
const names = new Set();
|
|
95
|
+
for (const candidate of candidates) {
|
|
96
|
+
if (names.has(candidate.name)) {
|
|
97
|
+
throw new Error(`scoreMargin: duplicate candidate name '${candidate.name}' — names must be unique`);
|
|
98
|
+
}
|
|
99
|
+
names.add(candidate.name);
|
|
100
|
+
}
|
|
101
|
+
if (chosen.length === 0) {
|
|
102
|
+
throw new Error('scoreMargin: chosen must be non-empty — calls that chose nothing have no margin to score');
|
|
103
|
+
}
|
|
104
|
+
for (const name of chosen) {
|
|
105
|
+
if (!names.has(name)) {
|
|
106
|
+
throw new Error(`scoreMargin: chosen '${name}' is not among the candidates`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=margin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"margin.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/margin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FDL influence signals — the four-signal composite from the Visible
|
|
3
|
+
* Reasoning paper (Eq. 1–6), extracted verbatim as RFC-003 block D6.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: pure scorer functions + one async orchestrator. Vector-level
|
|
6
|
+
* functions are deterministic and embedder-free; only
|
|
7
|
+
* `scoreInfluence` touches the injected `Embedder`.
|
|
8
|
+
* Role: `src/lib/influence-core/` leaf. Consumers: the FDL paper
|
|
9
|
+
* pipeline (stage 5, computeInfluenceScores), RFC-003 D7's
|
|
10
|
+
* LLM-edge weigher, and — one level up — RFC-002's margin
|
|
11
|
+
* scoring shares the same geometry via `margin.ts`.
|
|
12
|
+
*
|
|
13
|
+
* ## Honest claim per signal (RFC-002 §2 discipline)
|
|
14
|
+
*
|
|
15
|
+
* Every signal is embedding GEOMETRY — a deterministic proxy, not a
|
|
16
|
+
* window into the model:
|
|
17
|
+
*
|
|
18
|
+
* - FA "the tool's output is semantically close to the final
|
|
19
|
+
* answer" — NOT "the answer was derived from it".
|
|
20
|
+
* - AVG "the tool's output stayed semantically close to the
|
|
21
|
+
* reasoning steps" — NOT "the model kept consulting it".
|
|
22
|
+
* - PERSIST "many reasoning steps are similar to it above T" — breadth
|
|
23
|
+
* of apparent reference, NOT counted citations.
|
|
24
|
+
* - DEPTH pure structure (1/(1+ancestors)) — directness of position
|
|
25
|
+
* in the trace, knows nothing about content at all.
|
|
26
|
+
*
|
|
27
|
+
* The composite S(d) means "high semantic alignment with the answer",
|
|
28
|
+
* never "this source contributed X% of the answer" (paper §5.2: scores
|
|
29
|
+
* are per-item, not additive, not causal attribution). Same inputs →
|
|
30
|
+
* same scores, unlike LLM-as-judge.
|
|
31
|
+
*/
|
|
32
|
+
import { cosineSimilarity } from '../../memory/embedding/cosine.js';
|
|
33
|
+
import { DEFAULT_INFLUENCE_WEIGHTS, DEFAULT_PERSISTENCE_THRESHOLD } from './types.js';
|
|
34
|
+
/**
|
|
35
|
+
* FA — Final Answer Similarity (paper Eq. 1).
|
|
36
|
+
*
|
|
37
|
+
* `FA(d) = sim(e_d, e_f)` — cosine between the evidence embedding and
|
|
38
|
+
* the final-answer embedding. The strongest prior: verbatim or
|
|
39
|
+
* paraphrased reuse of a tool result scores high. Proxy: semantic
|
|
40
|
+
* overlap, not provenance.
|
|
41
|
+
*/
|
|
42
|
+
export function finalAnswerSimilarity(evidenceVec, finalAnswerVec) {
|
|
43
|
+
return cosineSimilarity(evidenceVec, finalAnswerVec);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* AVG — Average Relevancy (paper Eq. 2).
|
|
47
|
+
*
|
|
48
|
+
* Mean cosine between the evidence and each LLM reasoning ancestor;
|
|
49
|
+
* 0 when there are no ancestors (structurally zero — see
|
|
50
|
+
* `adaptWeights`). Proxy: consistent semantic closeness across the
|
|
51
|
+
* chain, not actual consultation.
|
|
52
|
+
*/
|
|
53
|
+
export function averageRelevancy(evidenceVec, ancestorVecs) {
|
|
54
|
+
const n = ancestorVecs.length;
|
|
55
|
+
if (n === 0)
|
|
56
|
+
return 0;
|
|
57
|
+
let sum = 0;
|
|
58
|
+
for (const ancestorVec of ancestorVecs) {
|
|
59
|
+
sum += cosineSimilarity(evidenceVec, ancestorVec);
|
|
60
|
+
}
|
|
61
|
+
return sum / n;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* PERSIST — Persistence (paper Eq. 3).
|
|
65
|
+
*
|
|
66
|
+
* Fraction of ancestors whose similarity to the evidence EXCEEDS the
|
|
67
|
+
* threshold T (strict `>`, default 0.30); 0 when there are no
|
|
68
|
+
* ancestors. Unlike AVG it measures BREADTH: referenced in 4 of 5
|
|
69
|
+
* steps (0.8) beats referenced intensely in 1. Proxy: similarity
|
|
70
|
+
* above a tunable bar, not counted citations.
|
|
71
|
+
*/
|
|
72
|
+
export function persistence(evidenceVec, ancestorVecs, threshold = DEFAULT_PERSISTENCE_THRESHOLD) {
|
|
73
|
+
const n = ancestorVecs.length;
|
|
74
|
+
if (n === 0)
|
|
75
|
+
return 0;
|
|
76
|
+
let above = 0;
|
|
77
|
+
for (const ancestorVec of ancestorVecs) {
|
|
78
|
+
if (cosineSimilarity(evidenceVec, ancestorVec) > threshold)
|
|
79
|
+
above += 1;
|
|
80
|
+
}
|
|
81
|
+
return above / n;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* DEPTH — Structural Proximity (paper Eq. 4).
|
|
85
|
+
*
|
|
86
|
+
* `DEPTH(d) = 1 / (1 + n)` where n counts LLM reasoning ancestors
|
|
87
|
+
* ONLY (not pipeline plumbing — callers decide what counts as an
|
|
88
|
+
* ancestor when building `EvidenceInput.ancestorTexts`). Direct
|
|
89
|
+
* evidence with no intermediaries gets exactly 1.0. The only
|
|
90
|
+
* content-blind signal: pure trace structure.
|
|
91
|
+
*/
|
|
92
|
+
export function structuralProximity(ancestorCount) {
|
|
93
|
+
if (!Number.isInteger(ancestorCount) || ancestorCount < 0) {
|
|
94
|
+
throw new Error(`structuralProximity: ancestorCount must be a non-negative integer (got ${ancestorCount})`);
|
|
95
|
+
}
|
|
96
|
+
return 1 / (1 + ancestorCount);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Adaptive weight redistribution (paper Eq. 6, §5.3).
|
|
100
|
+
*
|
|
101
|
+
* When an item has NO LLM ancestors, AVG and PERSIST are structurally
|
|
102
|
+
* zero — not because the evidence was uninfluential, but because there
|
|
103
|
+
* is nothing to measure against. Without adaptation its score is
|
|
104
|
+
* capped at α+δ (≈0.50 under defaults). Eq. 6 moves the β+γ mass onto
|
|
105
|
+
* FA and DEPTH preserving their ratio:
|
|
106
|
+
*
|
|
107
|
+
* α′ = α + (β+γ)·α/(α+δ), δ′ = δ + (β+γ)·δ/(α+δ), β′ = γ′ = 0
|
|
108
|
+
*
|
|
109
|
+
* Defaults → α′=0.80, δ′=0.20 (the 4:1 FA:DEPTH ratio kept).
|
|
110
|
+
* Per-evidence-item: in a multi-tool pipeline some items adapt while
|
|
111
|
+
* others keep standard weights; `adapted` says which (surface it — the
|
|
112
|
+
* paper's UI marks adapted items).
|
|
113
|
+
*
|
|
114
|
+
* Degenerate guard: if α+δ = 0 there is no defined ratio to preserve —
|
|
115
|
+
* weights return unchanged with `adapted: false`, and the composite is
|
|
116
|
+
* honestly 0 for a no-ancestor item.
|
|
117
|
+
*/
|
|
118
|
+
export function adaptWeights(weights, ancestorCount) {
|
|
119
|
+
if (ancestorCount > 0)
|
|
120
|
+
return { weights, adapted: false };
|
|
121
|
+
const base = weights.fa + weights.depth;
|
|
122
|
+
if (base === 0)
|
|
123
|
+
return { weights, adapted: false };
|
|
124
|
+
const mass = weights.avg + weights.persist;
|
|
125
|
+
return {
|
|
126
|
+
weights: {
|
|
127
|
+
fa: weights.fa + (mass * weights.fa) / base,
|
|
128
|
+
avg: 0,
|
|
129
|
+
persist: 0,
|
|
130
|
+
depth: weights.depth + (mass * weights.depth) / base,
|
|
131
|
+
},
|
|
132
|
+
adapted: mass > 0,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Composite score S(d) (paper Eq. 5).
|
|
137
|
+
*
|
|
138
|
+
* `S = α·FA + β·AVG + γ·PERSIST + δ·DEPTH` under the given weights —
|
|
139
|
+
* pass the EFFECTIVE weights from `adaptWeights` for no-ancestor
|
|
140
|
+
* items. With weights summing to 1, S ∈ [−(α+β), 1] (FA/AVG are
|
|
141
|
+
* cosines and may go negative; PERSIST/DEPTH are non-negative).
|
|
142
|
+
*/
|
|
143
|
+
export function compositeScore(signals, weights) {
|
|
144
|
+
return (weights.fa * signals.fa +
|
|
145
|
+
weights.avg * signals.avg +
|
|
146
|
+
weights.persist * signals.persist +
|
|
147
|
+
weights.depth * signals.depth);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Score every evidence item on the four FDL signals and rank by
|
|
151
|
+
* composite, descending (paper pipeline stages 4–6 in one call:
|
|
152
|
+
* embed → score → rank). Ties keep input order (stable sort).
|
|
153
|
+
*
|
|
154
|
+
* Deterministic for a deterministic embedder: same inputs → same
|
|
155
|
+
* scores. All texts are embedded in ONE deduplicated batch — with an
|
|
156
|
+
* `EmbeddingCache` injected, repeat calls embed nothing.
|
|
157
|
+
*
|
|
158
|
+
* Honest claim: ranked semantic-alignment proxies. NOT causal
|
|
159
|
+
* attribution — see module docs.
|
|
160
|
+
*/
|
|
161
|
+
export async function scoreInfluence(args) {
|
|
162
|
+
const weights = args.weights ?? DEFAULT_INFLUENCE_WEIGHTS;
|
|
163
|
+
assertValidWeights(weights);
|
|
164
|
+
const threshold = args.persistenceThreshold ?? DEFAULT_PERSISTENCE_THRESHOLD;
|
|
165
|
+
assertUniqueIds(args.evidence);
|
|
166
|
+
// ONE deduplicated embedding pass over every distinct text.
|
|
167
|
+
const texts = new Set([args.finalAnswerText]);
|
|
168
|
+
for (const item of args.evidence) {
|
|
169
|
+
texts.add(item.text);
|
|
170
|
+
for (const ancestor of item.ancestorTexts)
|
|
171
|
+
texts.add(ancestor);
|
|
172
|
+
}
|
|
173
|
+
const vectorByText = await embedAll(args.embedder, [...texts], args.signal);
|
|
174
|
+
const finalVec = vectorByText.get(args.finalAnswerText);
|
|
175
|
+
const scored = args.evidence.map((item) => {
|
|
176
|
+
const evidenceVec = vectorByText.get(item.text);
|
|
177
|
+
const ancestorVecs = item.ancestorTexts.map((t) => vectorByText.get(t));
|
|
178
|
+
const signals = {
|
|
179
|
+
fa: finalAnswerSimilarity(evidenceVec, finalVec),
|
|
180
|
+
avg: averageRelevancy(evidenceVec, ancestorVecs),
|
|
181
|
+
persist: persistence(evidenceVec, ancestorVecs, threshold),
|
|
182
|
+
depth: structuralProximity(ancestorVecs.length),
|
|
183
|
+
};
|
|
184
|
+
const effective = adaptWeights(weights, ancestorVecs.length);
|
|
185
|
+
return {
|
|
186
|
+
id: item.id,
|
|
187
|
+
signals,
|
|
188
|
+
weights: effective.weights,
|
|
189
|
+
adapted: effective.adapted,
|
|
190
|
+
score: compositeScore(signals, effective.weights),
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
// Stable sort — equal scores keep evidence input order.
|
|
194
|
+
return scored.sort((a, b) => b.score - a.score);
|
|
195
|
+
}
|
|
196
|
+
/** Embed distinct texts via batch API when available, else sequentially. */
|
|
197
|
+
async function embedAll(embedder, texts, signal) {
|
|
198
|
+
const vectors = embedder.embedBatch
|
|
199
|
+
? await embedder.embedBatch({ texts, ...(signal ? { signal } : {}) })
|
|
200
|
+
: await sequentialEmbed(embedder, texts, signal);
|
|
201
|
+
const byText = new Map();
|
|
202
|
+
for (let i = 0; i < texts.length; i++)
|
|
203
|
+
byText.set(texts[i], vectors[i]);
|
|
204
|
+
return byText;
|
|
205
|
+
}
|
|
206
|
+
async function sequentialEmbed(embedder, texts, signal) {
|
|
207
|
+
const out = [];
|
|
208
|
+
for (const text of texts) {
|
|
209
|
+
out.push(await embedder.embed({ text, ...(signal ? { signal } : {}) }));
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
function assertValidWeights(weights) {
|
|
214
|
+
for (const [name, value] of Object.entries(weights)) {
|
|
215
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
216
|
+
throw new Error(`scoreInfluence: weight '${name}' must be a finite non-negative number (got ${value})`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (weights.fa + weights.avg + weights.persist + weights.depth === 0) {
|
|
220
|
+
throw new Error('scoreInfluence: all weights are zero — the composite would always be 0');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function assertUniqueIds(evidence) {
|
|
224
|
+
const seen = new Set();
|
|
225
|
+
for (const item of evidence) {
|
|
226
|
+
if (seen.has(item.id)) {
|
|
227
|
+
throw new Error(`scoreInfluence: duplicate evidence id '${item.id}' — ids must be unique`);
|
|
228
|
+
}
|
|
229
|
+
seen.add(item.id);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/signals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pairwiseSimilarity — pairwise cosine over a set of texts
|
|
3
|
+
* (RFC-002 C1's core: tool descriptions → matrix + ranked pairs).
|
|
4
|
+
*
|
|
5
|
+
* Pattern: pure async function, embedder-injected. No thresholds, no
|
|
6
|
+
* verdicts, no lint rules — those are C1's `analyzeToolCatalog`
|
|
7
|
+
* policy layer ON TOP of this geometry. The core stays
|
|
8
|
+
* reusable for any "how confusable are these texts" question.
|
|
9
|
+
* Role: `src/lib/influence-core/` leaf. No agent/runtime imports.
|
|
10
|
+
*
|
|
11
|
+
* Honest claim: similarity is embedding geometry over the DESCRIPTIONS
|
|
12
|
+
* — a confusability HEURISTIC, not a measurement of the model's actual
|
|
13
|
+
* selection function (RFC-002 §2; tier 3 validates the proxy via
|
|
14
|
+
* choice-entropy sampling).
|
|
15
|
+
*/
|
|
16
|
+
import { cosineSimilarity } from '../../memory/embedding/cosine.js';
|
|
17
|
+
/**
|
|
18
|
+
* Embed every item once (deduplicated batch) and compute the full
|
|
19
|
+
* cosine matrix plus ranked upper-triangle pairs (descending; ties
|
|
20
|
+
* keep input pair order).
|
|
21
|
+
*
|
|
22
|
+
* Invariants (pinned by property tests):
|
|
23
|
+
* - `matrix[i][j] === matrix[j][i]` — computed once, mirrored.
|
|
24
|
+
* - `matrix[i][i] === 1` EXACTLY — set by definition, so
|
|
25
|
+
* self-similarity is an invariant rather than a float artifact
|
|
26
|
+
* (and duplicate texts at different ids still compare via cosine).
|
|
27
|
+
* - N items → N·(N−1)/2 pairs.
|
|
28
|
+
*/
|
|
29
|
+
export async function pairwiseSimilarity(args) {
|
|
30
|
+
const { items, embedder } = args;
|
|
31
|
+
assertUniqueIds(items);
|
|
32
|
+
const ids = items.map((item) => item.id);
|
|
33
|
+
if (items.length === 0)
|
|
34
|
+
return { ids, matrix: [], pairs: [] };
|
|
35
|
+
// Deduplicated embedding pass (identical descriptions embed once).
|
|
36
|
+
const distinct = [...new Set(items.map((item) => item.text))];
|
|
37
|
+
const vectors = embedder.embedBatch
|
|
38
|
+
? await embedder.embedBatch({
|
|
39
|
+
texts: distinct,
|
|
40
|
+
...(args.signal ? { signal: args.signal } : {}),
|
|
41
|
+
})
|
|
42
|
+
: await sequentialEmbed(embedder, distinct, args.signal);
|
|
43
|
+
const vectorByText = new Map();
|
|
44
|
+
for (let i = 0; i < distinct.length; i++)
|
|
45
|
+
vectorByText.set(distinct[i], vectors[i]);
|
|
46
|
+
const itemVecs = items.map((item) => vectorByText.get(item.text));
|
|
47
|
+
// Upper triangle once, mirrored; diagonal exactly 1 by definition.
|
|
48
|
+
const matrix = items.map(() => new Array(items.length).fill(0));
|
|
49
|
+
const pairs = [];
|
|
50
|
+
for (let i = 0; i < items.length; i++) {
|
|
51
|
+
matrix[i][i] = 1;
|
|
52
|
+
for (let j = i + 1; j < items.length; j++) {
|
|
53
|
+
const similarity = cosineSimilarity(itemVecs[i], itemVecs[j]);
|
|
54
|
+
matrix[i][j] = similarity;
|
|
55
|
+
matrix[j][i] = similarity;
|
|
56
|
+
pairs.push({ a: ids[i], b: ids[j], similarity });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Stable sort — ties keep (i, j) input order.
|
|
60
|
+
pairs.sort((p, q) => q.similarity - p.similarity);
|
|
61
|
+
return { ids, matrix, pairs };
|
|
62
|
+
}
|
|
63
|
+
async function sequentialEmbed(embedder, texts, signal) {
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const text of texts) {
|
|
66
|
+
out.push(await embedder.embed({ text, ...(signal ? { signal } : {}) }));
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function assertUniqueIds(items) {
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
if (seen.has(item.id)) {
|
|
74
|
+
throw new Error(`pairwiseSimilarity: duplicate item id '${item.id}' — ids must be unique`);
|
|
75
|
+
}
|
|
76
|
+
seen.add(item.id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=similarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.js","sourceRoot":"","sources":["../../../../src/lib/influence-core/similarity.ts"],"names":[],"mappings":"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"}
|