@librechat/agents 3.6.11 → 3.6.13
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/cjs/agents/AgentContext.cjs +4 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +45 -116
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/contextPressureMeter.cjs +212 -0
- package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -0
- package/dist/cjs/llm/invoke.cjs +46 -75
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/prepareProviderRequest.cjs +108 -0
- package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -0
- package/dist/cjs/llm/tokenCounterCacheCompatibility.cjs +14 -0
- package/dist/cjs/llm/tokenCounterCacheCompatibility.cjs.map +1 -0
- package/dist/cjs/main.cjs +4 -0
- package/dist/cjs/messages/content.cjs +5 -4
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +3 -2
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +4 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +48 -119
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/contextPressureMeter.mjs +211 -0
- package/dist/esm/llm/contextPressureMeter.mjs.map +1 -0
- package/dist/esm/llm/invoke.mjs +48 -75
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/prepareProviderRequest.mjs +105 -0
- package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -0
- package/dist/esm/llm/tokenCounterCacheCompatibility.mjs +13 -0
- package/dist/esm/llm/tokenCounterCacheCompatibility.mjs.map +1 -0
- package/dist/esm/main.mjs +3 -1
- package/dist/esm/messages/content.mjs +5 -4
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +3 -2
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +3 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/llm/contextPressureMeter.d.ts +36 -0
- package/dist/types/llm/invoke.d.ts +31 -33
- package/dist/types/llm/prepareProviderRequest.d.ts +55 -0
- package/dist/types/llm/tokenCounterCacheCompatibility.d.ts +7 -0
- package/package.json +2 -1
- package/src/agents/AgentContext.ts +9 -0
- package/src/graphs/Graph.ts +70 -276
- package/src/index.ts +9 -0
- package/src/llm/contextPressureMeter.ts +449 -0
- package/src/llm/invoke.ts +136 -296
- package/src/llm/prepareProviderRequest.ts +357 -0
- package/src/llm/tokenCounterCacheCompatibility.ts +22 -0
- package/src/messages/content.ts +3 -3
- package/src/utils/tokens.ts +3 -2
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { isProxy } from 'node:util/types';
|
|
2
|
+
import { ToolMessage } from '@langchain/core/messages';
|
|
3
|
+
|
|
4
|
+
import type { AIMessage, BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import type { ProviderPayloadMeasurement } from '@/llm/prepareProviderRequest';
|
|
6
|
+
import type * as t from '@/types';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
REPLY_PRIMER_TOKENS,
|
|
10
|
+
isSyntheticProviderContextMessage,
|
|
11
|
+
} from '@/messages';
|
|
12
|
+
import { apportionTokenCounts } from '@/utils';
|
|
13
|
+
|
|
14
|
+
interface ContextPressureUsage {
|
|
15
|
+
contextBudget?: number;
|
|
16
|
+
effectiveInstructionTokens?: number;
|
|
17
|
+
remainingContextTokens?: number;
|
|
18
|
+
calibrationRatio?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ContextPressureMeterParams {
|
|
22
|
+
tokenCounter?: t.TokenCounter;
|
|
23
|
+
tokenCountCache?: ExactTokenCountCache;
|
|
24
|
+
sourceMessages: BaseMessage[];
|
|
25
|
+
retainedMessages: BaseMessage[];
|
|
26
|
+
indexTokenCountMap: Record<string, number | undefined>;
|
|
27
|
+
contextUsage?: ContextPressureUsage | null;
|
|
28
|
+
instructionTokens: number;
|
|
29
|
+
calibrationRatio: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface ProviderPayloadMeasureOptions {
|
|
33
|
+
contextBudget?: number;
|
|
34
|
+
forceRawRecount?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ProviderMessageBaseline {
|
|
38
|
+
rawTokens: number;
|
|
39
|
+
accountingWeight: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ProviderBaselineAttribution {
|
|
43
|
+
attributedByOrigin: number[];
|
|
44
|
+
projectedBaseTokens: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ContextPressureMeter {
|
|
48
|
+
trackProjection(before: BaseMessage[], after: BaseMessage[]): BaseMessage[];
|
|
49
|
+
trackClone(source: BaseMessage, clone: BaseMessage): void;
|
|
50
|
+
measure(
|
|
51
|
+
messages: BaseMessage[],
|
|
52
|
+
options?: ProviderPayloadMeasureOptions
|
|
53
|
+
): ProviderPayloadMeasurement;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ExactTokenCountCache {
|
|
57
|
+
count(message: BaseMessage): number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface StableTokenSurface {
|
|
61
|
+
content: string;
|
|
62
|
+
messageType: string;
|
|
63
|
+
role?: string;
|
|
64
|
+
additionalType?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface ExactTokenCountCacheEntry {
|
|
68
|
+
surface: StableTokenSurface;
|
|
69
|
+
tokens: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readDataProperty(
|
|
73
|
+
owner: object,
|
|
74
|
+
property: PropertyKey
|
|
75
|
+
): { own: boolean; safe: boolean; value?: unknown } {
|
|
76
|
+
let descriptor: PropertyDescriptor | undefined;
|
|
77
|
+
try {
|
|
78
|
+
descriptor = Object.getOwnPropertyDescriptor(owner, property);
|
|
79
|
+
} catch {
|
|
80
|
+
return { own: false, safe: false };
|
|
81
|
+
}
|
|
82
|
+
if (descriptor == null) {
|
|
83
|
+
return { own: false, safe: true };
|
|
84
|
+
}
|
|
85
|
+
return 'value' in descriptor
|
|
86
|
+
? { own: true, safe: true, value: descriptor.value }
|
|
87
|
+
: { own: true, safe: false };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getStableTokenSurface(
|
|
91
|
+
message: BaseMessage
|
|
92
|
+
): StableTokenSurface | undefined {
|
|
93
|
+
if (isProxy(message)) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const contentProperty = readDataProperty(message, 'content');
|
|
97
|
+
if (
|
|
98
|
+
!contentProperty.safe ||
|
|
99
|
+
!contentProperty.own ||
|
|
100
|
+
typeof contentProperty.value !== 'string'
|
|
101
|
+
) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const messageType = message.getType();
|
|
105
|
+
const roleProperty = readDataProperty(message, 'role');
|
|
106
|
+
if (!roleProperty.safe || (!roleProperty.own && 'role' in message)) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const role = roleProperty.value;
|
|
110
|
+
if (role != null && typeof role !== 'string') {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const additionalKwargsProperty = readDataProperty(
|
|
115
|
+
message,
|
|
116
|
+
'additional_kwargs'
|
|
117
|
+
);
|
|
118
|
+
if (!additionalKwargsProperty.safe || !additionalKwargsProperty.own) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const rawAdditionalKwargs = additionalKwargsProperty.value;
|
|
122
|
+
if (
|
|
123
|
+
rawAdditionalKwargs == null ||
|
|
124
|
+
typeof rawAdditionalKwargs !== 'object' ||
|
|
125
|
+
isProxy(rawAdditionalKwargs)
|
|
126
|
+
) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const additionalKwargs = rawAdditionalKwargs;
|
|
130
|
+
const typeProperty = readDataProperty(additionalKwargs, 'type');
|
|
131
|
+
if (!typeProperty.safe) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
if (messageType === 'ai' || role === 'assistant') {
|
|
135
|
+
const toolCallsProperty = readDataProperty(
|
|
136
|
+
message as AIMessage,
|
|
137
|
+
'tool_calls'
|
|
138
|
+
);
|
|
139
|
+
if (
|
|
140
|
+
!toolCallsProperty.safe ||
|
|
141
|
+
(!toolCallsProperty.own && 'tool_calls' in message)
|
|
142
|
+
) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
const toolCalls = toolCallsProperty.value;
|
|
146
|
+
if (
|
|
147
|
+
toolCalls != null &&
|
|
148
|
+
(!Array.isArray(toolCalls) || isProxy(toolCalls) || toolCalls.length > 0)
|
|
149
|
+
) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const functionCall = readDataProperty(additionalKwargs, 'function_call');
|
|
153
|
+
if (!functionCall.safe || functionCall.value != null) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let additionalType: string | undefined;
|
|
159
|
+
if (messageType === 'tool') {
|
|
160
|
+
const typeValue = typeProperty.value;
|
|
161
|
+
if (typeValue != null && typeof typeValue !== 'string') {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
additionalType = typeof typeValue === 'string' ? typeValue : undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
content: contentProperty.value,
|
|
169
|
+
messageType,
|
|
170
|
+
...(role != null && { role }),
|
|
171
|
+
...(additionalType != null && { additionalType }),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function tokenSurfacesMatch(
|
|
176
|
+
left: StableTokenSurface,
|
|
177
|
+
right: StableTokenSurface
|
|
178
|
+
): boolean {
|
|
179
|
+
return (
|
|
180
|
+
left.content === right.content &&
|
|
181
|
+
left.messageType === right.messageType &&
|
|
182
|
+
left.role === right.role &&
|
|
183
|
+
left.additionalType === right.additionalType
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Reuses exact counts only while every token-relevant stable field matches. */
|
|
188
|
+
export function createExactTokenCountCache(
|
|
189
|
+
tokenCounter: t.TokenCounter
|
|
190
|
+
): ExactTokenCountCache {
|
|
191
|
+
const entries = new WeakMap<BaseMessage, ExactTokenCountCacheEntry>();
|
|
192
|
+
return {
|
|
193
|
+
count(message): number {
|
|
194
|
+
const surface = getStableTokenSurface(message);
|
|
195
|
+
const cached = entries.get(message);
|
|
196
|
+
if (
|
|
197
|
+
surface != null &&
|
|
198
|
+
cached != null &&
|
|
199
|
+
tokenSurfacesMatch(cached.surface, surface)
|
|
200
|
+
) {
|
|
201
|
+
return cached.tokens;
|
|
202
|
+
}
|
|
203
|
+
const tokens = tokenCounter(message);
|
|
204
|
+
if (surface != null) {
|
|
205
|
+
entries.set(message, { surface, tokens });
|
|
206
|
+
} else {
|
|
207
|
+
entries.delete(message);
|
|
208
|
+
}
|
|
209
|
+
return tokens;
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getProviderMessageOriginKey(message: BaseMessage): string | undefined {
|
|
215
|
+
const type = message.getType();
|
|
216
|
+
if (
|
|
217
|
+
message instanceof ToolMessage &&
|
|
218
|
+
typeof message.tool_call_id === 'string' &&
|
|
219
|
+
message.tool_call_id.length > 0
|
|
220
|
+
) {
|
|
221
|
+
return `tool:call:${message.tool_call_id}`;
|
|
222
|
+
}
|
|
223
|
+
if (typeof message.id === 'string' && message.id.length > 0) {
|
|
224
|
+
return `${type}:id:${message.id}`;
|
|
225
|
+
}
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Measures repeated provider projections while tokenizing each message object once. */
|
|
230
|
+
export function createContextPressureMeter({
|
|
231
|
+
tokenCounter,
|
|
232
|
+
tokenCountCache,
|
|
233
|
+
sourceMessages,
|
|
234
|
+
retainedMessages,
|
|
235
|
+
indexTokenCountMap,
|
|
236
|
+
contextUsage,
|
|
237
|
+
instructionTokens,
|
|
238
|
+
calibrationRatio,
|
|
239
|
+
}: ContextPressureMeterParams): ContextPressureMeter {
|
|
240
|
+
const tokenCounts = new WeakMap<BaseMessage, number>();
|
|
241
|
+
const origins = new WeakMap<BaseMessage, number>();
|
|
242
|
+
const baselineAttributions = new Map<number, ProviderBaselineAttribution>();
|
|
243
|
+
let baseline: ProviderMessageBaseline[] | undefined;
|
|
244
|
+
let baselineWeights: Record<string, number> | undefined;
|
|
245
|
+
let totalBaselineWeight = 0;
|
|
246
|
+
|
|
247
|
+
const count = (message: BaseMessage): number => {
|
|
248
|
+
const cached = tokenCounts.get(message);
|
|
249
|
+
if (cached != null) {
|
|
250
|
+
return cached;
|
|
251
|
+
}
|
|
252
|
+
const tokens =
|
|
253
|
+
tokenCountCache?.count(message) ?? tokenCounter?.(message) ?? 0;
|
|
254
|
+
tokenCounts.set(message, tokens);
|
|
255
|
+
return tokens;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
if (
|
|
259
|
+
contextUsage != null &&
|
|
260
|
+
(tokenCounter != null || tokenCountCache != null)
|
|
261
|
+
) {
|
|
262
|
+
const sourceIndices = new WeakMap<BaseMessage, number>();
|
|
263
|
+
for (let i = 0; i < sourceMessages.length; i++) {
|
|
264
|
+
sourceIndices.set(sourceMessages[i], i);
|
|
265
|
+
}
|
|
266
|
+
baseline = retainedMessages.map((message, index) => {
|
|
267
|
+
const rawTokens = count(message);
|
|
268
|
+
const sourceIndex = sourceIndices.get(message);
|
|
269
|
+
const indexedTokens =
|
|
270
|
+
sourceIndex != null ? indexTokenCountMap[sourceIndex] : undefined;
|
|
271
|
+
const accountingWeight =
|
|
272
|
+
indexedTokens != null &&
|
|
273
|
+
Number.isFinite(indexedTokens) &&
|
|
274
|
+
indexedTokens >= 0
|
|
275
|
+
? indexedTokens
|
|
276
|
+
: rawTokens;
|
|
277
|
+
if (!origins.has(message)) {
|
|
278
|
+
origins.set(message, index);
|
|
279
|
+
}
|
|
280
|
+
return { rawTokens, accountingWeight };
|
|
281
|
+
});
|
|
282
|
+
baselineWeights = {};
|
|
283
|
+
for (let i = 0; i < baseline.length; i++) {
|
|
284
|
+
baselineWeights[i] = baseline[i].accountingWeight;
|
|
285
|
+
totalBaselineWeight += baseline[i].accountingWeight;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const trackClone = (source: BaseMessage, clone: BaseMessage): void => {
|
|
290
|
+
const origin = origins.get(source);
|
|
291
|
+
if (origin != null && !origins.has(clone)) {
|
|
292
|
+
origins.set(clone, origin);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const trackProjection = (
|
|
297
|
+
before: BaseMessage[],
|
|
298
|
+
after: BaseMessage[]
|
|
299
|
+
): BaseMessage[] => {
|
|
300
|
+
if (baseline == null || before === after) {
|
|
301
|
+
return after;
|
|
302
|
+
}
|
|
303
|
+
if (before.length === after.length) {
|
|
304
|
+
for (let i = 0; i < after.length; i++) {
|
|
305
|
+
if (
|
|
306
|
+
before[i].getType() === after[i].getType() &&
|
|
307
|
+
!isSyntheticProviderContextMessage(after[i])
|
|
308
|
+
) {
|
|
309
|
+
trackClone(before[i], after[i]);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return after;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const keyedOrigins = new Map<string, number | null>();
|
|
316
|
+
for (const message of before) {
|
|
317
|
+
const origin = origins.get(message);
|
|
318
|
+
const key = getProviderMessageOriginKey(message);
|
|
319
|
+
if (origin == null || key == null) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
keyedOrigins.set(key, keyedOrigins.has(key) ? null : origin);
|
|
323
|
+
}
|
|
324
|
+
for (const message of after) {
|
|
325
|
+
if (origins.has(message) || isSyntheticProviderContextMessage(message)) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const key = getProviderMessageOriginKey(message);
|
|
329
|
+
const origin = key != null ? keyedOrigins.get(key) : undefined;
|
|
330
|
+
if (origin != null) {
|
|
331
|
+
origins.set(message, origin);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return after;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const measure = (
|
|
338
|
+
messages: BaseMessage[],
|
|
339
|
+
options?: ProviderPayloadMeasureOptions
|
|
340
|
+
): ProviderPayloadMeasurement => {
|
|
341
|
+
const contextBudget = options?.contextBudget ?? contextUsage?.contextBudget;
|
|
342
|
+
const forceRawRecount = options?.forceRawRecount === true;
|
|
343
|
+
const effectiveInstructionTokens =
|
|
344
|
+
contextUsage?.effectiveInstructionTokens ??
|
|
345
|
+
(forceRawRecount ? instructionTokens : undefined);
|
|
346
|
+
if (
|
|
347
|
+
(tokenCounter == null && tokenCountCache == null) ||
|
|
348
|
+
contextBudget == null ||
|
|
349
|
+
effectiveInstructionTokens == null
|
|
350
|
+
) {
|
|
351
|
+
return { fits: true };
|
|
352
|
+
}
|
|
353
|
+
const availableMessageTokens = Math.max(
|
|
354
|
+
0,
|
|
355
|
+
contextBudget - effectiveInstructionTokens
|
|
356
|
+
);
|
|
357
|
+
let usageRatio = calibrationRatio > 0 ? calibrationRatio : 1;
|
|
358
|
+
if (
|
|
359
|
+
contextUsage?.calibrationRatio != null &&
|
|
360
|
+
contextUsage.calibrationRatio > 0
|
|
361
|
+
) {
|
|
362
|
+
usageRatio = contextUsage.calibrationRatio;
|
|
363
|
+
}
|
|
364
|
+
if (forceRawRecount) {
|
|
365
|
+
usageRatio = Math.max(1, usageRatio);
|
|
366
|
+
}
|
|
367
|
+
const baselineRemaining = contextUsage?.remainingContextTokens;
|
|
368
|
+
const accountedMessageTokens =
|
|
369
|
+
!forceRawRecount &&
|
|
370
|
+
baseline != null &&
|
|
371
|
+
baselineRemaining != null &&
|
|
372
|
+
Number.isFinite(baselineRemaining)
|
|
373
|
+
? availableMessageTokens -
|
|
374
|
+
Math.min(availableMessageTokens, Math.max(0, baselineRemaining))
|
|
375
|
+
: undefined;
|
|
376
|
+
|
|
377
|
+
let projectedMessageTokens: number;
|
|
378
|
+
if (
|
|
379
|
+
accountedMessageTokens != null &&
|
|
380
|
+
baseline != null &&
|
|
381
|
+
baselineWeights != null
|
|
382
|
+
) {
|
|
383
|
+
let attribution = baselineAttributions.get(availableMessageTokens);
|
|
384
|
+
if (attribution == null) {
|
|
385
|
+
const replyPrimerTokens = Math.round(
|
|
386
|
+
REPLY_PRIMER_TOKENS * usageRatio
|
|
387
|
+
);
|
|
388
|
+
const attributableTokens =
|
|
389
|
+
totalBaselineWeight > 0
|
|
390
|
+
? Math.min(
|
|
391
|
+
Math.max(0, accountedMessageTokens - replyPrimerTokens),
|
|
392
|
+
Math.round(totalBaselineWeight * usageRatio)
|
|
393
|
+
)
|
|
394
|
+
: 0;
|
|
395
|
+
const apportionedTokens =
|
|
396
|
+
totalBaselineWeight > 0
|
|
397
|
+
? apportionTokenCounts(
|
|
398
|
+
baselineWeights,
|
|
399
|
+
attributableTokens / totalBaselineWeight,
|
|
400
|
+
attributableTokens
|
|
401
|
+
)
|
|
402
|
+
: {};
|
|
403
|
+
attribution = {
|
|
404
|
+
attributedByOrigin: baseline.map(
|
|
405
|
+
(_, origin) => apportionedTokens[origin] || 0
|
|
406
|
+
),
|
|
407
|
+
projectedBaseTokens: Math.max(
|
|
408
|
+
replyPrimerTokens,
|
|
409
|
+
accountedMessageTokens - attributableTokens
|
|
410
|
+
),
|
|
411
|
+
};
|
|
412
|
+
baselineAttributions.set(availableMessageTokens, attribution);
|
|
413
|
+
}
|
|
414
|
+
projectedMessageTokens = attribution.projectedBaseTokens;
|
|
415
|
+
let newRawTokens = 0;
|
|
416
|
+
const usedOrigins = new Set<number>();
|
|
417
|
+
for (const message of messages) {
|
|
418
|
+
const rawTokens = count(message);
|
|
419
|
+
const origin = origins.get(message);
|
|
420
|
+
if (origin == null || usedOrigins.has(origin)) {
|
|
421
|
+
newRawTokens += rawTokens;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
usedOrigins.add(origin);
|
|
425
|
+
projectedMessageTokens += Math.max(
|
|
426
|
+
0,
|
|
427
|
+
attribution.attributedByOrigin[origin] +
|
|
428
|
+
Math.round((rawTokens - baseline[origin].rawTokens) * usageRatio)
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
projectedMessageTokens += Math.round(newRawTokens * usageRatio);
|
|
432
|
+
} else {
|
|
433
|
+
let rawTokens = REPLY_PRIMER_TOKENS;
|
|
434
|
+
for (const message of messages) {
|
|
435
|
+
rawTokens += count(message);
|
|
436
|
+
}
|
|
437
|
+
projectedMessageTokens = Math.round(rawTokens * usageRatio);
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
fits: projectedMessageTokens <= availableMessageTokens,
|
|
441
|
+
projectedMessageTokens,
|
|
442
|
+
availableMessageTokens,
|
|
443
|
+
contextBudget,
|
|
444
|
+
effectiveInstructionTokens,
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
return { trackProjection, trackClone, measure };
|
|
449
|
+
}
|