@animalabs/membrane 0.5.80 → 0.5.81
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/context/index.d.ts +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +1 -1
- package/dist/context/index.js.map +1 -1
- package/dist/context/process.d.ts +3 -1
- package/dist/context/process.d.ts.map +1 -1
- package/dist/context/process.js +230 -49
- package/dist/context/process.js.map +1 -1
- package/dist/context/types.d.ts +30 -0
- package/dist/context/types.d.ts.map +1 -1
- package/dist/context/types.js +16 -0
- package/dist/context/types.js.map +1 -1
- package/dist/floating-cache-marker.test.js +21 -0
- package/dist/floating-cache-marker.test.js.map +1 -1
- package/dist/formatters/anthropic-xml.d.ts +2 -0
- package/dist/formatters/anthropic-xml.d.ts.map +1 -1
- package/dist/formatters/anthropic-xml.js +57 -19
- package/dist/formatters/anthropic-xml.js.map +1 -1
- package/dist/formatters/normalize-tool-pairs.d.ts +7 -0
- package/dist/formatters/normalize-tool-pairs.d.ts.map +1 -1
- package/dist/formatters/normalize-tool-pairs.js +45 -0
- package/dist/formatters/normalize-tool-pairs.js.map +1 -1
- package/dist/formatters/types.d.ts +29 -0
- package/dist/formatters/types.d.ts.map +1 -1
- package/dist/membrane.d.ts +137 -8
- package/dist/membrane.d.ts.map +1 -1
- package/dist/membrane.js +812 -310
- package/dist/membrane.js.map +1 -1
- package/dist/providers/anthropic.d.ts +26 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +142 -25
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts +8 -0
- package/dist/providers/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +24 -1
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/gemini.d.ts +11 -0
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +104 -82
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/mock.d.ts +2 -0
- package/dist/providers/mock.d.ts.map +1 -1
- package/dist/providers/mock.js +2 -0
- package/dist/providers/mock.js.map +1 -1
- package/dist/providers/openai-compatible.d.ts +7 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -1
- package/dist/providers/openai-compatible.js +99 -48
- package/dist/providers/openai-compatible.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +5 -0
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +107 -44
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-api.d.ts +16 -0
- package/dist/providers/openai-responses-api.d.ts.map +1 -1
- package/dist/providers/openai-responses-api.js +38 -10
- package/dist/providers/openai-responses-api.js.map +1 -1
- package/dist/providers/openai-responses.d.ts +2 -0
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +8 -1
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +92 -44
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +9 -0
- package/dist/providers/openrouter.d.ts.map +1 -1
- package/dist/providers/openrouter.js +118 -62
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/utils.d.ts +74 -2
- package/dist/providers/utils.d.ts.map +1 -1
- package/dist/providers/utils.js +184 -3
- package/dist/providers/utils.js.map +1 -1
- package/dist/registry/default-pricing.d.ts +22 -0
- package/dist/registry/default-pricing.d.ts.map +1 -1
- package/dist/registry/default-pricing.js +162 -28
- package/dist/registry/default-pricing.js.map +1 -1
- package/dist/types/content.d.ts +13 -0
- package/dist/types/content.d.ts.map +1 -1
- package/dist/types/content.js.map +1 -1
- package/dist/types/errors.d.ts +15 -0
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +26 -0
- package/dist/types/errors.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/provider.d.ts +50 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/response.d.ts +85 -4
- package/dist/types/response.d.ts.map +1 -1
- package/dist/types/response.js.map +1 -1
- package/dist/types/streaming.d.ts +6 -1
- package/dist/types/streaming.d.ts.map +1 -1
- package/dist/utils/cache-marker-budget.d.ts +82 -0
- package/dist/utils/cache-marker-budget.d.ts.map +1 -0
- package/dist/utils/cache-marker-budget.js +189 -0
- package/dist/utils/cache-marker-budget.js.map +1 -0
- package/dist/utils/cost.d.ts +22 -0
- package/dist/utils/cost.d.ts.map +1 -1
- package/dist/utils/cost.js +55 -0
- package/dist/utils/cost.js.map +1 -1
- package/dist/utils/thinking-carriers.d.ts +80 -0
- package/dist/utils/thinking-carriers.d.ts.map +1 -0
- package/dist/utils/thinking-carriers.js +115 -0
- package/dist/utils/thinking-carriers.js.map +1 -0
- package/dist/utils/tool-parser.d.ts +24 -0
- package/dist/utils/tool-parser.d.ts.map +1 -1
- package/dist/utils/tool-parser.js +367 -161
- package/dist/utils/tool-parser.js.map +1 -1
- package/dist/utils/usage.d.ts +71 -0
- package/dist/utils/usage.d.ts.map +1 -0
- package/dist/utils/usage.js +161 -0
- package/dist/utils/usage.js.map +1 -0
- package/dist/yielding-stream.d.ts +8 -0
- package/dist/yielding-stream.d.ts.map +1 -1
- package/dist/yielding-stream.js +49 -3
- package/dist/yielding-stream.js.map +1 -1
- package/package.json +1 -1
- package/src/context/index.ts +1 -0
- package/src/context/process.ts +305 -59
- package/src/context/types.ts +38 -0
- package/src/floating-cache-marker.test.ts +24 -0
- package/src/formatters/anthropic-xml.ts +62 -20
- package/src/formatters/normalize-tool-pairs.ts +49 -0
- package/src/formatters/types.ts +32 -0
- package/src/membrane.ts +963 -313
- package/src/providers/anthropic.ts +161 -19
- package/src/providers/bedrock.ts +25 -1
- package/src/providers/gemini.ts +125 -87
- package/src/providers/mock.ts +3 -0
- package/src/providers/openai-compatible.ts +102 -45
- package/src/providers/openai-completions.ts +112 -46
- package/src/providers/openai-responses-api.ts +45 -15
- package/src/providers/openai-responses.ts +8 -1
- package/src/providers/openai.ts +95 -42
- package/src/providers/openrouter.ts +125 -63
- package/src/providers/utils.ts +227 -6
- package/src/registry/default-pricing.ts +165 -28
- package/src/types/content.ts +14 -0
- package/src/types/errors.ts +28 -0
- package/src/types/index.ts +6 -0
- package/src/types/provider.ts +55 -0
- package/src/types/response.ts +109 -8
- package/src/types/streaming.ts +8 -2
- package/src/utils/cache-marker-budget.ts +208 -0
- package/src/utils/cost.ts +64 -0
- package/src/utils/thinking-carriers.ts +125 -0
- package/src/utils/tool-parser.ts +473 -174
- package/src/utils/usage.ts +184 -0
- package/src/yielding-stream.ts +54 -3
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
type ToolDefinitionForPrompt,
|
|
35
35
|
} from '../utils/tool-parser.js';
|
|
36
36
|
import { IncrementalXmlParser } from '../utils/stream-parser.js';
|
|
37
|
+
import { clampCacheMarkers } from '../utils/cache-marker-budget.js';
|
|
38
|
+
import { lastCacheableBlockIndex } from './native.js';
|
|
37
39
|
import { isAcceptedImageMediaType, strippedImagePlaceholder } from '../utils/image-media.js';
|
|
38
40
|
|
|
39
41
|
// ============================================================================
|
|
@@ -118,9 +120,13 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
118
120
|
readonly name = 'anthropic-xml';
|
|
119
121
|
readonly usesPrefill = true;
|
|
120
122
|
|
|
123
|
+
/** See PrefillFormatter.configuredToolMode — undefined when the caller left the mode to Membrane. */
|
|
124
|
+
readonly configuredToolMode: 'xml' | 'native' | undefined;
|
|
125
|
+
|
|
121
126
|
private config: Required<AnthropicXmlFormatterConfig>;
|
|
122
127
|
|
|
123
128
|
constructor(config: AnthropicXmlFormatterConfig = {}) {
|
|
129
|
+
this.configuredToolMode = config.toolMode;
|
|
124
130
|
this.config = {
|
|
125
131
|
toolMode: config.toolMode ?? 'xml',
|
|
126
132
|
toolInjectionMode: config.toolInjectionMode ?? 'conversation',
|
|
@@ -149,6 +155,10 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
149
155
|
hasCacheMarker,
|
|
150
156
|
} = options;
|
|
151
157
|
|
|
158
|
+
// Membrane resolves the mode per request and passes it here; the
|
|
159
|
+
// constructor-time mode is the fallback for direct callers only.
|
|
160
|
+
const toolMode = options.toolMode ?? this.config.toolMode;
|
|
161
|
+
|
|
152
162
|
// Build cache_control object (with optional TTL for extended caching)
|
|
153
163
|
const cacheControl: Record<string, unknown> = { type: 'ephemeral' };
|
|
154
164
|
if (cacheTtl) {
|
|
@@ -165,42 +175,66 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
165
175
|
// message continues the same turn, so it must not get a fresh label.
|
|
166
176
|
let lastWasToolResults = false;
|
|
167
177
|
|
|
168
|
-
// Track cache markers applied
|
|
169
|
-
let cacheMarkersApplied = 0;
|
|
170
178
|
|
|
171
179
|
// Calculate tool injection point
|
|
172
180
|
const totalMessages = messages.length;
|
|
173
181
|
const toolInjectionIndex = Math.max(0, totalMessages - this.config.toolInjectionPosition);
|
|
174
182
|
let toolsInjected = false;
|
|
175
183
|
const hasToolsForConversation =
|
|
176
|
-
|
|
184
|
+
toolMode === 'xml' &&
|
|
177
185
|
this.config.toolInjectionMode === 'conversation' &&
|
|
178
186
|
tools &&
|
|
179
187
|
tools.length > 0;
|
|
180
188
|
const toolsText = hasToolsForConversation ? this.formatToolsForInjection(tools!) : '';
|
|
181
189
|
|
|
182
|
-
// Build system content
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
// Build system content. A caller-supplied system ARRAY keeps its block
|
|
191
|
+
// structure and its per-block cache_control: `request.system` explicitly
|
|
192
|
+
// accepts caller-marked blocks, and flattening them into one text block
|
|
193
|
+
// discarded every marker the caller placed (three in, one out) — the
|
|
194
|
+
// caller's stable prefixes then re-paid full input price forever.
|
|
195
|
+
const callerSystemBlocks = Array.isArray(systemPrompt)
|
|
196
|
+
? systemPrompt
|
|
197
|
+
.filter((b): b is ContentBlock & { type: 'text' } => b.type === 'text')
|
|
198
|
+
.map((b) => {
|
|
199
|
+
const callerMarker = (b as unknown as { cache_control?: unknown }).cache_control;
|
|
200
|
+
const block: Record<string, unknown> = { type: 'text', text: b.text };
|
|
201
|
+
if (callerMarker) block.cache_control = callerMarker;
|
|
202
|
+
return block;
|
|
203
|
+
})
|
|
204
|
+
: undefined;
|
|
205
|
+
const systemBlocks = callerSystemBlocks?.length ? callerSystemBlocks : undefined;
|
|
206
|
+
|
|
207
|
+
let systemText = typeof systemPrompt === 'string'
|
|
208
|
+
? systemPrompt
|
|
209
|
+
: (systemBlocks?.map((b) => b.text as string).join('\n') ?? '');
|
|
190
210
|
|
|
191
211
|
// Inject tools into system if configured
|
|
192
|
-
if (
|
|
212
|
+
if (toolMode === 'xml' && this.config.toolInjectionMode === 'system' && tools?.length) {
|
|
193
213
|
const toolsXml = this.formatToolDefinitionsXml(tools);
|
|
194
214
|
systemText = this.injectToolsIntoSystem(systemText, toolsXml);
|
|
215
|
+
if (systemBlocks) {
|
|
216
|
+
// Append to the LAST block only — appending to the join would
|
|
217
|
+
// collapse the array and take every earlier block's marker with it.
|
|
218
|
+
const tail = systemBlocks[systemBlocks.length - 1]!;
|
|
219
|
+
tail.text = this.injectToolsIntoSystem(tail.text as string, toolsXml);
|
|
220
|
+
}
|
|
195
221
|
}
|
|
196
222
|
|
|
197
223
|
// Build system content with optional cache control
|
|
198
224
|
let systemContent: unknown;
|
|
199
|
-
if (
|
|
225
|
+
if (systemBlocks) {
|
|
226
|
+
// The caller's own markers are authoritative: adding one beside them
|
|
227
|
+
// spends a slot the caller already allocated.
|
|
228
|
+
const callerMarkedAny = systemBlocks.some((b) => b.cache_control);
|
|
229
|
+
if (promptCaching && !callerMarkedAny) {
|
|
230
|
+
const bpIdx = lastCacheableBlockIndex(systemBlocks);
|
|
231
|
+
if (bpIdx >= 0) systemBlocks[bpIdx]!.cache_control = cacheControl;
|
|
232
|
+
}
|
|
233
|
+
systemContent = systemBlocks;
|
|
234
|
+
} else if (systemText) {
|
|
200
235
|
const systemBlock: Record<string, unknown> = { type: 'text', text: systemText };
|
|
201
236
|
if (promptCaching) {
|
|
202
237
|
systemBlock.cache_control = cacheControl;
|
|
203
|
-
cacheMarkersApplied++;
|
|
204
238
|
}
|
|
205
239
|
systemContent = [systemBlock];
|
|
206
240
|
}
|
|
@@ -210,7 +244,6 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
210
244
|
const prefixBlock: Record<string, unknown> = { type: 'text', text: contextPrefix };
|
|
211
245
|
if (promptCaching) {
|
|
212
246
|
prefixBlock.cache_control = cacheControl;
|
|
213
|
-
cacheMarkersApplied++;
|
|
214
247
|
}
|
|
215
248
|
providerMessages.push({
|
|
216
249
|
role: 'assistant',
|
|
@@ -276,7 +309,6 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
276
309
|
const content = currentConversation.join(joiner);
|
|
277
310
|
const contentBlock: Record<string, unknown> = { type: 'text', text: content };
|
|
278
311
|
contentBlock.cache_control = cacheControl;
|
|
279
|
-
cacheMarkersApplied++;
|
|
280
312
|
providerMessages.push({
|
|
281
313
|
role: 'assistant',
|
|
282
314
|
content: [contentBlock],
|
|
@@ -344,7 +376,6 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
344
376
|
const content = currentConversation.join(joiner);
|
|
345
377
|
const contentBlock: Record<string, unknown> = { type: 'text', text: content };
|
|
346
378
|
contentBlock.cache_control = cacheControl;
|
|
347
|
-
cacheMarkersApplied++;
|
|
348
379
|
providerMessages.push({
|
|
349
380
|
role: 'assistant',
|
|
350
381
|
content: [contentBlock],
|
|
@@ -396,7 +427,6 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
396
427
|
};
|
|
397
428
|
if (promptCaching) {
|
|
398
429
|
cliSystemBlock.cache_control = cacheControl;
|
|
399
|
-
cacheMarkersApplied++;
|
|
400
430
|
}
|
|
401
431
|
systemContent = [cliSystemBlock];
|
|
402
432
|
providerMessages.unshift({
|
|
@@ -416,10 +446,22 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
416
446
|
const stopSequences = this.buildStopSequences(messages, assistantParticipant, options);
|
|
417
447
|
|
|
418
448
|
// Native tools if configured
|
|
419
|
-
const nativeTools =
|
|
449
|
+
const nativeTools = toolMode === 'native' && tools?.length
|
|
420
450
|
? this.convertToNativeTools(tools)
|
|
421
451
|
: undefined;
|
|
422
452
|
|
|
453
|
+
// Budget. Five sites above attach cache_control (system, contextPrefix,
|
|
454
|
+
// hasCacheMarker flush, cacheBreakpoint flush, CLI-simulation system) and
|
|
455
|
+
// multiple cacheBreakpoints are documented input, so a prefill turn with
|
|
456
|
+
// three marked messages reaches five markers — one over Anthropic's hard
|
|
457
|
+
// limit, which rejects the request outright. Clamping here, once, on the
|
|
458
|
+
// finished artifacts is the only count that can see all five sites; the
|
|
459
|
+
// reported tally is that same recount, so it can never drift from the wire.
|
|
460
|
+
const budget = clampCacheMarkers(
|
|
461
|
+
{ messages: providerMessages, system: systemContent, tools: nativeTools },
|
|
462
|
+
'anthropic-xml'
|
|
463
|
+
);
|
|
464
|
+
|
|
423
465
|
return {
|
|
424
466
|
messages: providerMessages,
|
|
425
467
|
systemContent,
|
|
@@ -428,7 +470,7 @@ export class AnthropicXmlFormatter implements PrefillFormatter {
|
|
|
428
470
|
: undefined,
|
|
429
471
|
stopSequences,
|
|
430
472
|
nativeTools,
|
|
431
|
-
cacheMarkersApplied,
|
|
473
|
+
cacheMarkersApplied: budget.total,
|
|
432
474
|
};
|
|
433
475
|
}
|
|
434
476
|
|
|
@@ -43,6 +43,55 @@ import type { NormalizeEvent } from './types.js';
|
|
|
43
43
|
*/
|
|
44
44
|
export type ProviderBlock = Record<string, unknown> & { type: string };
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Does this repair REWRITE prefix bytes, or only re-shape envelopes?
|
|
48
|
+
*
|
|
49
|
+
* The two `true` entries are placeholders that get rewritten again the moment
|
|
50
|
+
* the real pairing arrives — the synthetic `[pending]` result when its
|
|
51
|
+
* tool_result lands, the textified orphan when its tool_use does — so a cache
|
|
52
|
+
* breakpoint placed at or past one caches a prefix that is about to change,
|
|
53
|
+
* poisoning every subsequent read.
|
|
54
|
+
*
|
|
55
|
+
* EVERY kind is listed, and the type is a `Record` over the whole union
|
|
56
|
+
* precisely so the compiler forces that: a new `NormalizeEvent` kind is a
|
|
57
|
+
* missing-property error here until someone decides which side it falls on.
|
|
58
|
+
* The previous shape (`[...] satisfies Array<NormalizeEvent['kind']>`) only
|
|
59
|
+
* checked that the listed kinds were REAL, and a `satisfies` on an array
|
|
60
|
+
* accepts any subset — so a new prefix-rewriting repair could be added to the
|
|
61
|
+
* union, never listed, and silently escape the cache gate below while the
|
|
62
|
+
* comment claimed a compile error that did not exist.
|
|
63
|
+
*
|
|
64
|
+
* NOTE FOR COMPOSE: the `stray_tool_result_textified` kind landing from the
|
|
65
|
+
* stray-orphan lane belongs here as `true` — it rewrites prefix bytes for the
|
|
66
|
+
* same reason `orphan_tool_result_textified` does.
|
|
67
|
+
*/
|
|
68
|
+
const NORMALIZE_EVENT_REWRITES_PREFIX: Record<NormalizeEvent['kind'], boolean> = {
|
|
69
|
+
block_re_roled: false,
|
|
70
|
+
tool_result_hoisted: false,
|
|
71
|
+
interloper_deferred: false,
|
|
72
|
+
synthetic_pending_result: true,
|
|
73
|
+
orphan_tool_result_textified: true,
|
|
74
|
+
pending_in_flight: false,
|
|
75
|
+
cache_suppressed_for_synthetic: false,
|
|
76
|
+
leading_user_synthesized: false,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function deriveKindsThatRewritePrefix(): ReadonlySet<NormalizeEvent['kind']> {
|
|
80
|
+
const kinds = Object.entries(NORMALIZE_EVENT_REWRITES_PREFIX)
|
|
81
|
+
.filter(([, rewritesPrefix]) => rewritesPrefix)
|
|
82
|
+
.map(([kind]) => kind as NormalizeEvent['kind']);
|
|
83
|
+
return new Set(kinds);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The repairs that rewrite prefix bytes, derived from the exhaustive
|
|
88
|
+
* classification above. Consumers gate cache placement on this SET rather
|
|
89
|
+
* than on a single kind, so a normalizer that grows another prefix-rewriting
|
|
90
|
+
* repair is covered at every cache site by classifying it once.
|
|
91
|
+
*/
|
|
92
|
+
export const PREFIX_REWRITING_NORMALIZE_EVENT_KINDS: ReadonlySet<NormalizeEvent['kind']> =
|
|
93
|
+
deriveKindsThatRewritePrefix();
|
|
94
|
+
|
|
46
95
|
export interface NormalizeOptions {
|
|
47
96
|
/** See `BuildOptions.pendingToolCallIds`. */
|
|
48
97
|
pendingToolCallIds?: ReadonlySet<string>;
|
package/src/formatters/types.ts
CHANGED
|
@@ -49,6 +49,16 @@ export interface BuildOptions {
|
|
|
49
49
|
/** Tool definitions to include */
|
|
50
50
|
tools?: ToolDefinition[];
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The tool mode Membrane RESOLVED for this request (see
|
|
54
|
+
* `Membrane.resolveToolMode`), which already accounts for `request.toolMode`,
|
|
55
|
+
* the formatter's own configured mode, and provider/formatter derivation.
|
|
56
|
+
* A formatter that supports both shapes must build for THIS mode; its
|
|
57
|
+
* constructor-time mode is only the fallback for direct `buildMessages`
|
|
58
|
+
* callers that resolve nothing.
|
|
59
|
+
*/
|
|
60
|
+
toolMode?: 'xml' | 'native';
|
|
61
|
+
|
|
52
62
|
/** Whether thinking is enabled */
|
|
53
63
|
thinking?: { enabled: boolean; budgetTokens?: number };
|
|
54
64
|
|
|
@@ -163,6 +173,18 @@ export interface BuildResult {
|
|
|
163
173
|
/** Number of cache control markers applied (for Anthropic prompt caching) */
|
|
164
174
|
cacheMarkersApplied?: number;
|
|
165
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Offset into the turn's accumulated assistant text at which the CURRENT
|
|
178
|
+
* last message of `messages` begins. Zero (or absent) for an ordinary
|
|
179
|
+
* build: the whole accumulated document is the trailing assistant prefill.
|
|
180
|
+
*
|
|
181
|
+
* A split-turn image injection persists its three messages here and moves
|
|
182
|
+
* this watermark to the image seam, so later continuations replace only
|
|
183
|
+
* the trailing assistant message and never re-flatten the pre-image text
|
|
184
|
+
* over the user turn that carries the image.
|
|
185
|
+
*/
|
|
186
|
+
accumulatedBaseOffset?: number;
|
|
187
|
+
|
|
166
188
|
/**
|
|
167
189
|
* `false` only when the tool-pair normalizer detected a trailing
|
|
168
190
|
* unmatched tool_use whose id is in `pendingToolCallIds` — i.e. the
|
|
@@ -258,6 +280,16 @@ export interface PrefillFormatter {
|
|
|
258
280
|
/** Whether this formatter uses prefill (vs native pass-through) */
|
|
259
281
|
readonly usesPrefill: boolean;
|
|
260
282
|
|
|
283
|
+
/**
|
|
284
|
+
* The tool mode this formatter instance was EXPLICITLY constructed with, if
|
|
285
|
+
* any. Read by `Membrane.resolveToolMode` as the fallback under an explicit
|
|
286
|
+
* `request.toolMode`: a formatter that can build either shape carries its
|
|
287
|
+
* caller's configured choice here so resolution honors it instead of
|
|
288
|
+
* re-deriving one from the formatter's name. Left undefined by formatters
|
|
289
|
+
* that build exactly one shape.
|
|
290
|
+
*/
|
|
291
|
+
readonly configuredToolMode?: 'xml' | 'native';
|
|
292
|
+
|
|
261
293
|
// ==========================================================================
|
|
262
294
|
// REQUEST BUILDING
|
|
263
295
|
// ==========================================================================
|