@seanhogg/builderforce-memory 2026.9.29 → 2026.9.30
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/bridges/AnthropicBridge.d.ts.map +1 -1
- package/dist/bridges/AnthropicBridge.js +24 -65
- package/dist/bridges/AnthropicBridge.js.map +1 -1
- package/dist/bridges/OpenAIBridge.d.ts.map +1 -1
- package/dist/bridges/OpenAIBridge.js +12 -46
- package/dist/bridges/OpenAIBridge.js.map +1 -1
- package/dist/bridges/VertexAIBridge.d.ts.map +1 -1
- package/dist/bridges/VertexAIBridge.js +3 -34
- package/dist/bridges/VertexAIBridge.js.map +1 -1
- package/dist/distillation/DistillationEngine.d.ts +71 -54
- package/dist/distillation/DistillationEngine.d.ts.map +1 -1
- package/dist/distillation/DistillationEngine.js +112 -95
- package/dist/distillation/DistillationEngine.js.map +1 -1
- package/dist/distillation/index.d.ts +7 -1
- package/dist/distillation/index.d.ts.map +1 -1
- package/dist/distillation/index.js +5 -0
- package/dist/distillation/index.js.map +1 -1
- package/dist/distillation/ports.d.ts +43 -0
- package/dist/distillation/ports.d.ts.map +1 -0
- package/dist/distillation/ports.js +29 -0
- package/dist/distillation/ports.js.map +1 -0
- package/dist/eval/EvalHarness.d.ts +2 -1
- package/dist/eval/EvalHarness.d.ts.map +1 -1
- package/dist/eval/EvalHarness.js +13 -1
- package/dist/eval/EvalHarness.js.map +1 -1
- package/dist/eval/baseline.d.ts +33 -0
- package/dist/eval/baseline.d.ts.map +1 -0
- package/dist/eval/baseline.js +41 -0
- package/dist/eval/baseline.js.map +1 -0
- package/dist/eval/index.d.ts +1 -0
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/index.js +1 -0
- package/dist/eval/index.js.map +1 -1
- package/dist/eval/types.d.ts +7 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/rag/EnterpriseRetriever.d.ts.map +1 -1
- package/dist/rag/EnterpriseRetriever.js +2 -1
- package/dist/rag/EnterpriseRetriever.js.map +1 -1
- package/dist/retrieval/HybridRetriever.d.ts.map +1 -1
- package/dist/retrieval/HybridRetriever.js +2 -1
- package/dist/retrieval/HybridRetriever.js.map +1 -1
- package/dist/retrieval/decay.d.ts +24 -0
- package/dist/retrieval/decay.d.ts.map +1 -0
- package/dist/retrieval/decay.js +29 -0
- package/dist/retrieval/decay.js.map +1 -0
- package/dist/retrieval/fusion.d.ts +13 -19
- package/dist/retrieval/fusion.d.ts.map +1 -1
- package/dist/retrieval/fusion.js +17 -44
- package/dist/retrieval/fusion.js.map +1 -1
- package/dist/retrieval/index.d.ts +13 -6
- package/dist/retrieval/index.d.ts.map +1 -1
- package/dist/retrieval/index.js +10 -5
- package/dist/retrieval/index.js.map +1 -1
- package/dist/retrieval/mmr.d.ts +60 -0
- package/dist/retrieval/mmr.d.ts.map +1 -0
- package/dist/retrieval/mmr.js +94 -0
- package/dist/retrieval/mmr.js.map +1 -0
- package/dist/retrieval/weightedFusion.d.ts +64 -0
- package/dist/retrieval/weightedFusion.d.ts.map +1 -0
- package/dist/retrieval/weightedFusion.js +80 -0
- package/dist/retrieval/weightedFusion.js.map +1 -0
- package/dist/similarity/index.d.ts +6 -4
- package/dist/similarity/index.d.ts.map +1 -1
- package/dist/similarity/index.js +5 -3
- package/dist/similarity/index.js.map +1 -1
- package/dist/wire/index.d.ts +18 -0
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +17 -0
- package/dist/wire/index.js.map +1 -0
- package/dist/wire/sse.d.ts +29 -0
- package/dist/wire/sse.d.ts.map +1 -0
- package/dist/wire/sse.js +81 -0
- package/dist/wire/sse.js.map +1 -0
- package/dist/wire/usage.d.ts +46 -0
- package/dist/wire/usage.d.ts.map +1 -0
- package/dist/wire/usage.js +78 -0
- package/dist/wire/usage.js.map +1 -0
- package/package.json +11 -3
- package/src/bridges/AnthropicBridge.ts +24 -68
- package/src/bridges/OpenAIBridge.ts +11 -50
- package/src/bridges/VertexAIBridge.ts +3 -32
- package/src/distillation/DistillationEngine.ts +176 -141
- package/src/distillation/index.ts +15 -1
- package/src/distillation/ports.ts +55 -0
- package/src/eval/EvalHarness.ts +12 -1
- package/src/eval/baseline.ts +51 -0
- package/src/eval/index.ts +1 -0
- package/src/eval/types.ts +7 -0
- package/src/index.ts +5 -2
- package/src/rag/EnterpriseRetriever.ts +2 -1
- package/src/retrieval/HybridRetriever.ts +2 -1
- package/src/retrieval/decay.ts +29 -0
- package/src/retrieval/fusion.ts +19 -52
- package/src/retrieval/index.ts +24 -6
- package/src/retrieval/mmr.ts +126 -0
- package/src/retrieval/weightedFusion.ts +101 -0
- package/src/similarity/index.ts +6 -4
- package/src/wire/index.ts +18 -0
- package/src/wire/sse.ts +74 -0
- package/src/wire/usage.ts +95 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seanhogg/builderforce-memory",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.30",
|
|
4
4
|
"description": "BuilderForce Agent Memory — runtime layer. SSM execution, Transformer orchestration, online distillation, enterprise RAG (ingestion pipelines, vector-store adapters, tenant/ACL-scoped hybrid retrieval), multi-agent graph orchestration, LLM telemetry and cost accounting, and persistent agent memory for BuilderForce.ai agents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
"types": "./dist/retrieval/index.d.ts",
|
|
15
15
|
"import": "./dist/retrieval/index.js"
|
|
16
16
|
},
|
|
17
|
+
"./wire": {
|
|
18
|
+
"types": "./dist/wire/index.d.ts",
|
|
19
|
+
"import": "./dist/wire/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./distillation": {
|
|
22
|
+
"types": "./dist/distillation/index.d.ts",
|
|
23
|
+
"import": "./dist/distillation/index.js"
|
|
24
|
+
},
|
|
17
25
|
"./telemetry": {
|
|
18
26
|
"types": "./dist/telemetry/index.d.ts",
|
|
19
27
|
"import": "./dist/telemetry/index.js"
|
|
@@ -87,7 +95,7 @@
|
|
|
87
95
|
},
|
|
88
96
|
"homepage": "https://github.com/SeanHogg/builderforce-memory/tree/main/packages/memory#readme",
|
|
89
97
|
"peerDependencies": {
|
|
90
|
-
"@seanhogg/builderforce-memory-engine": "^2026.9.
|
|
98
|
+
"@seanhogg/builderforce-memory-engine": "^2026.9.30"
|
|
91
99
|
},
|
|
92
100
|
"devDependencies": {
|
|
93
101
|
"@jest/globals": "^29.7.0",
|
|
@@ -101,7 +109,7 @@
|
|
|
101
109
|
"ts-jest": "^29.2.0",
|
|
102
110
|
"typescript": "^6.0.3",
|
|
103
111
|
"@typescript/native-preview": "7.0.0-dev.20260627.2",
|
|
104
|
-
"@seanhogg/builderforce-memory-engine": "2026.9.
|
|
112
|
+
"@seanhogg/builderforce-memory-engine": "2026.9.30"
|
|
105
113
|
},
|
|
106
114
|
"jest": {
|
|
107
115
|
"preset": "ts-jest/presets/default-esm",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { SSMError } from '../errors/SSMError.js';
|
|
9
9
|
import type { LlmUsage } from '../telemetry/types.js';
|
|
10
10
|
import type { TransformerBridge, BridgeGenerateOptions, BridgeCallInfo } from './TransformerBridge.js';
|
|
11
|
+
import { readSseDataFrames, readUsageFields, AnthropicStreamUsage } from '../wire/index.js';
|
|
11
12
|
|
|
12
13
|
export interface AnthropicBridgeOptions {
|
|
13
14
|
/** Anthropic API key. */
|
|
@@ -110,30 +111,26 @@ export class AnthropicBridge implements TransformerBridge {
|
|
|
110
111
|
throw new SSMError('BRIDGE_RESPONSE_INVALID', 'Anthropic streaming response has no body.');
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
input = Math.max(input, Number(usage.input_tokens) || 0);
|
|
125
|
-
output = Math.max(output, Number(usage.output_tokens) || 0);
|
|
126
|
-
cacheRead = Math.max(cacheRead, Number(usage.cache_read_input_tokens) || 0);
|
|
127
|
-
cacheWrite = Math.max(cacheWrite, Number(usage.cache_creation_input_tokens) || 0);
|
|
128
|
-
});
|
|
114
|
+
// `message_start` carries the input split; `message_delta` the running
|
|
115
|
+
// output count. Both are folded so the final usage is provider-measured.
|
|
116
|
+
const usage = new AnthropicStreamUsage();
|
|
117
|
+
for await (const frame of readSseDataFrames(res.body)) {
|
|
118
|
+
usage.observe(frame);
|
|
119
|
+
const ev = frame as { type?: unknown; delta?: { text?: unknown } };
|
|
120
|
+
// content_block_delta events carry the streamed text
|
|
121
|
+
if (ev.type === 'content_block_delta' && typeof ev.delta?.text === 'string' && ev.delta.text.length > 0) {
|
|
122
|
+
yield ev.delta.text;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
129
125
|
|
|
126
|
+
const totals = usage.result();
|
|
130
127
|
this._lastCall = {
|
|
131
128
|
usage: {
|
|
132
|
-
model:
|
|
133
|
-
inputTokens:
|
|
134
|
-
outputTokens:
|
|
135
|
-
cachedInputTokens:
|
|
136
|
-
cacheWriteTokens:
|
|
129
|
+
model: totals.model ?? opts.model ?? this._model,
|
|
130
|
+
inputTokens: totals.inputTokens,
|
|
131
|
+
outputTokens: totals.outputTokens,
|
|
132
|
+
cachedInputTokens: totals.cacheReadTokens,
|
|
133
|
+
cacheWriteTokens: totals.cacheCreationTokens,
|
|
137
134
|
},
|
|
138
135
|
};
|
|
139
136
|
}
|
|
@@ -171,48 +168,6 @@ export class AnthropicBridge implements TransformerBridge {
|
|
|
171
168
|
}
|
|
172
169
|
}
|
|
173
170
|
|
|
174
|
-
// ── SSE parser (Anthropic event format) ──────────────────────────────────────
|
|
175
|
-
|
|
176
|
-
async function* parseAnthropicStream(
|
|
177
|
-
body: ReadableStream<Uint8Array>,
|
|
178
|
-
onEvent?: (event: Record<string, unknown>) => void,
|
|
179
|
-
): AsyncIterable<string> {
|
|
180
|
-
const reader = body.getReader();
|
|
181
|
-
const decoder = new TextDecoder();
|
|
182
|
-
let buffer = '';
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
while (true) {
|
|
186
|
-
const { done, value } = await reader.read();
|
|
187
|
-
if (done) break;
|
|
188
|
-
|
|
189
|
-
buffer += decoder.decode(value, { stream: true });
|
|
190
|
-
const lines = buffer.split('\n');
|
|
191
|
-
buffer = lines.pop() as string; // split() always yields ≥1 element → never undefined
|
|
192
|
-
|
|
193
|
-
for (const line of lines) {
|
|
194
|
-
const trimmed = line.trim();
|
|
195
|
-
if (!trimmed.startsWith('data: ')) continue;
|
|
196
|
-
|
|
197
|
-
const data = trimmed.slice(6);
|
|
198
|
-
try {
|
|
199
|
-
const event = JSON.parse(data) as Record<string, unknown>;
|
|
200
|
-
onEvent?.(event);
|
|
201
|
-
// content_block_delta events carry the streamed text
|
|
202
|
-
if (event['type'] === 'content_block_delta') {
|
|
203
|
-
const text = (event as any).delta?.text;
|
|
204
|
-
if (typeof text === 'string' && text.length > 0) yield text;
|
|
205
|
-
}
|
|
206
|
-
} catch {
|
|
207
|
-
// Skip malformed SSE lines
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
} finally {
|
|
212
|
-
reader.releaseLock();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
171
|
/**
|
|
217
172
|
* Maps an Anthropic `usage` object onto the canonical {@link LlmUsage} shape.
|
|
218
173
|
* Absent when the response omits usage (an older gateway shim), in which case
|
|
@@ -220,12 +175,13 @@ async function* parseAnthropicStream(
|
|
|
220
175
|
*/
|
|
221
176
|
function readAnthropicUsage(usage: unknown, model: string): LlmUsage | undefined {
|
|
222
177
|
if (!usage || typeof usage !== 'object') return undefined;
|
|
223
|
-
|
|
178
|
+
// Anthropic's `input_tokens` already excludes both cache counts.
|
|
179
|
+
const u = readUsageFields(usage);
|
|
224
180
|
return {
|
|
225
181
|
model,
|
|
226
|
-
inputTokens:
|
|
227
|
-
outputTokens:
|
|
228
|
-
cachedInputTokens:
|
|
229
|
-
cacheWriteTokens:
|
|
182
|
+
inputTokens: u.promptTokens ?? 0,
|
|
183
|
+
outputTokens: u.completionTokens ?? 0,
|
|
184
|
+
cachedInputTokens: u.cacheReadTokens ?? 0,
|
|
185
|
+
cacheWriteTokens: u.cacheCreationTokens ?? 0,
|
|
230
186
|
};
|
|
231
187
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { SSMError } from '../errors/SSMError.js';
|
|
9
9
|
import type { LlmUsage } from '../telemetry/types.js';
|
|
10
10
|
import type { TransformerBridge, BridgeGenerateOptions, BridgeCallInfo } from './TransformerBridge.js';
|
|
11
|
+
import { readSseDataFrames, readUsageFields } from '../wire/index.js';
|
|
11
12
|
|
|
12
13
|
export interface OpenAIBridgeOptions {
|
|
13
14
|
/** OpenAI API key (or compatible service key). */
|
|
@@ -88,12 +89,14 @@ export class OpenAIBridge implements TransformerBridge {
|
|
|
88
89
|
const model = opts.model ?? this._model;
|
|
89
90
|
let usage: LlmUsage | undefined;
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
//
|
|
92
|
+
for await (const chunk of readSseDataFrames(res.body)) {
|
|
93
|
+
// Usage is only emitted because `_buildBody` sets `stream_options.include_usage`;
|
|
93
94
|
// without it OpenAI streams no usage at all and cost would be a guess.
|
|
94
|
-
const reported = readOpenAIUsage((
|
|
95
|
+
const reported = readOpenAIUsage((chunk as any).usage, (chunk as any).model ?? model);
|
|
95
96
|
if (reported) usage = reported;
|
|
96
|
-
|
|
97
|
+
const delta = (chunk as any).choices?.[0]?.delta?.content;
|
|
98
|
+
if (typeof delta === 'string' && delta.length > 0) yield delta;
|
|
99
|
+
}
|
|
97
100
|
|
|
98
101
|
this._lastCall = usage ? { usage } : {};
|
|
99
102
|
}
|
|
@@ -130,59 +133,17 @@ export class OpenAIBridge implements TransformerBridge {
|
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
// ── SSE parser ────────────────────────────────────────────────────────────────
|
|
134
|
-
|
|
135
|
-
async function* parseOpenAIStream(
|
|
136
|
-
body: ReadableStream<Uint8Array>,
|
|
137
|
-
onEvent?: (event: Record<string, unknown>) => void,
|
|
138
|
-
): AsyncIterable<string> {
|
|
139
|
-
const reader = body.getReader();
|
|
140
|
-
const decoder = new TextDecoder();
|
|
141
|
-
let buffer = '';
|
|
142
|
-
|
|
143
|
-
try {
|
|
144
|
-
while (true) {
|
|
145
|
-
const { done, value } = await reader.read();
|
|
146
|
-
if (done) break;
|
|
147
|
-
|
|
148
|
-
buffer += decoder.decode(value, { stream: true });
|
|
149
|
-
const lines = buffer.split('\n');
|
|
150
|
-
buffer = lines.pop() as string; // keep the last (possibly partial) line; split() always yields ≥1 element
|
|
151
|
-
|
|
152
|
-
for (const line of lines) {
|
|
153
|
-
const trimmed = line.trim();
|
|
154
|
-
if (!trimmed.startsWith('data: ')) continue;
|
|
155
|
-
|
|
156
|
-
const data = trimmed.slice(6);
|
|
157
|
-
if (data === '[DONE]') return;
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
const chunk = JSON.parse(data) as Record<string, unknown>;
|
|
161
|
-
onEvent?.(chunk);
|
|
162
|
-
const delta = (chunk as any).choices?.[0]?.delta?.content;
|
|
163
|
-
if (typeof delta === 'string' && delta.length > 0) yield delta;
|
|
164
|
-
} catch {
|
|
165
|
-
// Malformed JSON in stream — skip silently
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
} finally {
|
|
170
|
-
reader.releaseLock();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
136
|
/** Maps an OpenAI `usage` object onto the canonical {@link LlmUsage} shape. */
|
|
175
137
|
function readOpenAIUsage(usage: unknown, model: string): LlmUsage | undefined {
|
|
176
138
|
if (!usage || typeof usage !== 'object') return undefined;
|
|
177
|
-
const u = usage
|
|
178
|
-
const cached =
|
|
179
|
-
const prompt = Number(u['prompt_tokens']) || 0;
|
|
139
|
+
const u = readUsageFields(usage);
|
|
140
|
+
const cached = u.cacheReadTokens ?? 0;
|
|
180
141
|
return {
|
|
181
142
|
model,
|
|
182
143
|
// OpenAI reports `prompt_tokens` INCLUSIVE of cached tokens; the canonical
|
|
183
144
|
// shape keeps them disjoint so the two rates are applied exactly once each.
|
|
184
|
-
inputTokens: Math.max(0,
|
|
185
|
-
outputTokens:
|
|
145
|
+
inputTokens: Math.max(0, (u.promptTokens ?? 0) - cached),
|
|
146
|
+
outputTokens: u.completionTokens ?? 0,
|
|
186
147
|
cachedInputTokens: cached,
|
|
187
148
|
};
|
|
188
149
|
}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { SSMError } from '../errors/SSMError.js';
|
|
26
26
|
import type { LlmUsage } from '../telemetry/types.js';
|
|
27
27
|
import type { TransformerBridge, BridgeGenerateOptions, BridgeCallInfo } from './TransformerBridge.js';
|
|
28
|
+
import { readSseDataFrames } from '../wire/index.js';
|
|
28
29
|
|
|
29
30
|
export interface VertexAIBridgeOptions {
|
|
30
31
|
/** GCP project id. */
|
|
@@ -101,7 +102,8 @@ export class VertexAIBridge implements TransformerBridge {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
let usage: LlmUsage | undefined;
|
|
104
|
-
for await (const
|
|
105
|
+
for await (const frame of readSseDataFrames(res.body)) {
|
|
106
|
+
const event = frame as Record<string, unknown>;
|
|
105
107
|
const text = readCandidateText(event);
|
|
106
108
|
if (text) yield text;
|
|
107
109
|
// Vertex repeats cumulative usage on each chunk; the last one wins.
|
|
@@ -264,34 +266,3 @@ function readVertexUsage(usage: unknown, model: string): LlmUsage | undefined {
|
|
|
264
266
|
cachedInputTokens: cached,
|
|
265
267
|
};
|
|
266
268
|
}
|
|
267
|
-
|
|
268
|
-
/** Parses an `alt=sse` stream into JSON events. */
|
|
269
|
-
async function* parseSseJson(body: ReadableStream<Uint8Array>): AsyncIterable<Record<string, unknown>> {
|
|
270
|
-
const reader = body.getReader();
|
|
271
|
-
const decoder = new TextDecoder();
|
|
272
|
-
let buffer = '';
|
|
273
|
-
|
|
274
|
-
try {
|
|
275
|
-
while (true) {
|
|
276
|
-
const { done, value } = await reader.read();
|
|
277
|
-
if (done) break;
|
|
278
|
-
buffer += decoder.decode(value, { stream: true });
|
|
279
|
-
|
|
280
|
-
const lines = buffer.split('\n');
|
|
281
|
-
buffer = lines.pop() as string;
|
|
282
|
-
for (const line of lines) {
|
|
283
|
-
const trimmed = line.trim();
|
|
284
|
-
if (!trimmed.startsWith('data:')) continue;
|
|
285
|
-
const payload = trimmed.slice(5).trim();
|
|
286
|
-
if (!payload || payload === '[DONE]') continue;
|
|
287
|
-
try {
|
|
288
|
-
yield JSON.parse(payload) as Record<string, unknown>;
|
|
289
|
-
} catch {
|
|
290
|
-
// Skip malformed SSE frames rather than failing the stream.
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
} finally {
|
|
295
|
-
reader.releaseLock();
|
|
296
|
-
}
|
|
297
|
-
}
|