@nijaru/pi-provider-compaction 0.0.9 → 0.0.11
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 +7 -5
- package/dist/index.js +226 -123
- package/index.ts +63 -63
- package/package.json +3 -2
- package/policy.ts +9 -16
- package/protocol.ts +16 -13
- package/provider.ts +81 -14
- package/usage-observer.ts +52 -0
package/dist/index.js
CHANGED
|
@@ -16,7 +16,6 @@ var COMPACTION_MODEL_FLAG = "compaction-model";
|
|
|
16
16
|
var PROJECT_POLICY = "compaction-policy.json";
|
|
17
17
|
var AGENT_DIR_OVERRIDE = "PI_COMPACTOR_AGENT_DIR";
|
|
18
18
|
var MAX_POLICY_BYTES = 64 * 1024;
|
|
19
|
-
var MAX_MODEL_SELECTORS = 8;
|
|
20
19
|
var MAX_SELECTOR_LENGTH = 512;
|
|
21
20
|
function normalizeSelector(value) {
|
|
22
21
|
if (typeof value !== "string")
|
|
@@ -26,23 +25,14 @@ function normalizeSelector(value) {
|
|
|
26
25
|
return;
|
|
27
26
|
return selector;
|
|
28
27
|
}
|
|
29
|
-
function
|
|
28
|
+
function policyHasSelectors(value) {
|
|
30
29
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
31
30
|
return;
|
|
32
31
|
if (!("models" in value) || !Array.isArray(value.models))
|
|
33
32
|
return;
|
|
34
|
-
|
|
35
|
-
for (const model of value.models) {
|
|
36
|
-
const selector = normalizeSelector(model);
|
|
37
|
-
if (selector === undefined || selectors.includes(selector))
|
|
38
|
-
continue;
|
|
39
|
-
selectors.push(selector);
|
|
40
|
-
if (selectors.length === MAX_MODEL_SELECTORS)
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
return selectors;
|
|
33
|
+
return value.models.some((model) => normalizeSelector(model) !== undefined);
|
|
44
34
|
}
|
|
45
|
-
function
|
|
35
|
+
function readPolicySelection(configPath) {
|
|
46
36
|
let fd;
|
|
47
37
|
try {
|
|
48
38
|
fd = openSync(configPath, fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
|
|
@@ -59,7 +49,7 @@ function readPolicySelectors(configPath) {
|
|
|
59
49
|
}
|
|
60
50
|
if (bytesRead > MAX_POLICY_BYTES)
|
|
61
51
|
return;
|
|
62
|
-
return
|
|
52
|
+
return policyHasSelectors(JSON.parse(buffer.subarray(0, bytesRead).toString("utf8")));
|
|
63
53
|
} catch {
|
|
64
54
|
return;
|
|
65
55
|
} finally {
|
|
@@ -141,9 +131,9 @@ function resolveCompactionModelPolicy(pi, ctx, argv = process.argv.slice(2)) {
|
|
|
141
131
|
}
|
|
142
132
|
candidates.push({ path: join(agentDir, PROJECT_POLICY), source: "agent-policy" });
|
|
143
133
|
for (const candidate of candidates) {
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
return
|
|
134
|
+
const hasSelectors = readPolicySelection(candidate.path);
|
|
135
|
+
if (hasSelectors !== undefined) {
|
|
136
|
+
return hasSelectors ? { hasSelectors: true, source: candidate.source } : { hasSelectors: false };
|
|
147
137
|
}
|
|
148
138
|
}
|
|
149
139
|
return { hasSelectors: false };
|
|
@@ -1170,7 +1160,7 @@ async function collectProviderUsage(stream, signal) {
|
|
|
1170
1160
|
if (signal.aborted)
|
|
1171
1161
|
throw new DOMException("Compaction aborted", "AbortError");
|
|
1172
1162
|
if (event.type === "error")
|
|
1173
|
-
throw new
|
|
1163
|
+
throw new NativeCompactionValidationError("Native compaction provider stream failed", event.error.usage);
|
|
1174
1164
|
if (event.type === "done")
|
|
1175
1165
|
usage = event.message.usage;
|
|
1176
1166
|
}
|
|
@@ -1216,14 +1206,15 @@ async function requestResponsesCompact(request) {
|
|
|
1216
1206
|
if (!response.ok)
|
|
1217
1207
|
return response;
|
|
1218
1208
|
const parsed = await readJsonBounded(response, request.signal);
|
|
1209
|
+
if (!isObject(parsed))
|
|
1210
|
+
throw new Error("Responses Compact returned a non-object response");
|
|
1211
|
+
const completion = syntheticCompletion(parsed, preparedPayload);
|
|
1219
1212
|
try {
|
|
1220
|
-
|
|
1221
|
-
throw new Error("Responses Compact returned a non-object response");
|
|
1222
|
-
compacted = { response: parsed, output: validateCompactedOutput(parsed) };
|
|
1213
|
+
compacted = validateCompactedOutput(parsed);
|
|
1223
1214
|
} catch (error) {
|
|
1224
|
-
|
|
1215
|
+
bridgeError = new NativeCompactionValidationError(error instanceof Error ? error.message : String(error));
|
|
1225
1216
|
}
|
|
1226
|
-
return
|
|
1217
|
+
return completion;
|
|
1227
1218
|
} catch (error) {
|
|
1228
1219
|
bridgeError = error;
|
|
1229
1220
|
return errorResponse(error);
|
|
@@ -1238,6 +1229,7 @@ async function requestResponsesCompact(request) {
|
|
|
1238
1229
|
transport: "sse",
|
|
1239
1230
|
cacheRetention: "none",
|
|
1240
1231
|
maxRetries: 0,
|
|
1232
|
+
sessionId: request.sessionId,
|
|
1241
1233
|
fetch: bridgeFetch,
|
|
1242
1234
|
onPayload: (payload) => {
|
|
1243
1235
|
assertCurrent(request);
|
|
@@ -1251,7 +1243,7 @@ async function requestResponsesCompact(request) {
|
|
|
1251
1243
|
});
|
|
1252
1244
|
const usage = await collectProviderUsage(stream, request.signal).catch((error) => {
|
|
1253
1245
|
if (bridgeError)
|
|
1254
|
-
throw
|
|
1246
|
+
throw new NativeCompactionValidationError("Native compaction transport or validation failed", error instanceof NativeCompactionValidationError ? error.usage : undefined);
|
|
1255
1247
|
throw error;
|
|
1256
1248
|
});
|
|
1257
1249
|
if (bridgeError instanceof NativeCompactionValidationError)
|
|
@@ -1260,7 +1252,7 @@ async function requestResponsesCompact(request) {
|
|
|
1260
1252
|
throw bridgeError;
|
|
1261
1253
|
if (!compacted || dispatches !== 1)
|
|
1262
1254
|
throw new Error("Responses Compact did not complete exactly one request");
|
|
1263
|
-
return { output: compacted
|
|
1255
|
+
return { output: compacted, usage };
|
|
1264
1256
|
}
|
|
1265
1257
|
async function collectCompactionSse(stream, signal) {
|
|
1266
1258
|
const reader = stream.getReader();
|
|
@@ -1405,6 +1397,7 @@ async function requestRemoteV2(request) {
|
|
|
1405
1397
|
transport: "sse",
|
|
1406
1398
|
cacheRetention: "none",
|
|
1407
1399
|
maxRetries: 0,
|
|
1400
|
+
sessionId: request.sessionId,
|
|
1408
1401
|
fetch: inspectedFetch,
|
|
1409
1402
|
onPayload: (payload) => {
|
|
1410
1403
|
assertCurrent(request);
|
|
@@ -1419,10 +1412,10 @@ async function requestRemoteV2(request) {
|
|
|
1419
1412
|
try {
|
|
1420
1413
|
const usage = await collectProviderUsage(stream, request.signal);
|
|
1421
1414
|
if (!sentInput || inspections.length !== 1)
|
|
1422
|
-
throw new
|
|
1415
|
+
throw new NativeCompactionValidationError(`Remote V2 observed ${inspections.length} successful provider responses; expected exactly one`, usage);
|
|
1423
1416
|
const inspection = await inspections[0];
|
|
1424
1417
|
if ("error" in inspection)
|
|
1425
|
-
throw
|
|
1418
|
+
throw new NativeCompactionValidationError("Remote V2 checkpoint validation failed", usage);
|
|
1426
1419
|
return { output: buildRemoteV2History(sentInput, inspection.result.item), usage };
|
|
1427
1420
|
} finally {
|
|
1428
1421
|
controller.abort();
|
|
@@ -1825,17 +1818,26 @@ async function cachedSummary(request, context, preparation, instructions, signal
|
|
|
1825
1818
|
}
|
|
1826
1819
|
|
|
1827
1820
|
// provider.ts
|
|
1821
|
+
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
1822
|
+
var USAGE_OWNERS = Symbol.for("@nijaru/pi-provider-compaction/usage-owners");
|
|
1823
|
+
var usageGlobal = globalThis;
|
|
1824
|
+
var usageOwners = usageGlobal[USAGE_OWNERS] ??= new WeakMap;
|
|
1825
|
+
function bindUsageHeaders(headers, sessionId) {
|
|
1826
|
+
usageOwners.set(headers, sessionId);
|
|
1827
|
+
}
|
|
1828
1828
|
var STREAM_OBSERVER = Symbol.for("@nijaru/pi-provider-compaction/stream-observer");
|
|
1829
1829
|
function observedStream(model, context, options, observe, dispatch) {
|
|
1830
|
+
const headerOwner = options?.headers ? usageOwners.get(options.headers) : undefined;
|
|
1831
|
+
const scope = { sessionId: headerOwner ?? options?.sessionId };
|
|
1830
1832
|
let handler;
|
|
1831
1833
|
try {
|
|
1832
|
-
handler = observe(model, context, options ?? {});
|
|
1834
|
+
handler = observe(model, context, options ?? {}, scope);
|
|
1833
1835
|
} catch {
|
|
1834
1836
|
return dispatch(options);
|
|
1835
1837
|
}
|
|
1836
1838
|
if (!handler)
|
|
1837
1839
|
return dispatch(options);
|
|
1838
|
-
const
|
|
1840
|
+
const run = () => dispatch({
|
|
1839
1841
|
...options,
|
|
1840
1842
|
...handler.cacheRetention === undefined ? {} : { cacheRetention: handler.cacheRetention },
|
|
1841
1843
|
onPayload: async (payload, requestModel) => {
|
|
@@ -1848,14 +1850,64 @@ function observedStream(model, context, options, observe, dispatch) {
|
|
|
1848
1850
|
}
|
|
1849
1851
|
}
|
|
1850
1852
|
});
|
|
1853
|
+
const stream = run();
|
|
1854
|
+
if (handler.priceUsage)
|
|
1855
|
+
return pricedStream(stream, model, handler);
|
|
1851
1856
|
if (handler.complete)
|
|
1852
1857
|
stream.result().then(handler.complete).catch(() => {});
|
|
1853
1858
|
return stream;
|
|
1854
1859
|
}
|
|
1860
|
+
function pricedStream(stream, model, handler) {
|
|
1861
|
+
const out = createAssistantMessageEventStream();
|
|
1862
|
+
(async () => {
|
|
1863
|
+
let final;
|
|
1864
|
+
let partial;
|
|
1865
|
+
try {
|
|
1866
|
+
for await (const event of stream) {
|
|
1867
|
+
if ("partial" in event)
|
|
1868
|
+
partial = event.partial;
|
|
1869
|
+
if (event.type === "done" || event.type === "error") {
|
|
1870
|
+
final = event.type === "done" ? event.message : event.error;
|
|
1871
|
+
try {
|
|
1872
|
+
handler.priceUsage?.(final);
|
|
1873
|
+
} catch {}
|
|
1874
|
+
try {
|
|
1875
|
+
handler.complete?.(final);
|
|
1876
|
+
} catch {}
|
|
1877
|
+
}
|
|
1878
|
+
out.push(event);
|
|
1879
|
+
}
|
|
1880
|
+
if (final) {
|
|
1881
|
+
out.end(final);
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
} catch {}
|
|
1885
|
+
const message = {
|
|
1886
|
+
...partial ?? {
|
|
1887
|
+
role: "assistant",
|
|
1888
|
+
api: model.api,
|
|
1889
|
+
provider: model.provider,
|
|
1890
|
+
model: model.id,
|
|
1891
|
+
content: [],
|
|
1892
|
+
timestamp: Date.now(),
|
|
1893
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }
|
|
1894
|
+
},
|
|
1895
|
+
stopReason: "error",
|
|
1896
|
+
errorMessage: "Provider stream ended before a terminal event"
|
|
1897
|
+
};
|
|
1898
|
+
try {
|
|
1899
|
+
handler.priceUsage?.(message);
|
|
1900
|
+
} catch {}
|
|
1901
|
+
out.push({ type: "error", reason: "error", error: message });
|
|
1902
|
+
out.end(message);
|
|
1903
|
+
})();
|
|
1904
|
+
return out;
|
|
1905
|
+
}
|
|
1855
1906
|
function observerLeases(provider, owned, restore) {
|
|
1856
1907
|
const leases = new Map;
|
|
1857
1908
|
let closed = false;
|
|
1858
|
-
const
|
|
1909
|
+
const live = (token) => !closed && leases.has(token);
|
|
1910
|
+
const active = (token) => live(token) && leases.get(token)?.current() === true && owned();
|
|
1859
1911
|
const release = () => {
|
|
1860
1912
|
if (closed)
|
|
1861
1913
|
return;
|
|
@@ -1874,6 +1926,10 @@ function observerLeases(provider, owned, restore) {
|
|
|
1874
1926
|
continue;
|
|
1875
1927
|
handlers.push(Object.assign((payload) => active(token) ? handler(payload) : payload, {
|
|
1876
1928
|
cacheRetention: handler.cacheRetention,
|
|
1929
|
+
priceUsage: handler.priceUsage ? (message) => {
|
|
1930
|
+
if (live(token))
|
|
1931
|
+
handler.priceUsage(message);
|
|
1932
|
+
} : undefined,
|
|
1877
1933
|
complete: handler.complete ? (message) => {
|
|
1878
1934
|
if (active(token))
|
|
1879
1935
|
handler.complete(message);
|
|
@@ -1894,6 +1950,13 @@ function observerLeases(provider, owned, restore) {
|
|
|
1894
1950
|
return payload;
|
|
1895
1951
|
}, {
|
|
1896
1952
|
cacheRetention: [...handlers].reverse().find((handler) => handler.cacheRetention !== undefined)?.cacheRetention,
|
|
1953
|
+
priceUsage: handlers.some((handler) => handler.priceUsage) ? (message) => {
|
|
1954
|
+
for (const handler of handlers) {
|
|
1955
|
+
try {
|
|
1956
|
+
handler.priceUsage?.(message);
|
|
1957
|
+
} catch {}
|
|
1958
|
+
}
|
|
1959
|
+
} : undefined,
|
|
1897
1960
|
complete: handlers.some((handler) => handler.complete) ? (message) => {
|
|
1898
1961
|
for (const handler of handlers) {
|
|
1899
1962
|
try {
|
|
@@ -1918,7 +1981,7 @@ function observerLeases(provider, owned, restore) {
|
|
|
1918
1981
|
}
|
|
1919
1982
|
};
|
|
1920
1983
|
}
|
|
1921
|
-
var LEASE_TABLES = Symbol.for("@nijaru/pi-provider-compaction/observer-leases-
|
|
1984
|
+
var LEASE_TABLES = Symbol.for("@nijaru/pi-provider-compaction/observer-leases-v2");
|
|
1922
1985
|
var shared = globalThis;
|
|
1923
1986
|
var { effectiveLeases, effectiveRestores, nativeLeases, legacyLeases } = shared[LEASE_TABLES] ??= {
|
|
1924
1987
|
effectiveLeases: new WeakMap,
|
|
@@ -1994,13 +2057,21 @@ function installObserver(pi, ctx, observe) {
|
|
|
1994
2057
|
return;
|
|
1995
2058
|
const registry = ctx.modelRegistry;
|
|
1996
2059
|
const id = model.provider;
|
|
2060
|
+
const observeApi = (requestModel, ...args) => requestModel.api === model.api ? observe(requestModel, ...args) : undefined;
|
|
2061
|
+
const tagged = (lease) => lease ? Object.assign(lease, { api: model.api }) : undefined;
|
|
2062
|
+
const effective = () => {
|
|
2063
|
+
const base = registry.getProvider(id);
|
|
2064
|
+
if (!base?.getModels().some((entry) => entry.api === model.api))
|
|
2065
|
+
return;
|
|
2066
|
+
return tagged(leaseEffectiveProvider(base, () => registry.getProvider(id), observeApi));
|
|
2067
|
+
};
|
|
1997
2068
|
const native = registry.getRegisteredNativeProvider(id);
|
|
1998
2069
|
if (native) {
|
|
1999
2070
|
if (typeof native.getModels !== "function" || !native.getModels().some((entry) => entry.api === model.api))
|
|
2000
|
-
return;
|
|
2071
|
+
return effective();
|
|
2001
2072
|
const existing = nativeLeases.get(native);
|
|
2002
2073
|
if (existing)
|
|
2003
|
-
return existing.add(
|
|
2074
|
+
return tagged(existing.add(observeApi));
|
|
2004
2075
|
const leases = observerLeases(id, () => registry.getRegisteredNativeProvider(id) === wrapper, () => {
|
|
2005
2076
|
if (registry.getRegisteredNativeProvider(id) === wrapper)
|
|
2006
2077
|
registry.registerProvider(native);
|
|
@@ -2009,15 +2080,15 @@ function installObserver(pi, ctx, observe) {
|
|
|
2009
2080
|
const wrapper = decorateProvider(native, leases.observe);
|
|
2010
2081
|
nativeLeases.set(wrapper, leases);
|
|
2011
2082
|
pi.registerProvider(wrapper);
|
|
2012
|
-
return leases.add(
|
|
2083
|
+
return tagged(leases.add(observeApi));
|
|
2013
2084
|
}
|
|
2014
2085
|
const original = registry.getRegisteredProviderConfig(id);
|
|
2015
2086
|
if (!original?.streamSimple || original.refreshModels || original.oauth?.modifyModels || original.api !== model.api) {
|
|
2016
|
-
return
|
|
2087
|
+
return effective();
|
|
2017
2088
|
}
|
|
2018
2089
|
const existing = legacyLeases.get(original.streamSimple);
|
|
2019
2090
|
if (existing)
|
|
2020
|
-
return existing.add(
|
|
2091
|
+
return tagged(existing.add(observeApi));
|
|
2021
2092
|
const delegate = original.streamSimple.bind(original);
|
|
2022
2093
|
const owned = () => registry.getRegisteredProviderConfig(id)?.streamSimple === wrapper && !registry.getRegisteredNativeProvider(id);
|
|
2023
2094
|
const leases = observerLeases(id, owned, () => {
|
|
@@ -2032,7 +2103,62 @@ function installObserver(pi, ctx, observe) {
|
|
|
2032
2103
|
Object.defineProperty(wrapper, STREAM_OBSERVER, { value: { delegate: original.streamSimple, release: leases.release } });
|
|
2033
2104
|
legacyLeases.set(wrapper, leases);
|
|
2034
2105
|
pi.registerProvider(id, { ...original, streamSimple: wrapper });
|
|
2035
|
-
return leases.add(
|
|
2106
|
+
return tagged(leases.add(observeApi));
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
// usage-observer.ts
|
|
2110
|
+
var REQUEST = "pi-provider-compaction:observe-usage";
|
|
2111
|
+
var READY = "pi-provider-compaction:usage-observer-ready";
|
|
2112
|
+
function serveUsageObservers(pi, current) {
|
|
2113
|
+
const leases = new Set;
|
|
2114
|
+
const remove = pi.events.on(REQUEST, (value) => {
|
|
2115
|
+
const request = value;
|
|
2116
|
+
const owner = current();
|
|
2117
|
+
if (!owner || !request || request.handled || !request.context || !request.model || typeof request.priceFor !== "function" || typeof request.respond !== "function")
|
|
2118
|
+
return;
|
|
2119
|
+
try {
|
|
2120
|
+
if (request.context.modelRegistry !== owner.modelRegistry || request.context.sessionManager.getSessionId() !== owner.sessionManager.getSessionId())
|
|
2121
|
+
return;
|
|
2122
|
+
request.handled = true;
|
|
2123
|
+
const sessionId = owner.sessionManager.getSessionId();
|
|
2124
|
+
const installed = installObserver(pi, { model: request.model, modelRegistry: owner.modelRegistry }, (model, _context, _options, scope) => {
|
|
2125
|
+
const price = request.priceFor({ provider: model.provider, id: model.id });
|
|
2126
|
+
return Object.assign((payload) => payload, { priceUsage: (message) => {
|
|
2127
|
+
if (scope.sessionId === sessionId)
|
|
2128
|
+
price(message.timestamp, message.usage);
|
|
2129
|
+
} });
|
|
2130
|
+
});
|
|
2131
|
+
if (!installed) {
|
|
2132
|
+
request.respond(undefined);
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
const lease = {
|
|
2136
|
+
provider: installed.provider,
|
|
2137
|
+
api: installed.api,
|
|
2138
|
+
isInstalled: () => installed.isInstalled(),
|
|
2139
|
+
remove() {
|
|
2140
|
+
leases.delete(lease);
|
|
2141
|
+
installed.remove();
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
leases.add(lease);
|
|
2145
|
+
request.respond(lease);
|
|
2146
|
+
} catch {}
|
|
2147
|
+
});
|
|
2148
|
+
return {
|
|
2149
|
+
ready(context) {
|
|
2150
|
+
pi.events.emit(READY, { context });
|
|
2151
|
+
},
|
|
2152
|
+
clear() {
|
|
2153
|
+
for (const lease of leases)
|
|
2154
|
+
lease.remove();
|
|
2155
|
+
},
|
|
2156
|
+
dispose() {
|
|
2157
|
+
remove();
|
|
2158
|
+
for (const lease of leases)
|
|
2159
|
+
lease.remove();
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2036
2162
|
}
|
|
2037
2163
|
|
|
2038
2164
|
// index.ts
|
|
@@ -2136,9 +2262,10 @@ function pi_provider_compaction_default(pi) {
|
|
|
2136
2262
|
const routeDigest = (value) => createHmac("sha256", secret).update(JSON.stringify(value)).digest("hex");
|
|
2137
2263
|
let runtimeId = randomUUID2();
|
|
2138
2264
|
let ctx;
|
|
2265
|
+
let usageObservers = serveUsageObservers(pi, () => ctx);
|
|
2139
2266
|
let snapshot;
|
|
2140
2267
|
let cache;
|
|
2141
|
-
let
|
|
2268
|
+
let compactionRequest = false;
|
|
2142
2269
|
let expiryAttempt;
|
|
2143
2270
|
let lastOutcome;
|
|
2144
2271
|
let active;
|
|
@@ -2147,30 +2274,10 @@ function pi_provider_compaction_default(pi) {
|
|
|
2147
2274
|
let unregisterCleanup;
|
|
2148
2275
|
let generation = 0;
|
|
2149
2276
|
let requestSequence = 0;
|
|
2150
|
-
let captureStatus = "no request observed";
|
|
2151
|
-
let compactionStatus = "not attempted in this runtime";
|
|
2152
2277
|
let fallbackReason;
|
|
2153
|
-
const captureRejected = (reason) => {
|
|
2154
|
-
captureStatus = reason;
|
|
2155
|
-
};
|
|
2156
2278
|
const fallback = (reason) => {
|
|
2157
2279
|
fallbackReason = reason;
|
|
2158
|
-
compactionStatus = `portable: ${reason}`;
|
|
2159
2280
|
};
|
|
2160
|
-
pi.registerCommand("provider-compaction-status", {
|
|
2161
|
-
description: "Show cache/native eligibility and measured compaction usage",
|
|
2162
|
-
handler: async (_args, context) => {
|
|
2163
|
-
const state = cacheState(context);
|
|
2164
|
-
const remaining = state.expiresAt === undefined ? "unknown lifetime" : `estimated ${Math.max(0, Math.ceil((state.expiresAt - Date.now()) / 1000))}s remaining (model declaration; best effort)`;
|
|
2165
|
-
const usage = lastOutcome?.usage;
|
|
2166
|
-
context.ui.notify(`Provider compaction
|
|
2167
|
-
Observer: ${observer?.isInstalled() ? "installed" : "unavailable for current registration"}
|
|
2168
|
-
Capture: ${captureStatus}
|
|
2169
|
-
Cache: ${state.reason}; ${remaining}
|
|
2170
|
-
Last attempt: ${compactionStatus}${lastOutcome ? `
|
|
2171
|
-
Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usage.input}, cache read ${usage.cacheRead}, write ${usage.cacheWrite}, output ${usage.output}; catalog cost $${usage.cost.total.toFixed(6)}` : "; usage unavailable"}${lastOutcome.reason ? `; ${lastOutcome.reason}` : ""}` : ""}`, "info");
|
|
2172
|
-
}
|
|
2173
|
-
});
|
|
2174
2281
|
const invalidate = () => {
|
|
2175
2282
|
generation++;
|
|
2176
2283
|
runtimeId = randomUUID2();
|
|
@@ -2179,9 +2286,9 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2179
2286
|
active = undefined;
|
|
2180
2287
|
pending = undefined;
|
|
2181
2288
|
expiryAttempt = undefined;
|
|
2182
|
-
captureStatus = "invalidated by lifecycle/history/policy change";
|
|
2183
2289
|
};
|
|
2184
2290
|
const retire = () => {
|
|
2291
|
+
usageObservers.clear();
|
|
2185
2292
|
invalidate();
|
|
2186
2293
|
const installed = observer;
|
|
2187
2294
|
observer = undefined;
|
|
@@ -2191,6 +2298,10 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2191
2298
|
installed?.remove();
|
|
2192
2299
|
lastOutcome = undefined;
|
|
2193
2300
|
};
|
|
2301
|
+
pi.on("before_provider_headers", (event, context) => {
|
|
2302
|
+
bindUsageHeaders(event.headers, context.sessionManager.getSessionId());
|
|
2303
|
+
usageObservers.ready(context);
|
|
2304
|
+
});
|
|
2194
2305
|
const generic = (context) => resolveCompactionModelPolicy(pi, context).hasSelectors;
|
|
2195
2306
|
const route = (model, options) => routeDigest({ model, apiKey: options.apiKey, headers: options.headers, env: options.env });
|
|
2196
2307
|
const unchanged = (branch, length, hash) => branch.length >= length && digest(branch.slice(0, length)) === hash && !branch.slice(length).some((entry) => entry.type === "context_edit");
|
|
@@ -2262,25 +2373,14 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2262
2373
|
}
|
|
2263
2374
|
if (options.sessionId !== manager.getSessionId())
|
|
2264
2375
|
return;
|
|
2265
|
-
if (
|
|
2266
|
-
return;
|
|
2267
|
-
if (!supportsNativeCompaction(model) && !supportsCachedSummary(model)) {
|
|
2268
|
-
captureRejected("unsupported API");
|
|
2376
|
+
if (compactionRequest)
|
|
2269
2377
|
return;
|
|
2270
|
-
|
|
2271
|
-
if (!options.onPayload) {
|
|
2272
|
-
if (captureStatus !== "eligible prepared request captured")
|
|
2273
|
-
captureRejected("no payload callback");
|
|
2378
|
+
if (!supportsNativeCompaction(model) && !supportsCachedSummary(model))
|
|
2274
2379
|
return;
|
|
2275
|
-
|
|
2276
|
-
if (generic(owner)) {
|
|
2277
|
-
captureRejected("generic compaction model takes precedence");
|
|
2380
|
+
if (!options.onPayload || generic(owner))
|
|
2278
2381
|
return;
|
|
2279
|
-
|
|
2280
|
-
if (!supportedRouteOptions(model, options)) {
|
|
2281
|
-
captureRejected("unsupported route overrides");
|
|
2382
|
+
if (!supportedRouteOptions(model, options))
|
|
2282
2383
|
return;
|
|
2283
|
-
}
|
|
2284
2384
|
const ticket = generation;
|
|
2285
2385
|
const startedAt = Date.now();
|
|
2286
2386
|
const sequence = ++requestSequence;
|
|
@@ -2311,14 +2411,11 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2311
2411
|
if (!auth.ok || generic(owner) || auth.apiKey !== options.apiKey || (auth.baseUrl ?? model.baseUrl) !== model.baseUrl || !equal(normalizedHeaders(auth.headers), normalizedHeaders(options.headers)) || !equal(auth.env ?? {}, options.env ?? {})) {
|
|
2312
2412
|
active = undefined;
|
|
2313
2413
|
cache = undefined;
|
|
2314
|
-
captureRejected("resolved auth/route differs from prepared request, or generic policy changed");
|
|
2315
2414
|
return payload;
|
|
2316
2415
|
}
|
|
2317
2416
|
const provider = owner.modelRegistry.getProvider(model.provider);
|
|
2318
|
-
if (!provider)
|
|
2319
|
-
captureRejected("provider unavailable");
|
|
2417
|
+
if (!provider)
|
|
2320
2418
|
return payload;
|
|
2321
|
-
}
|
|
2322
2419
|
const routeHash = route(model, options);
|
|
2323
2420
|
if (config.cachedSummaries && supportsCachedSummary(model)) {
|
|
2324
2421
|
const warming = options.maxTokens === 1 && cache && cache.authHash === routeDigest(auth) && sameCachedPrompt(cache.payload, payload) && equal(cache.context, context);
|
|
@@ -2337,11 +2434,9 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2337
2434
|
if (!warming)
|
|
2338
2435
|
cache = undefined;
|
|
2339
2436
|
}
|
|
2340
|
-
if (!supportsNativeCompaction(model))
|
|
2341
|
-
captureStatus = candidate ? "eligible cached request captured" : "cached request ineligible";
|
|
2437
|
+
if (!supportsNativeCompaction(model))
|
|
2342
2438
|
return payload;
|
|
2343
|
-
|
|
2344
|
-
const prepared = capturePreparedRequest(model, context, canonical, payload, captureRejected);
|
|
2439
|
+
const prepared = capturePreparedRequest(model, context, canonical, payload);
|
|
2345
2440
|
if (!prepared) {
|
|
2346
2441
|
active = undefined;
|
|
2347
2442
|
return payload;
|
|
@@ -2370,7 +2465,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2370
2465
|
active = undefined;
|
|
2371
2466
|
} else
|
|
2372
2467
|
active = undefined;
|
|
2373
|
-
captureStatus = "eligible prepared request captured";
|
|
2374
2468
|
snapshot = {
|
|
2375
2469
|
prepared,
|
|
2376
2470
|
model: structuredClone(model),
|
|
@@ -2398,7 +2492,7 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2398
2492
|
};
|
|
2399
2493
|
const install = (context) => {
|
|
2400
2494
|
ctx = context;
|
|
2401
|
-
if (observer?.provider === context.model?.provider && observer?.isInstalled())
|
|
2495
|
+
if (observer?.provider === context.model?.provider && observer?.api === context.model?.api && observer?.isInstalled())
|
|
2402
2496
|
return;
|
|
2403
2497
|
if (observer) {
|
|
2404
2498
|
invalidate();
|
|
@@ -2408,15 +2502,19 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2408
2502
|
if (!supportsNativeCompaction(context.model) && !supportsCachedSummary(context.model))
|
|
2409
2503
|
return;
|
|
2410
2504
|
observer = installObserver(pi, context, observe);
|
|
2411
|
-
captureStatus = observer ? "awaiting prepared request" : "unsupported provider registration";
|
|
2412
2505
|
};
|
|
2413
2506
|
pi.on("session_start", (_event, context) => {
|
|
2414
2507
|
retire();
|
|
2508
|
+
usageObservers.dispose();
|
|
2509
|
+
usageObservers = serveUsageObservers(pi, () => ctx);
|
|
2415
2510
|
install(context);
|
|
2511
|
+
usageObservers.ready(context);
|
|
2416
2512
|
const sessionId = context.sessionManager.getSessionId();
|
|
2417
2513
|
unregisterCleanup = registerSessionResourceCleanup((disposedId) => {
|
|
2418
|
-
if (disposedId === undefined || disposedId === sessionId)
|
|
2514
|
+
if (disposedId === undefined || disposedId === sessionId) {
|
|
2419
2515
|
retire();
|
|
2516
|
+
usageObservers.dispose();
|
|
2517
|
+
}
|
|
2420
2518
|
});
|
|
2421
2519
|
const latest = latestCompaction(context.sessionManager.getBranch());
|
|
2422
2520
|
if (latest?.summary === NATIVE_COMPACTION_SUMMARY)
|
|
@@ -2424,23 +2522,28 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2424
2522
|
});
|
|
2425
2523
|
pi.on("before_agent_start", (_event, context) => {
|
|
2426
2524
|
install(context);
|
|
2525
|
+
usageObservers.ready(context);
|
|
2427
2526
|
});
|
|
2428
2527
|
pi.on("model_select", (_event, context) => {
|
|
2429
2528
|
invalidate();
|
|
2430
2529
|
install(context);
|
|
2530
|
+
usageObservers.ready(context);
|
|
2431
2531
|
});
|
|
2432
2532
|
pi.on("session_tree", () => {
|
|
2433
2533
|
invalidate();
|
|
2434
2534
|
});
|
|
2435
|
-
pi.on("session_shutdown",
|
|
2535
|
+
pi.on("session_shutdown", () => {
|
|
2536
|
+
retire();
|
|
2537
|
+
usageObservers.dispose();
|
|
2538
|
+
});
|
|
2436
2539
|
const removeInvalidation = pi.events.on("pi-provider-compaction:invalidate", invalidate);
|
|
2437
2540
|
pi.on("session_shutdown", () => {
|
|
2438
2541
|
if (typeof removeInvalidation === "function")
|
|
2439
2542
|
removeInvalidation();
|
|
2440
2543
|
});
|
|
2441
2544
|
pi.on("session_before_compact", async (event, context) => {
|
|
2442
|
-
const began = Date.now();
|
|
2443
|
-
|
|
2545
|
+
const began = Date.now(), ticket = generation;
|
|
2546
|
+
const sessionId = context.sessionManager.getSessionId();
|
|
2444
2547
|
fallbackReason = undefined;
|
|
2445
2548
|
const attempt = expiryAttempt;
|
|
2446
2549
|
const capture = snapshot;
|
|
@@ -2449,7 +2552,12 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2449
2552
|
cache = undefined;
|
|
2450
2553
|
pending = undefined;
|
|
2451
2554
|
const cancelled = (usage, reason = "compaction ownership or input changed") => {
|
|
2452
|
-
|
|
2555
|
+
if (usage && ticket === generation && ctx) {
|
|
2556
|
+
try {
|
|
2557
|
+
if (ctx.sessionManager.getSessionId() === sessionId)
|
|
2558
|
+
pi.appendEntry("pi-provider-compaction:diagnostic", { status: `${attempt ? "expiry cancelled" : "cancelled"}: ${reason}`, usage });
|
|
2559
|
+
} catch {}
|
|
2560
|
+
}
|
|
2453
2561
|
return { cancel: true };
|
|
2454
2562
|
};
|
|
2455
2563
|
const reject = (reason) => {
|
|
@@ -2463,7 +2571,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2463
2571
|
}
|
|
2464
2572
|
if (!capture && !cached || !context.model || generic(context) || event.signal.aborted)
|
|
2465
2573
|
return reject("no eligible snapshot, explicit model override, or aborted attempt");
|
|
2466
|
-
const ticket = generation;
|
|
2467
2574
|
const branch = context.sessionManager.getBranch();
|
|
2468
2575
|
const branchHash = digest(branch);
|
|
2469
2576
|
const source = cached ?? capture;
|
|
@@ -2488,8 +2595,7 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2488
2595
|
let summaryPath = "standard";
|
|
2489
2596
|
let summaryReason;
|
|
2490
2597
|
if (cached) {
|
|
2491
|
-
|
|
2492
|
-
summarizing = true;
|
|
2598
|
+
compactionRequest = true;
|
|
2493
2599
|
try {
|
|
2494
2600
|
const result = await cachedSummary(cached, context, event.preparation, event.customInstructions, event.signal, current);
|
|
2495
2601
|
portable = result.result;
|
|
@@ -2498,7 +2604,7 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2498
2604
|
if (portable)
|
|
2499
2605
|
summaryPath = "cached";
|
|
2500
2606
|
} finally {
|
|
2501
|
-
|
|
2607
|
+
compactionRequest = false;
|
|
2502
2608
|
}
|
|
2503
2609
|
if (!current())
|
|
2504
2610
|
return cancelled(attemptedUsage);
|
|
@@ -2508,7 +2614,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2508
2614
|
return cancelled(attemptedUsage, summaryReason ?? "warm cache unavailable");
|
|
2509
2615
|
if (!input && !attemptedUsage)
|
|
2510
2616
|
return reject(summaryReason ?? fallbackReason ?? "native coverage unavailable");
|
|
2511
|
-
compactionStatus = "generating portable summary";
|
|
2512
2617
|
try {
|
|
2513
2618
|
portable = await compact(event.preparation, source.model, auth.apiKey, auth.headers, event.customInstructions, event.signal, context.thinkingLevel, source.provider.streamSimple.bind(source.provider), auth.env, undefined, undefined, source.sessionId);
|
|
2514
2619
|
portable = withFileTracking(portable, event.preparation);
|
|
@@ -2527,7 +2632,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2527
2632
|
return { compaction: result };
|
|
2528
2633
|
};
|
|
2529
2634
|
if (!capture || !protocol || !input) {
|
|
2530
|
-
compactionStatus = `${summaryPath} portable summary prepared`;
|
|
2531
2635
|
return outcome(summaryPath, portable, summaryReason);
|
|
2532
2636
|
}
|
|
2533
2637
|
try {
|
|
@@ -2538,28 +2642,35 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2538
2642
|
fallback("auth changed after portable generation");
|
|
2539
2643
|
return outcome(summaryPath, portable, "native auth changed");
|
|
2540
2644
|
}
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2645
|
+
let native;
|
|
2646
|
+
compactionRequest = true;
|
|
2647
|
+
try {
|
|
2648
|
+
native = await requestProviderCompaction({
|
|
2649
|
+
provider: capture.provider,
|
|
2650
|
+
model: capture.model,
|
|
2651
|
+
context: capture.prepared.context,
|
|
2652
|
+
input,
|
|
2653
|
+
preparedPayload: capture.prepared.payload,
|
|
2654
|
+
protocol,
|
|
2655
|
+
apiKey: capture.options.apiKey,
|
|
2656
|
+
headers: capture.options.headers,
|
|
2657
|
+
env: capture.options.env,
|
|
2658
|
+
signal: event.signal,
|
|
2659
|
+
isCurrent: current,
|
|
2660
|
+
fetch: capture.options.fetch,
|
|
2661
|
+
sessionId: source.sessionId,
|
|
2662
|
+
...capture.options.serviceTier === undefined ? {} : { serviceTier: capture.options.serviceTier }
|
|
2663
|
+
});
|
|
2664
|
+
} finally {
|
|
2665
|
+
compactionRequest = false;
|
|
2666
|
+
}
|
|
2557
2667
|
if (!current())
|
|
2558
2668
|
return cancelled(combineUsage(native.usage, portable.usage));
|
|
2559
2669
|
const finalAuth = await context.modelRegistry.getApiKeyAndHeaders(context.model);
|
|
2560
2670
|
if (!current())
|
|
2561
2671
|
return cancelled(combineUsage(native.usage, portable.usage));
|
|
2562
2672
|
if (!finalAuth.ok || routeDigest(finalAuth) !== capture.authHash) {
|
|
2673
|
+
portable.usage = combineUsage(native.usage, portable.usage);
|
|
2563
2674
|
fallback("auth changed after native generation");
|
|
2564
2675
|
return outcome(summaryPath, portable, "native auth changed");
|
|
2565
2676
|
}
|
|
@@ -2592,19 +2703,12 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2592
2703
|
pi.on("session_compact", (event, context) => {
|
|
2593
2704
|
const recorded = isObject(event.compactionEntry.details) ? event.compactionEntry.details.providerCompaction : undefined;
|
|
2594
2705
|
const owned = isObject(recorded) && equal(recorded, lastOutcome);
|
|
2595
|
-
if (readNativeCompactionDetails(event.compactionEntry.details)?.version
|
|
2596
|
-
compactionStatus = "native checkpoint committed";
|
|
2597
|
-
else if (fallbackReason)
|
|
2598
|
-
compactionStatus = `portable: ${fallbackReason}`;
|
|
2599
|
-
else if (owned)
|
|
2600
|
-
compactionStatus = `${lastOutcome.path} portable summary committed`;
|
|
2601
|
-
else
|
|
2706
|
+
if (!owned && !fallbackReason && readNativeCompactionDetails(event.compactionEntry.details)?.version !== 3)
|
|
2602
2707
|
fallback("another handler supplied the committed result");
|
|
2603
2708
|
if (owned) {
|
|
2604
2709
|
if (configuration().outcomeNotifications && lastOutcome)
|
|
2605
|
-
context.ui.notify(`Compacted via ${lastOutcome.path} in ${(lastOutcome.elapsedMs / 1000).toFixed(1)}s${lastOutcome.usage ? `; cache read ${lastOutcome.usage.cacheRead}, write ${lastOutcome.usage.cacheWrite};
|
|
2606
|
-
}
|
|
2607
|
-
lastOutcome = { path: "Pi/other handler", elapsedMs: 0, usage: event.compactionEntry.usage, reason: compactionStatus };
|
|
2710
|
+
context.ui.notify(`Compacted via ${lastOutcome.path} in ${(lastOutcome.elapsedMs / 1000).toFixed(1)}s${lastOutcome.usage ? `; cache read ${lastOutcome.usage.cacheRead}, write ${lastOutcome.usage.cacheWrite}; estimated cost $${lastOutcome.usage.cost.total.toFixed(6)}` : ""}`, "info");
|
|
2711
|
+
}
|
|
2608
2712
|
if (fallbackReason)
|
|
2609
2713
|
pi.appendEntry("pi-provider-compaction:diagnostic", { status: `portable: ${fallbackReason}` });
|
|
2610
2714
|
active = undefined;
|
|
@@ -2633,7 +2737,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2633
2737
|
pending = undefined;
|
|
2634
2738
|
snapshot = undefined;
|
|
2635
2739
|
cache = undefined;
|
|
2636
|
-
compactionStatus = "compaction failed or cancelled";
|
|
2637
2740
|
});
|
|
2638
2741
|
}
|
|
2639
2742
|
export {
|