@chroxify/eunoe 1.3.0 → 1.3.1
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 +19 -12
- package/dist/core/index.js +285 -166
- package/dist/types/context/fold/strategies.d.ts +1 -1
- package/dist/types/context/fold/turns.d.ts +2 -0
- package/dist/types/context/recall/index.d.ts +3 -0
- package/dist/types/core/config.d.ts +1 -0
- package/dist/types/core/recall.d.ts +1 -0
- package/dist/types/core/rewrite.d.ts +3 -3
- package/dist/types/core/state.d.ts +3 -0
- package/dist/types/core/usage.d.ts +2 -1
- package/dist/types/core/window.d.ts +5 -2
- package/package.json +1 -1
- package/src/context/fold/strategies.ts +23 -15
- package/src/context/fold/turns.ts +12 -0
- package/src/context/recall/index.ts +6 -1
- package/src/core/compaction.ts +55 -30
- package/src/core/config.ts +1 -1
- package/src/core/fold.ts +19 -17
- package/src/core/forward.ts +4 -2
- package/src/core/recall.ts +27 -3
- package/src/core/rewrite.ts +19 -7
- package/src/core/state.ts +10 -0
- package/src/core/usage.ts +17 -8
- package/src/core/window.ts +40 -10
package/dist/core/index.js
CHANGED
|
@@ -545,6 +545,16 @@ function splitTurns(messages) {
|
|
|
545
545
|
function turnFingerprint(turn) {
|
|
546
546
|
return shortHash(JSON.stringify(blocks(turn[0]).map(withoutCacheControl)), 24);
|
|
547
547
|
}
|
|
548
|
+
function turnKeys(turns) {
|
|
549
|
+
const copies = new Map;
|
|
550
|
+
return turns.map((turn) => {
|
|
551
|
+
const fingerprint = turnFingerprint(turn);
|
|
552
|
+
const earlier = copies.get(fingerprint) ?? 0;
|
|
553
|
+
copies.set(fingerprint, earlier + 1);
|
|
554
|
+
return earlier === 0 ? fingerprint : `${fingerprint}~${earlier}`;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
var fingerprintOfKey = (key) => key.split("~")[0];
|
|
548
558
|
function isHarness(message) {
|
|
549
559
|
return message.role === "system";
|
|
550
560
|
}
|
|
@@ -593,8 +603,19 @@ function reduceTurn(turn, options = {}) {
|
|
|
593
603
|
}
|
|
594
604
|
|
|
595
605
|
// src/context/fold/strategies.ts
|
|
596
|
-
|
|
597
|
-
|
|
606
|
+
function pinnedAt(turns, { pinned }) {
|
|
607
|
+
if (pinned === undefined || pinned.size === 0)
|
|
608
|
+
return () => false;
|
|
609
|
+
const keys = turnKeys(turns);
|
|
610
|
+
return (index) => pinned.has(keys[index]);
|
|
611
|
+
}
|
|
612
|
+
function pinnedPrompt({ pinned }) {
|
|
613
|
+
if (pinned === undefined || pinned.size === 0)
|
|
614
|
+
return () => false;
|
|
615
|
+
const fingerprints = new Set([...pinned].map(fingerprintOfKey));
|
|
616
|
+
return (turn) => fingerprints.has(turnFingerprint(turn));
|
|
617
|
+
}
|
|
618
|
+
var countPinned = (keys, pinned) => pinned.size > 0 ? keys.filter((key) => pinned.has(key)).length : 0;
|
|
598
619
|
function trimMessages(messages, options = {}) {
|
|
599
620
|
const { preamble, turns } = splitTurns(messages);
|
|
600
621
|
if (turns.length < 2)
|
|
@@ -624,18 +645,19 @@ function compactMessages(messages, cutIndex, options) {
|
|
|
624
645
|
if (cutIndex <= 0 || cutIndex >= turns.length)
|
|
625
646
|
return messages;
|
|
626
647
|
const reduce = options.reduce ?? reduceTurn;
|
|
627
|
-
const isPinned =
|
|
648
|
+
const isPinned = pinnedAt(turns, options);
|
|
649
|
+
const indexed = turns.map((turn, index) => ({ turn, pinned: isPinned(index) }));
|
|
628
650
|
const keptFrom = Math.max(1, cutIndex - options.keepTurns);
|
|
629
651
|
const [first, ...rest] = turns[0];
|
|
630
|
-
const openingPinned =
|
|
652
|
+
const openingPinned = indexed[0].pinned;
|
|
631
653
|
const openingFolded = !openingPinned && foldsOpening(turns[0], keptFrom);
|
|
632
|
-
const dropped =
|
|
633
|
-
const whole = dropped.filter(
|
|
634
|
-
const pinned = whole.length +
|
|
654
|
+
const dropped = indexed.slice(1, keptFrom);
|
|
655
|
+
const whole = dropped.filter((t) => t.pinned).map((t) => t.turn);
|
|
656
|
+
const pinned = whole.length + indexed.slice(keptFrom, cutIndex).filter((t) => t.pinned).length + (openingPinned ? 1 : 0);
|
|
635
657
|
const kept = cutIndex - keptFrom + (openingFolded ? 1 : 0);
|
|
636
658
|
const notice = compactionNotice({ omitted: dropped.length - whole.length + (options.omittedBefore ?? 0), kept, pinned, guide: options.guide });
|
|
637
|
-
const removed = [...openingPinned || openingFolded ? [] : rest, ...dropped.filter((
|
|
638
|
-
const shown = (
|
|
659
|
+
const removed = [...openingPinned || openingFolded ? [] : rest, ...dropped.filter((t) => !t.pinned).flatMap((t) => t.turn)];
|
|
660
|
+
const shown = (t) => t.pinned ? t.turn.map(clean) : reduce(t.turn, options);
|
|
639
661
|
return [
|
|
640
662
|
...preamble,
|
|
641
663
|
first,
|
|
@@ -643,7 +665,7 @@ function compactMessages(messages, cutIndex, options) {
|
|
|
643
665
|
{ role: "user", content: [{ type: "text", text: notice }, ...carriedHarness(removed)] },
|
|
644
666
|
...openingFolded ? reduce(turns[0], options).slice(1) : [],
|
|
645
667
|
...whole.flatMap((turn) => turn.map(clean)),
|
|
646
|
-
...
|
|
668
|
+
...indexed.slice(keptFrom, cutIndex).flatMap(shown),
|
|
647
669
|
...turns.slice(cutIndex).flat()
|
|
648
670
|
];
|
|
649
671
|
}
|
|
@@ -655,7 +677,7 @@ function capTurns(messages, options) {
|
|
|
655
677
|
if (turns.length < 3)
|
|
656
678
|
return messages;
|
|
657
679
|
const live = turns.length - 1;
|
|
658
|
-
const isPinned =
|
|
680
|
+
const isPinned = pinnedPrompt(options);
|
|
659
681
|
const over = (turn, i) => i > 0 && i < live && !isPinned(turn) && JSON.stringify(turn).length > capChars;
|
|
660
682
|
if (!turns.some(over))
|
|
661
683
|
return messages;
|
|
@@ -716,6 +738,7 @@ function appendSystem(system, text) {
|
|
|
716
738
|
}
|
|
717
739
|
|
|
718
740
|
// src/context/recall/index.ts
|
|
741
|
+
var RECALL_CHARS_TOTAL = 30000;
|
|
719
742
|
var SHOWN_LINES = 8;
|
|
720
743
|
var BM25_K1 = 1.2;
|
|
721
744
|
var BM25_B = 0.75;
|
|
@@ -1037,7 +1060,8 @@ async function pickRecallWith(prompt, chunks, rerank, options = {}) {
|
|
|
1037
1060
|
function renderRecall(prompt, chunks, picks, options = {}) {
|
|
1038
1061
|
if (picks.length === 0)
|
|
1039
1062
|
return null;
|
|
1040
|
-
const
|
|
1063
|
+
const own = queriesOf(prompt).length > 1 ? options.maxCharsMulti ?? 30000 : options.maxChars ?? 18000;
|
|
1064
|
+
const budget = Math.min(own, (options.maxCharsTotal ?? RECALL_CHARS_TOTAL) - (options.charsInUse ?? 0));
|
|
1041
1065
|
const entries = [];
|
|
1042
1066
|
let used = 0;
|
|
1043
1067
|
for (const { index, query } of [...picks].sort((a, b) => chunks[a.index].turn - chunks[b.index].turn || a.index - b.index)) {
|
|
@@ -1109,6 +1133,13 @@ function targetSize(size, window) {
|
|
|
1109
1133
|
return size.fixed + 0.3 * Math.max(0, window - size.fixed);
|
|
1110
1134
|
}
|
|
1111
1135
|
|
|
1136
|
+
// src/core/fingerprint.ts
|
|
1137
|
+
var isInjected = (text) => /^<(?:recalled-context|handoff)[\s>]/.test(String(text ?? ""));
|
|
1138
|
+
function messageFingerprint(message) {
|
|
1139
|
+
const own = blocks(message).filter((b) => !(b.type === "text" && isInjected(b.text))).map(withoutCacheControl);
|
|
1140
|
+
return shortHash(JSON.stringify(own), 24);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1112
1143
|
// src/core/state.ts
|
|
1113
1144
|
function memoryStore(initial = {}) {
|
|
1114
1145
|
const threads = new Map(Object.entries(initial).map(([key, state]) => [key, structuredClone(state)]));
|
|
@@ -1119,6 +1150,13 @@ function memoryStore(initial = {}) {
|
|
|
1119
1150
|
}
|
|
1120
1151
|
};
|
|
1121
1152
|
}
|
|
1153
|
+
function retireRecalls(state, folded) {
|
|
1154
|
+
if (!state.recalls)
|
|
1155
|
+
return;
|
|
1156
|
+
for (const message of folded.flat())
|
|
1157
|
+
if (isPrompt(message))
|
|
1158
|
+
delete state.recalls[messageFingerprint(message)];
|
|
1159
|
+
}
|
|
1122
1160
|
function resetCut(state) {
|
|
1123
1161
|
delete state.cut;
|
|
1124
1162
|
delete state.keep;
|
|
@@ -1145,15 +1183,14 @@ function reduceRemembered(turn, options) {
|
|
|
1145
1183
|
return reduced;
|
|
1146
1184
|
}
|
|
1147
1185
|
var PROTECTED_OUTPUTS = 5;
|
|
1148
|
-
var STUCK_STEP = 0.1;
|
|
1149
1186
|
function settledCut(turns, index) {
|
|
1150
1187
|
let cut = index;
|
|
1151
1188
|
while (cut > 1 && JSON.stringify(turns[cut][0].content).includes("[Request interrupted"))
|
|
1152
1189
|
cut -= 1;
|
|
1153
1190
|
return cut;
|
|
1154
1191
|
}
|
|
1155
|
-
function resolveCut(turns, state, config, rollingKeep) {
|
|
1156
|
-
let cutIndex = state.cut ?
|
|
1192
|
+
function resolveCut(turns, keys, state, config, rollingKeep) {
|
|
1193
|
+
let cutIndex = state.cut ? keys.indexOf(state.cut) : -1;
|
|
1157
1194
|
let stateChanged = false;
|
|
1158
1195
|
if (state.cut && cutIndex === -1) {
|
|
1159
1196
|
resetCut(state);
|
|
@@ -1161,7 +1198,8 @@ function resolveCut(turns, state, config, rollingKeep) {
|
|
|
1161
1198
|
}
|
|
1162
1199
|
if (cutIndex <= 0 && config.eval?.forceCut && turns.length >= 3) {
|
|
1163
1200
|
cutIndex = settledCut(turns, Math.max(1, turns.length - 1 - (rollingKeep ?? 1)));
|
|
1164
|
-
state.cut =
|
|
1201
|
+
state.cut = keys[cutIndex];
|
|
1202
|
+
retireRecalls(state, turns.slice(0, cutIndex));
|
|
1165
1203
|
stateChanged = true;
|
|
1166
1204
|
}
|
|
1167
1205
|
return { cutIndex, stateChanged };
|
|
@@ -1188,29 +1226,55 @@ function shrinkToTarget(over, step, actions, rebuild) {
|
|
|
1188
1226
|
const action = step();
|
|
1189
1227
|
actions.push(action);
|
|
1190
1228
|
if (action === "stuck")
|
|
1191
|
-
return
|
|
1229
|
+
return;
|
|
1192
1230
|
rebuild();
|
|
1193
1231
|
}
|
|
1194
|
-
return false;
|
|
1195
1232
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1233
|
+
function shrinkOrHold(state, actions, shrink) {
|
|
1234
|
+
const overWindow = (tokens) => shrink.window !== null && tokens > shrink.window;
|
|
1235
|
+
const held = state.stuck === undefined ? null : { keep: state.keep, cleared: state.cleared, actions: actions.length, overWindow: overWindow(shrink.estimate()) };
|
|
1236
|
+
shrinkToTarget(() => shrink.estimate() > shrink.target, shrink.step, actions, shrink.rebuild);
|
|
1237
|
+
const helped = (estimate) => estimate <= shrink.limit || held?.overWindow === true && !overWindow(estimate);
|
|
1238
|
+
if (held && !helped(shrink.estimate())) {
|
|
1239
|
+
restoreShrink(state, held);
|
|
1240
|
+
actions.length = held.actions;
|
|
1241
|
+
shrink.rebuild();
|
|
1242
|
+
}
|
|
1243
|
+
if (shrink.estimate() > shrink.limit)
|
|
1244
|
+
state.stuck = shrink.estimate();
|
|
1245
|
+
else {
|
|
1246
|
+
delete state.stuck;
|
|
1247
|
+
if (actions.at(-1) === "stuck")
|
|
1248
|
+
actions.pop();
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
function restoreShrink(state, held) {
|
|
1252
|
+
if (held.keep === undefined)
|
|
1253
|
+
delete state.keep;
|
|
1254
|
+
else
|
|
1255
|
+
state.keep = held.keep;
|
|
1256
|
+
if (held.cleared === undefined)
|
|
1257
|
+
delete state.cleared;
|
|
1258
|
+
else
|
|
1259
|
+
state.cleared = held.cleared;
|
|
1260
|
+
}
|
|
1261
|
+
var headLength = (preamble, first, firstKey, state) => preamble.length + 1 + (countPinned([firstKey], new Set(state.pins)) > 0 ? first.length : 1);
|
|
1198
1262
|
function stuckSizes(messages, headEnd, liveFrom, size) {
|
|
1199
1263
|
const of = (list) => size.of(list) - size.fixed;
|
|
1200
1264
|
return { fixed: size.fixed, head: of(messages.slice(0, headEnd)), folded: of(messages.slice(headEnd, liveFrom)), live: of(messages.slice(liveFrom)) };
|
|
1201
1265
|
}
|
|
1202
|
-
function cutNote(state, turns, cutIndex, configuredKeep, actions) {
|
|
1266
|
+
function cutNote(state, turns, keys, cutIndex, configuredKeep, actions) {
|
|
1203
1267
|
const pins = new Set(state.pins);
|
|
1204
1268
|
const keptFrom = cutIndex > 0 ? Math.max(1, cutIndex - (state.keep ?? configuredKeep)) : 0;
|
|
1205
|
-
const omitted = cutIndex > 0 ? keptFrom - 1 - countPinned(
|
|
1269
|
+
const omitted = cutIndex > 0 ? keptFrom - 1 - countPinned(keys.slice(1, keptFrom), pins) : 0;
|
|
1206
1270
|
return {
|
|
1207
1271
|
action: cutIndex > 0 ? "compact" : "full",
|
|
1208
1272
|
compacted: actions.includes("cut") || actions.includes("roll"),
|
|
1209
1273
|
actions: actions.length > 0 ? actions : undefined,
|
|
1210
1274
|
cutTurn: cutIndex > 0 ? cutIndex : null,
|
|
1211
1275
|
omitted,
|
|
1212
|
-
folded: cutIndex > 0 ? cutIndex - keptFrom - countPinned(
|
|
1213
|
-
pinned: cutIndex > 0 ? countPinned(
|
|
1276
|
+
folded: cutIndex > 0 ? cutIndex - keptFrom - countPinned(keys.slice(keptFrom, cutIndex), pins) + (foldsOpening(turns[0], keptFrom) && countPinned([keys[0]], pins) === 0 ? 1 : 0) : 0,
|
|
1277
|
+
pinned: cutIndex > 0 ? countPinned(keys.slice(0, cutIndex), pins) : 0,
|
|
1214
1278
|
host: state.host,
|
|
1215
1279
|
keep: state.keep,
|
|
1216
1280
|
cleared: state.cleared
|
|
@@ -1219,7 +1283,8 @@ function cutNote(state, turns, cutIndex, configuredKeep, actions) {
|
|
|
1219
1283
|
function compact({ body, config, state, window, guide, transcriptDir, mayCompact, omittedBefore = 0, decorate = (m) => m, reduce }) {
|
|
1220
1284
|
const { preamble, turns } = splitTurns(body.messages);
|
|
1221
1285
|
const rolling = config.eval?.rolling ?? (config.mode === "default" ? { keep: config.keep, budget: config.budget } : undefined);
|
|
1222
|
-
const
|
|
1286
|
+
const keys = turnKeys(turns);
|
|
1287
|
+
const resolved = resolveCut(turns, keys, state, config, rolling?.keep);
|
|
1223
1288
|
let cutIndex = resolved.cutIndex;
|
|
1224
1289
|
let stateChanged = resolved.stateChanged;
|
|
1225
1290
|
const configuredKeep = config.keepTurns === "all" ? Number.MAX_SAFE_INTEGER : config.keepTurns;
|
|
@@ -1248,7 +1313,8 @@ function compact({ body, config, state, window, guide, transcriptDir, mayCompact
|
|
|
1248
1313
|
const moveCut = (index, action) => {
|
|
1249
1314
|
cutIndex = index;
|
|
1250
1315
|
resetCut(state);
|
|
1251
|
-
state.cut =
|
|
1316
|
+
state.cut = keys[index];
|
|
1317
|
+
retireRecalls(state, turns.slice(0, index));
|
|
1252
1318
|
actions.push(action);
|
|
1253
1319
|
rebuild();
|
|
1254
1320
|
};
|
|
@@ -1261,21 +1327,18 @@ function compact({ body, config, state, window, guide, transcriptDir, mayCompact
|
|
|
1261
1327
|
const current = turns.length - 1;
|
|
1262
1328
|
if (current > 0 && current > cutIndex)
|
|
1263
1329
|
moveCut(current, "cut");
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
else
|
|
1269
|
-
delete state.stuck;
|
|
1270
|
-
}
|
|
1330
|
+
shrinkOrHold(state, actions, { estimate: () => estimate, step: () => shrinkOnce(state, cutIndex, configuredKeep, liveResults), rebuild, target, limit, window });
|
|
1331
|
+
stateChanged = true;
|
|
1332
|
+
} else if (state.stuck !== undefined) {
|
|
1333
|
+
delete state.stuck;
|
|
1271
1334
|
stateChanged = true;
|
|
1272
1335
|
}
|
|
1273
1336
|
return {
|
|
1274
1337
|
messages,
|
|
1275
1338
|
stateChanged,
|
|
1276
1339
|
note: {
|
|
1277
|
-
...cutNote(state, turns, cutIndex, configuredKeep, actions),
|
|
1278
|
-
stuck: state.stuck === undefined ? undefined : stuckSizes(messages, headLength(preamble, turns[0], state), liveStart(messages), size),
|
|
1340
|
+
...cutNote(state, turns, keys, cutIndex, configuredKeep, actions),
|
|
1341
|
+
stuck: state.stuck === undefined ? undefined : stuckSizes(messages, headLength(preamble, turns[0], keys[0], state), liveStart(messages), size),
|
|
1279
1342
|
turns: turns.length,
|
|
1280
1343
|
messagesIn: body.messages.length,
|
|
1281
1344
|
messagesOut: messages.length,
|
|
@@ -1285,13 +1348,6 @@ function compact({ body, config, state, window, guide, transcriptDir, mayCompact
|
|
|
1285
1348
|
};
|
|
1286
1349
|
}
|
|
1287
1350
|
|
|
1288
|
-
// src/core/fingerprint.ts
|
|
1289
|
-
var isInjected = (text) => /^<(?:recalled-context|handoff)[\s>]/.test(String(text ?? ""));
|
|
1290
|
-
function messageFingerprint(message) {
|
|
1291
|
-
const own = blocks(message).filter((b) => !(b.type === "text" && isInjected(b.text))).map(withoutCacheControl);
|
|
1292
|
-
return shortHash(JSON.stringify(own), 24);
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
1351
|
// src/context/fold/index.ts
|
|
1296
1352
|
var FOLD_CAP_CHARS = 400000;
|
|
1297
1353
|
var spoken = (message) => textOf(message).replace(/<(system-reminder|system-message|recalled-context|handoff)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, "").trim();
|
|
@@ -1431,6 +1487,26 @@ function recallTarget(original) {
|
|
|
1431
1487
|
const live = livePrompt(original.messages);
|
|
1432
1488
|
return live ? { key: messageFingerprint(original.messages[live.index]), text: live.text } : null;
|
|
1433
1489
|
}
|
|
1490
|
+
function charsInUse(messages, recalls) {
|
|
1491
|
+
return messages.reduce((sum, message) => sum + (isPrompt(message) ? recalls[messageFingerprint(message)]?.length ?? 0 : 0), 0);
|
|
1492
|
+
}
|
|
1493
|
+
var RECALL_SHARE = 0.25;
|
|
1494
|
+
var RECALL_FLOOR = 6000;
|
|
1495
|
+
function withinTotal(input, result, recalls) {
|
|
1496
|
+
const options = input.config.eval?.recallOptions;
|
|
1497
|
+
const share = Math.max(RECALL_FLOOR, RECALL_SHARE * JSON.stringify(result.body.messages).length);
|
|
1498
|
+
return { ...options, maxCharsTotal: Math.min(options?.maxCharsTotal ?? RECALL_CHARS_TOTAL, share), charsInUse: charsInUse(result.body.messages, recalls) };
|
|
1499
|
+
}
|
|
1500
|
+
function dropLiveRecall(input, threadKey) {
|
|
1501
|
+
const target = recallTarget(input.body);
|
|
1502
|
+
if (!target)
|
|
1503
|
+
return;
|
|
1504
|
+
const state = input.store.read(threadKey);
|
|
1505
|
+
if (state.recalls?.[target.key] === "")
|
|
1506
|
+
return;
|
|
1507
|
+
state.recalls = { ...state.recalls, [target.key]: "" };
|
|
1508
|
+
input.store.save(threadKey, state);
|
|
1509
|
+
}
|
|
1434
1510
|
function applyRecall(input, result) {
|
|
1435
1511
|
if (!touched(input, result) || !result.threadKey)
|
|
1436
1512
|
return result;
|
|
@@ -1439,7 +1515,7 @@ function applyRecall(input, result) {
|
|
|
1439
1515
|
const target = recallTarget(input.body);
|
|
1440
1516
|
let note = result.note;
|
|
1441
1517
|
if (target && !(target.key in state.recalls)) {
|
|
1442
|
-
state.recalls[target.key] = recallFor(target.text, droppedChunks(input, result, result.threadKey), input.
|
|
1518
|
+
state.recalls[target.key] = recallFor(target.text, droppedChunks(input, result, result.threadKey), withinTotal(input, result, state.recalls)) ?? "";
|
|
1443
1519
|
input.store.save(result.threadKey, state);
|
|
1444
1520
|
note = { ...note, recalled: state.recalls[target.key].length };
|
|
1445
1521
|
}
|
|
@@ -1456,12 +1532,112 @@ async function prefetchFor(input, result, rerank) {
|
|
|
1456
1532
|
state.recalls ??= {};
|
|
1457
1533
|
if (target.key in state.recalls)
|
|
1458
1534
|
return null;
|
|
1459
|
-
const options = { candidates: input.config.eval?.rerank?.candidates, ...input.
|
|
1535
|
+
const options = { candidates: input.config.eval?.rerank?.candidates, ...withinTotal(input, result, state.recalls) };
|
|
1460
1536
|
state.recalls[target.key] = await recallForWith(target.text, droppedChunks(input, result, result.threadKey), rerank, options) ?? "";
|
|
1461
1537
|
input.store.save(result.threadKey, state);
|
|
1462
1538
|
return state.recalls[target.key].length;
|
|
1463
1539
|
}
|
|
1464
1540
|
|
|
1541
|
+
// src/core/usage.ts
|
|
1542
|
+
function usageIn(line) {
|
|
1543
|
+
if (!line.startsWith("data:") || !line.includes("message_start"))
|
|
1544
|
+
return null;
|
|
1545
|
+
try {
|
|
1546
|
+
return JSON.parse(line.slice(5)).message?.usage ?? null;
|
|
1547
|
+
} catch {
|
|
1548
|
+
return null;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function trailingUsage(buffer) {
|
|
1552
|
+
if (!buffer.trim().startsWith("{"))
|
|
1553
|
+
return null;
|
|
1554
|
+
try {
|
|
1555
|
+
const json = JSON.parse(buffer);
|
|
1556
|
+
if (json.usage)
|
|
1557
|
+
return json.usage;
|
|
1558
|
+
return typeof json.input_tokens === "number" ? { input_tokens: json.input_tokens } : null;
|
|
1559
|
+
} catch {
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
function scanEvents(buffer) {
|
|
1564
|
+
let rest = buffer;
|
|
1565
|
+
for (let newline = rest.indexOf(`
|
|
1566
|
+
`);newline !== -1; newline = rest.indexOf(`
|
|
1567
|
+
`)) {
|
|
1568
|
+
const usage = usageIn(rest.slice(0, newline).trim());
|
|
1569
|
+
rest = rest.slice(newline + 1);
|
|
1570
|
+
if (usage)
|
|
1571
|
+
return { usage, rest };
|
|
1572
|
+
}
|
|
1573
|
+
return { usage: null, rest };
|
|
1574
|
+
}
|
|
1575
|
+
async function readUsage(stream) {
|
|
1576
|
+
const reader = stream.pipeThrough(new TextDecoderStream).getReader();
|
|
1577
|
+
let buffer = "";
|
|
1578
|
+
let usage = null;
|
|
1579
|
+
while (true) {
|
|
1580
|
+
const { value, done } = await reader.read();
|
|
1581
|
+
if (done)
|
|
1582
|
+
return usage ?? trailingUsage(buffer);
|
|
1583
|
+
if (usage)
|
|
1584
|
+
continue;
|
|
1585
|
+
const scanned = scanEvents(buffer + value);
|
|
1586
|
+
usage = scanned.usage;
|
|
1587
|
+
buffer = scanned.rest.length > 4000000 ? "" : scanned.rest;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
function watchUsage(response, onUsage) {
|
|
1591
|
+
if (!response.body)
|
|
1592
|
+
return response;
|
|
1593
|
+
const [toClient, toUs] = response.body.tee();
|
|
1594
|
+
readUsage(toUs).then((usage) => {
|
|
1595
|
+
if (usage)
|
|
1596
|
+
onUsage(usage);
|
|
1597
|
+
}).catch(() => {
|
|
1598
|
+
return;
|
|
1599
|
+
});
|
|
1600
|
+
return new Response(toClient, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
1601
|
+
}
|
|
1602
|
+
function inputTokens(usage) {
|
|
1603
|
+
return (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
|
|
1604
|
+
}
|
|
1605
|
+
var OVERFLOW_MARGIN = 1.1;
|
|
1606
|
+
var charsOf = (sent) => typeof sent === "number" ? sent : sizeOf(sent);
|
|
1607
|
+
function overflowOf(errorText) {
|
|
1608
|
+
const match = errorText.match(/prompt is too long[^0-9]*(\d+)\s*tokens?\s*>\s*(\d+)/i);
|
|
1609
|
+
const tokens = Number(match?.[1]);
|
|
1610
|
+
const maximum = Number(match?.[2]);
|
|
1611
|
+
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
1612
|
+
return null;
|
|
1613
|
+
return { tokens, maximum: Number.isFinite(maximum) && maximum > 0 ? maximum : null };
|
|
1614
|
+
}
|
|
1615
|
+
function recordUsage(store, threadKey, sent, usage) {
|
|
1616
|
+
const input = inputTokens(usage);
|
|
1617
|
+
const chars = charsOf(sent);
|
|
1618
|
+
if (input <= 0 || chars <= 0)
|
|
1619
|
+
return false;
|
|
1620
|
+
const state = store.read(threadKey);
|
|
1621
|
+
state.ratio = input / chars;
|
|
1622
|
+
store.save(threadKey, state);
|
|
1623
|
+
return true;
|
|
1624
|
+
}
|
|
1625
|
+
var limitKey = (model) => String(model ?? "");
|
|
1626
|
+
function recordOverflow(store, threadKey, sent, errorText, model = typeof sent === "number" ? undefined : sent.model) {
|
|
1627
|
+
const overflow = overflowOf(errorText);
|
|
1628
|
+
const chars = charsOf(sent);
|
|
1629
|
+
if (!overflow || chars <= 0)
|
|
1630
|
+
return null;
|
|
1631
|
+
const state = store.read(threadKey);
|
|
1632
|
+
state.ratio = Math.max(state.ratio ?? 0, overflow.tokens / chars) * OVERFLOW_MARGIN;
|
|
1633
|
+
if (overflow.maximum) {
|
|
1634
|
+
const key = limitKey(model);
|
|
1635
|
+
state.limits = { ...state.limits, [key]: Math.min(state.limits?.[key] ?? Number.POSITIVE_INFINITY, overflow.maximum) };
|
|
1636
|
+
}
|
|
1637
|
+
store.save(threadKey, state);
|
|
1638
|
+
return overflow.tokens;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1465
1641
|
// src/core/rewrite.ts
|
|
1466
1642
|
var pass = (body) => ({ body, threadKey: null, note: { action: "pass" } });
|
|
1467
1643
|
var isAgentThread = (body) => Array.isArray(body.tools) && body.tools.length > 0;
|
|
@@ -1521,7 +1697,7 @@ function strategize(input) {
|
|
|
1521
1697
|
if (body.messages.length < 2)
|
|
1522
1698
|
return { body: based, threadKey, note: { action: "full", turns: turns.length } };
|
|
1523
1699
|
const handoffs = activeHandoffs(state.handoffs);
|
|
1524
|
-
const
|
|
1700
|
+
const recalling = wantsRecall(input);
|
|
1525
1701
|
const result = compact({
|
|
1526
1702
|
body: based,
|
|
1527
1703
|
config,
|
|
@@ -1531,7 +1707,7 @@ function strategize(input) {
|
|
|
1531
1707
|
transcriptDir: sources.transcriptDir ?? null,
|
|
1532
1708
|
mayCompact: input.mayCompact && !off,
|
|
1533
1709
|
omittedBefore: input.omittedBefore,
|
|
1534
|
-
decorate: (messages) => withRecall(withHandoffs(messages, handoffs), messageFingerprint, recalls)
|
|
1710
|
+
decorate: (messages) => withRecall(withHandoffs(messages, handoffs), messageFingerprint, recalling ? activeRecalls(state.recalls) : {})
|
|
1535
1711
|
});
|
|
1536
1712
|
if (result.stateChanged)
|
|
1537
1713
|
store.save(threadKey, state);
|
|
@@ -1566,17 +1742,28 @@ function carried(from, to) {
|
|
|
1566
1742
|
return to;
|
|
1567
1743
|
return { ...to, note: { ...to.note, compacted: Boolean(to.note.compacted || from.note.compacted), actions } };
|
|
1568
1744
|
}
|
|
1569
|
-
function
|
|
1745
|
+
function withLearnedLimit(input) {
|
|
1746
|
+
const threadKey = input.threadId && Array.isArray(input.body.messages) ? threadKeyOf(input.threadId, input.body.messages) : null;
|
|
1747
|
+
const limit = threadKey ? input.store.read(threadKey).limits?.[limitKey(input.body.model)] : undefined;
|
|
1748
|
+
if (!limit || input.window !== null && input.window <= limit)
|
|
1749
|
+
return input;
|
|
1750
|
+
return { ...input, window: limit };
|
|
1751
|
+
}
|
|
1752
|
+
function rewrite(given, before) {
|
|
1753
|
+
const input = withLearnedLimit(given);
|
|
1570
1754
|
const result = before ? carried(before, once(input)) : once(input);
|
|
1571
1755
|
if (!overLimit(input, result))
|
|
1572
1756
|
return result;
|
|
1573
1757
|
const recut = carried(result, once(input));
|
|
1574
1758
|
if (!overLimit(input, recut))
|
|
1575
1759
|
return { ...recut, note: { ...recut.note, fit: "recut" } };
|
|
1576
|
-
|
|
1760
|
+
if (recut.threadKey && wantsRecall(input))
|
|
1761
|
+
dropLiveRecall(input, recut.threadKey);
|
|
1762
|
+
const bare = carried(recut, once(input));
|
|
1577
1763
|
return { ...bare, note: { ...bare.note, fit: overLimit(input, bare) ? "over" : "bare", fitEstimate: sentEstimate(input, bare) } };
|
|
1578
1764
|
}
|
|
1579
|
-
async function rewriteAsync(
|
|
1765
|
+
async function rewriteAsync(given, rerank = identityReranker) {
|
|
1766
|
+
const input = withLearnedLimit(given);
|
|
1580
1767
|
if (!wantsRecall(input) || rerank === identityReranker)
|
|
1581
1768
|
return rewrite(input);
|
|
1582
1769
|
const first = strategize(input);
|
|
@@ -1590,7 +1777,8 @@ function located({ store, threadId, messages }) {
|
|
|
1590
1777
|
const threadKey = threadKeyOf(threadId, messages);
|
|
1591
1778
|
if (!threadKey)
|
|
1592
1779
|
throw new RangeError("eunoe can only fold a thread that has at least one user turn");
|
|
1593
|
-
|
|
1780
|
+
const turns = splitTurns(messages).turns;
|
|
1781
|
+
return { threadKey, turns, keys: turnKeys(turns), state: store.read(threadKey) };
|
|
1594
1782
|
}
|
|
1595
1783
|
function checkedTurn(turn, turns, from) {
|
|
1596
1784
|
if (turns.length <= from)
|
|
@@ -1599,19 +1787,19 @@ function checkedTurn(turn, turns, from) {
|
|
|
1599
1787
|
throw new RangeError(`turn ${turn} is outside turns ${from}..${turns.length - 1}`);
|
|
1600
1788
|
return turn;
|
|
1601
1789
|
}
|
|
1602
|
-
function folded2(threadKey,
|
|
1603
|
-
const cutTurn = state.cut ?
|
|
1790
|
+
function folded2(threadKey, keys, state) {
|
|
1791
|
+
const cutTurn = state.cut ? keys.indexOf(state.cut) : -1;
|
|
1604
1792
|
const pins = new Set(state.pins);
|
|
1605
|
-
return { threadKey, cutTurn: cutTurn > 0 ? cutTurn : null, pinned:
|
|
1793
|
+
return { threadKey, cutTurn: cutTurn > 0 ? cutTurn : null, pinned: keys.flatMap((key, i) => pins.has(key) ? [i] : []) };
|
|
1606
1794
|
}
|
|
1607
|
-
function withPins(state, turns, indices, pinned) {
|
|
1795
|
+
function withPins(state, turns, keys, indices, pinned) {
|
|
1608
1796
|
const pins = new Set(state.pins);
|
|
1609
1797
|
for (const index of indices) {
|
|
1610
|
-
const
|
|
1798
|
+
const key = keys[checkedTurn(index, turns, 0)];
|
|
1611
1799
|
if (pinned)
|
|
1612
|
-
pins.add(
|
|
1800
|
+
pins.add(key);
|
|
1613
1801
|
else
|
|
1614
|
-
pins.delete(
|
|
1802
|
+
pins.delete(key);
|
|
1615
1803
|
}
|
|
1616
1804
|
if (pins.size > 0)
|
|
1617
1805
|
state.pins = [...pins].sort();
|
|
@@ -1619,115 +1807,28 @@ function withPins(state, turns, indices, pinned) {
|
|
|
1619
1807
|
delete state.pins;
|
|
1620
1808
|
}
|
|
1621
1809
|
function foldThread(target, { at = "latest", pin = [] } = {}) {
|
|
1622
|
-
const { threadKey, turns, state } = located(target);
|
|
1810
|
+
const { threadKey, turns, keys, state } = located(target);
|
|
1623
1811
|
const cut = checkedTurn(at === "latest" ? turns.length - 1 : at, turns, 1);
|
|
1624
|
-
withPins(state, turns, pin, true);
|
|
1812
|
+
withPins(state, turns, keys, pin, true);
|
|
1625
1813
|
resetCut(state);
|
|
1626
|
-
state.cut =
|
|
1814
|
+
state.cut = keys[cut];
|
|
1815
|
+
retireRecalls(state, turns.slice(0, cut));
|
|
1627
1816
|
state.keep = cut - 1;
|
|
1628
1817
|
state.host = true;
|
|
1629
1818
|
target.store.save(threadKey, state);
|
|
1630
|
-
return folded2(threadKey,
|
|
1819
|
+
return folded2(threadKey, keys, state);
|
|
1631
1820
|
}
|
|
1632
1821
|
function pinTurns(target, indices, pinned = true) {
|
|
1633
|
-
const { threadKey, turns, state } = located(target);
|
|
1634
|
-
withPins(state, turns, indices, pinned);
|
|
1822
|
+
const { threadKey, turns, keys, state } = located(target);
|
|
1823
|
+
withPins(state, turns, keys, indices, pinned);
|
|
1635
1824
|
target.store.save(threadKey, state);
|
|
1636
|
-
return folded2(threadKey,
|
|
1637
|
-
}
|
|
1638
|
-
// src/core/usage.ts
|
|
1639
|
-
function usageIn(line) {
|
|
1640
|
-
if (!line.startsWith("data:") || !line.includes("message_start"))
|
|
1641
|
-
return null;
|
|
1642
|
-
try {
|
|
1643
|
-
return JSON.parse(line.slice(5)).message?.usage ?? null;
|
|
1644
|
-
} catch {
|
|
1645
|
-
return null;
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
function trailingUsage(buffer) {
|
|
1649
|
-
if (!buffer.trim().startsWith("{"))
|
|
1650
|
-
return null;
|
|
1651
|
-
try {
|
|
1652
|
-
const json = JSON.parse(buffer);
|
|
1653
|
-
if (json.usage)
|
|
1654
|
-
return json.usage;
|
|
1655
|
-
return typeof json.input_tokens === "number" ? { input_tokens: json.input_tokens } : null;
|
|
1656
|
-
} catch {
|
|
1657
|
-
return null;
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
function scanEvents(buffer) {
|
|
1661
|
-
let rest = buffer;
|
|
1662
|
-
for (let newline = rest.indexOf(`
|
|
1663
|
-
`);newline !== -1; newline = rest.indexOf(`
|
|
1664
|
-
`)) {
|
|
1665
|
-
const usage = usageIn(rest.slice(0, newline).trim());
|
|
1666
|
-
rest = rest.slice(newline + 1);
|
|
1667
|
-
if (usage)
|
|
1668
|
-
return { usage, rest };
|
|
1669
|
-
}
|
|
1670
|
-
return { usage: null, rest };
|
|
1671
|
-
}
|
|
1672
|
-
async function readUsage(stream) {
|
|
1673
|
-
const reader = stream.pipeThrough(new TextDecoderStream).getReader();
|
|
1674
|
-
let buffer = "";
|
|
1675
|
-
let usage = null;
|
|
1676
|
-
while (true) {
|
|
1677
|
-
const { value, done } = await reader.read();
|
|
1678
|
-
if (done)
|
|
1679
|
-
return usage ?? trailingUsage(buffer);
|
|
1680
|
-
if (usage)
|
|
1681
|
-
continue;
|
|
1682
|
-
const scanned = scanEvents(buffer + value);
|
|
1683
|
-
usage = scanned.usage;
|
|
1684
|
-
buffer = scanned.rest.length > 4000000 ? "" : scanned.rest;
|
|
1685
|
-
}
|
|
1825
|
+
return folded2(threadKey, keys, state);
|
|
1686
1826
|
}
|
|
1687
|
-
function watchUsage(response, onUsage) {
|
|
1688
|
-
if (!response.body)
|
|
1689
|
-
return response;
|
|
1690
|
-
const [toClient, toUs] = response.body.tee();
|
|
1691
|
-
readUsage(toUs).then((usage) => {
|
|
1692
|
-
if (usage)
|
|
1693
|
-
onUsage(usage);
|
|
1694
|
-
}).catch(() => {
|
|
1695
|
-
return;
|
|
1696
|
-
});
|
|
1697
|
-
return new Response(toClient, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
1698
|
-
}
|
|
1699
|
-
function inputTokens(usage) {
|
|
1700
|
-
return (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
|
|
1701
|
-
}
|
|
1702
|
-
var OVERFLOW_MARGIN = 1.1;
|
|
1703
|
-
var charsOf = (sent) => typeof sent === "number" ? sent : sizeOf(sent);
|
|
1704
|
-
function overflowTokens(errorText) {
|
|
1705
|
-
const tokens = Number(errorText.match(/prompt is too long[^0-9]*(\d+)\s*tokens?\s*>\s*(\d+)/i)?.[1]);
|
|
1706
|
-
return Number.isFinite(tokens) && tokens > 0 ? tokens : null;
|
|
1707
|
-
}
|
|
1708
|
-
function recordUsage(store, threadKey, sent, usage) {
|
|
1709
|
-
const input = inputTokens(usage);
|
|
1710
|
-
const chars = charsOf(sent);
|
|
1711
|
-
if (input <= 0 || chars <= 0)
|
|
1712
|
-
return false;
|
|
1713
|
-
const state = store.read(threadKey);
|
|
1714
|
-
state.ratio = input / chars;
|
|
1715
|
-
store.save(threadKey, state);
|
|
1716
|
-
return true;
|
|
1717
|
-
}
|
|
1718
|
-
function recordOverflow(store, threadKey, sent, errorText) {
|
|
1719
|
-
const tokens = overflowTokens(errorText);
|
|
1720
|
-
const chars = charsOf(sent);
|
|
1721
|
-
if (!tokens || chars <= 0)
|
|
1722
|
-
return null;
|
|
1723
|
-
const state = store.read(threadKey);
|
|
1724
|
-
state.ratio = Math.max(state.ratio ?? 0, tokens / chars) * OVERFLOW_MARGIN;
|
|
1725
|
-
store.save(threadKey, state);
|
|
1726
|
-
return tokens;
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
1827
|
// src/core/window.ts
|
|
1730
|
-
var LOOKUP_TIMEOUT_MS =
|
|
1828
|
+
var LOOKUP_TIMEOUT_MS = 1500;
|
|
1829
|
+
var RETRY_AFTER_MS = 10 * 60000;
|
|
1830
|
+
var LONG_SUFFIX = /\[1m\]$/i;
|
|
1831
|
+
var SUFFIX = /\[[^\]]*\]$/;
|
|
1731
1832
|
function lookupHeaders(requestHeaders) {
|
|
1732
1833
|
const headers = new Headers;
|
|
1733
1834
|
for (const name of ["authorization", "x-api-key", "anthropic-version", "anthropic-beta"]) {
|
|
@@ -1739,10 +1840,21 @@ function lookupHeaders(requestHeaders) {
|
|
|
1739
1840
|
headers.set("anthropic-version", "2023-06-01");
|
|
1740
1841
|
return headers;
|
|
1741
1842
|
}
|
|
1742
|
-
function
|
|
1843
|
+
function familyWindow(model) {
|
|
1844
|
+
if (LONG_SUFFIX.test(model))
|
|
1845
|
+
return 1e6;
|
|
1846
|
+
const id = model.replace(SUFFIX, "");
|
|
1847
|
+
if (/^claude-(?:(?:opus|sonnet|haiku|fable)-5|opus-4-8)/.test(id))
|
|
1848
|
+
return 1e6;
|
|
1849
|
+
if (id.startsWith("claude-"))
|
|
1850
|
+
return 200000;
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
function createWindowLookup({ known = new Map, onLearn, timeoutMs = LOOKUP_TIMEOUT_MS, retryAfterMs = RETRY_AFTER_MS } = {}) {
|
|
1743
1854
|
const lookups = new Map;
|
|
1855
|
+
const failedAt = new Map;
|
|
1744
1856
|
const lookup = async (id, requestHeaders, upstream) => {
|
|
1745
|
-
const response = await fetch(`${upstream}/v1/models/${encodeURIComponent(id)}`, { headers: lookupHeaders(requestHeaders), signal: AbortSignal.timeout(
|
|
1857
|
+
const response = await fetch(`${upstream}/v1/models/${encodeURIComponent(id)}`, { headers: lookupHeaders(requestHeaders), signal: AbortSignal.timeout(timeoutMs) });
|
|
1746
1858
|
if (!response.ok)
|
|
1747
1859
|
return null;
|
|
1748
1860
|
const info = await response.json();
|
|
@@ -1753,18 +1865,24 @@ function createWindowLookup({ known = new Map, onLearn } = {}) {
|
|
|
1753
1865
|
return info.max_input_tokens;
|
|
1754
1866
|
};
|
|
1755
1867
|
return (model, requestHeaders, upstream = "https://api.anthropic.com") => {
|
|
1756
|
-
const
|
|
1868
|
+
const given = String(model ?? "");
|
|
1869
|
+
const id = given.replace(SUFFIX, "");
|
|
1757
1870
|
if (!id)
|
|
1758
1871
|
return Promise.resolve(null);
|
|
1759
1872
|
const window = known.get(id);
|
|
1760
1873
|
if (window)
|
|
1761
1874
|
return Promise.resolve(window);
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1875
|
+
const fallback = familyWindow(given);
|
|
1876
|
+
const failed = failedAt.get(id);
|
|
1877
|
+
if (failed !== undefined && Date.now() - failed < retryAfterMs)
|
|
1878
|
+
return Promise.resolve(fallback);
|
|
1879
|
+
const pending = lookups.get(id) ?? lookup(id, requestHeaders, upstream).catch(() => null).then((found) => {
|
|
1880
|
+
if (found === null)
|
|
1881
|
+
failedAt.set(id, Date.now());
|
|
1882
|
+
return found;
|
|
1883
|
+
}).finally(() => lookups.delete(id));
|
|
1766
1884
|
lookups.set(id, pending);
|
|
1767
|
-
return pending;
|
|
1885
|
+
return pending.then((found) => found ?? fallback);
|
|
1768
1886
|
};
|
|
1769
1887
|
}
|
|
1770
1888
|
|
|
@@ -1801,7 +1919,7 @@ function defaultPrepare(options, upstream) {
|
|
|
1801
1919
|
return rewriteAsync({ body, threadId, config: options.config ?? CORE_DEFAULTS, store: options.store, sources, window, mayCompact }, options.reranker);
|
|
1802
1920
|
};
|
|
1803
1921
|
}
|
|
1804
|
-
var passthrough = (bodyText) => ({ note: { action: "pass" }, threadKey: null, bodyText, sentSize: 0, givenSize: 0, fixedSize: 0 });
|
|
1922
|
+
var passthrough = (bodyText) => ({ note: { action: "pass" }, model: undefined, threadKey: null, bodyText, sentSize: 0, givenSize: 0, fixedSize: 0 });
|
|
1805
1923
|
async function outgoing(prepare, request, givenText, mayCompact) {
|
|
1806
1924
|
try {
|
|
1807
1925
|
const given = JSON.parse(givenText);
|
|
@@ -1809,6 +1927,7 @@ async function outgoing(prepare, request, givenText, mayCompact) {
|
|
|
1809
1927
|
const sentSize = sizeOf(result.body);
|
|
1810
1928
|
return {
|
|
1811
1929
|
note: result.note,
|
|
1930
|
+
model: given.model,
|
|
1812
1931
|
threadKey: result.threadKey,
|
|
1813
1932
|
bodyText: result.body !== given ? JSON.stringify(result.body, wellFormed) : givenText,
|
|
1814
1933
|
sentSize,
|
|
@@ -1889,7 +2008,7 @@ function createForwarder(options) {
|
|
|
1889
2008
|
if (answer.status >= 400 && out.note.action !== "pass") {
|
|
1890
2009
|
const errorText = await answer.text();
|
|
1891
2010
|
const logged = { path: url.pathname, status: answer.status, thread: out.threadKey, ...out.note };
|
|
1892
|
-
const overflow = out.threadKey && !isCountTokens ? recordOverflow(options.store, out.threadKey, out.sentSize, errorText) : null;
|
|
2011
|
+
const overflow = out.threadKey && !isCountTokens ? recordOverflow(options.store, out.threadKey, out.sentSize, errorText, out.model) : null;
|
|
1893
2012
|
if (!overflow) {
|
|
1894
2013
|
log({ ...logged, error: clipped(errorText) });
|
|
1895
2014
|
return errorResponse(errorText);
|