@juspay/neurolink 12.17.4 → 12.18.0
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/CHANGELOG.md +3 -3
- package/dist/browser/neurolink.min.js +392 -391
- package/dist/proxy/proxyContextPreflight.js +85 -23
- package/dist/proxy/proxyHistoryTruncation.d.ts +24 -0
- package/dist/proxy/proxyHistoryTruncation.js +171 -0
- package/dist/proxy/vertexAnthropicFallback.d.ts +82 -0
- package/dist/proxy/vertexAnthropicFallback.js +339 -0
- package/dist/server/routes/claudeProxyRoutes.js +39 -0
- package/dist/types/proxy.d.ts +22 -0
- package/dist/types/proxyContext.d.ts +19 -1
- package/docs-site/static/search-index.json +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRuntimeContextWindow, getRuntimeOutputCeiling, } from "../constants/contextWindows.js";
|
|
2
2
|
import { estimateTokens, IMAGE_TOKEN_ESTIMATE, } from "../utils/tokenEstimation.js";
|
|
3
|
+
import { truncateHistoryForBudget } from "./proxyHistoryTruncation.js";
|
|
3
4
|
/** Local refusal before spending a provider request; never silently truncates. */
|
|
4
5
|
export class ProxyContextPreflightError extends Error {
|
|
5
6
|
code;
|
|
@@ -65,13 +66,32 @@ export function parseProxyContextPolicy(raw) {
|
|
|
65
66
|
throw new ProxyContextPreflightError("invalid_context_policy", "models must be a bounded map of provider/model limits");
|
|
66
67
|
}
|
|
67
68
|
for (const [key, limit] of Object.entries(value.models)) {
|
|
69
|
+
// Read each budget into a local before comparing: narrowing from the
|
|
70
|
+
// `positive` predicate does not survive several `||` operands when it is
|
|
71
|
+
// applied to a property access, and the looser compiler flags used by the
|
|
72
|
+
// package build then see `unknown` on one side of `>=`.
|
|
73
|
+
const contextWindow = record(limit) ? limit.contextWindow : undefined;
|
|
74
|
+
const maxOutputTokens = record(limit) ? limit.maxOutputTokens : undefined;
|
|
75
|
+
const compactAt = record(limit) ? limit.compactAtTokens : undefined;
|
|
76
|
+
const compactTo = record(limit) ? limit.compactToTokens : undefined;
|
|
68
77
|
if (!key.includes("/") ||
|
|
69
78
|
!record(limit) ||
|
|
70
|
-
!positive(
|
|
71
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
!positive(contextWindow) ||
|
|
80
|
+
(maxOutputTokens !== undefined && !positive(maxOutputTokens)) ||
|
|
81
|
+
(compactAt !== undefined &&
|
|
82
|
+
(!positive(compactAt) || compactAt >= contextWindow)) ||
|
|
83
|
+
(compactTo !== undefined &&
|
|
84
|
+
(!positive(compactTo) ||
|
|
85
|
+
!positive(compactAt) ||
|
|
86
|
+
compactTo >= compactAt)) ||
|
|
87
|
+
(compactAt !== undefined && compactTo === undefined) ||
|
|
88
|
+
Object.keys(limit).some((k) => ![
|
|
89
|
+
"contextWindow",
|
|
90
|
+
"maxOutputTokens",
|
|
91
|
+
"compactAtTokens",
|
|
92
|
+
"compactToTokens",
|
|
93
|
+
].includes(k))) {
|
|
94
|
+
throw new ProxyContextPreflightError("invalid_context_policy", "Each model needs an explicit provider/model key, a positive contextWindow/output limit, and a compaction target below its trigger");
|
|
75
95
|
}
|
|
76
96
|
}
|
|
77
97
|
}
|
|
@@ -234,16 +254,21 @@ export function prepareProxyRequestContext(args) {
|
|
|
234
254
|
: {}),
|
|
235
255
|
};
|
|
236
256
|
}
|
|
237
|
-
|
|
238
|
-
|
|
257
|
+
let wire = body;
|
|
258
|
+
// Fixed cost and history are tracked separately: truncation can remove the
|
|
259
|
+
// only image in a request, and a sticky flag would then keep the ceiling
|
|
260
|
+
// check disabled for a request that no longer carries any media.
|
|
261
|
+
const fixedState = { multimodal: false };
|
|
262
|
+
let historyState = { multimodal: false };
|
|
263
|
+
const isMultimodal = () => fixedState.multimodal || historyState.multimodal;
|
|
239
264
|
const provider = args.provider === "codex" ? "openai" : args.provider;
|
|
240
|
-
const toolsTokensEstimate = estimateValue(wire.tools, provider,
|
|
265
|
+
const toolsTokensEstimate = estimateValue(wire.tools, provider, fixedState);
|
|
241
266
|
const instructionsTokensEstimate = [
|
|
242
267
|
wire.instructions,
|
|
243
268
|
wire.system,
|
|
244
269
|
wire.systemPrompt,
|
|
245
270
|
wire.systemInstruction,
|
|
246
|
-
].reduce((sum, value) => sum + estimateValue(value, provider,
|
|
271
|
+
].reduce((sum, value) => sum + estimateValue(value, provider, fixedState), 0);
|
|
247
272
|
const schemaTokensEstimate = [
|
|
248
273
|
wire.response_format,
|
|
249
274
|
wire.output_config,
|
|
@@ -256,19 +281,53 @@ export function prepareProxyRequestContext(args) {
|
|
|
256
281
|
record(wire.generationConfig)
|
|
257
282
|
? wire.generationConfig.responseJsonSchema
|
|
258
283
|
: undefined,
|
|
259
|
-
].reduce((sum, value) => sum + estimateValue(value, provider,
|
|
260
|
-
const
|
|
284
|
+
].reduce((sum, value) => sum + estimateValue(value, provider, fixedState), 0);
|
|
285
|
+
const estimate = (value) => estimateValue(value, provider, historyState);
|
|
286
|
+
const fixedTokensEstimate = 24 +
|
|
261
287
|
schemaTokensEstimate +
|
|
262
288
|
toolsTokensEstimate +
|
|
263
|
-
instructionsTokensEstimate
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
289
|
+
instructionsTokensEstimate;
|
|
290
|
+
const historyTokensEstimate = () => [
|
|
291
|
+
wire.messages,
|
|
292
|
+
wire.input,
|
|
293
|
+
wire.contents,
|
|
294
|
+
wire.conversationMessages,
|
|
295
|
+
wire.prompt,
|
|
296
|
+
].reduce((sum, value) => sum + estimate(value), 0);
|
|
297
|
+
let inputTokensEstimate = fixedTokensEstimate + historyTokensEstimate();
|
|
271
298
|
const configured = policy.models?.[`${args.provider}/${args.model}`];
|
|
299
|
+
// Cost control: reduce history before the refusal checks below, so those see
|
|
300
|
+
// the reduced estimate and a bounded request is dispatched instead of a
|
|
301
|
+
// rejected one. Runs on every dispatch path because every path lands here.
|
|
302
|
+
let historyModified = false;
|
|
303
|
+
let historyUnitsRemoved = 0;
|
|
304
|
+
const inputTokensBeforeTruncation = inputTokensEstimate;
|
|
305
|
+
if (configured?.compactAtTokens !== undefined &&
|
|
306
|
+
configured.compactToTokens !== undefined &&
|
|
307
|
+
inputTokensEstimate > configured.compactAtTokens) {
|
|
308
|
+
const truncated = truncateHistoryForBudget({
|
|
309
|
+
body,
|
|
310
|
+
inputTokensEstimate,
|
|
311
|
+
targetTokens: configured.compactToTokens,
|
|
312
|
+
estimate,
|
|
313
|
+
});
|
|
314
|
+
if (truncated.historyModified) {
|
|
315
|
+
body = truncated.body;
|
|
316
|
+
wire = body;
|
|
317
|
+
historyModified = true;
|
|
318
|
+
historyUnitsRemoved = truncated.unitsRemoved;
|
|
319
|
+
historyState = { multimodal: false };
|
|
320
|
+
inputTokensEstimate = fixedTokensEstimate + historyTokensEstimate();
|
|
321
|
+
}
|
|
322
|
+
// The design requires a typed local failure rather than a silent dispatch
|
|
323
|
+
// above the configured bound: fixed context alone, or the one unit that
|
|
324
|
+
// must be retained, can exceed the target with nothing left to remove.
|
|
325
|
+
if (inputTokensEstimate > configured.compactToTokens) {
|
|
326
|
+
throw new ProxyContextPreflightError("proxy_context_compaction_failed", `Compaction could not bring ${args.provider}/${args.model} within ` +
|
|
327
|
+
`${configured.compactToTokens} tokens; ${inputTokensEstimate} remain ` +
|
|
328
|
+
`after removing every removable unit`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
272
331
|
const discovered = getRuntimeContextWindow(args.provider, args.model);
|
|
273
332
|
const contextWindow = configured?.contextWindow ?? discovered;
|
|
274
333
|
const outputCeiling = configured?.maxOutputTokens ??
|
|
@@ -306,10 +365,13 @@ export function prepareProxyRequestContext(args) {
|
|
|
306
365
|
? "discovered"
|
|
307
366
|
: "unknown",
|
|
308
367
|
tokenCountSource: "estimated",
|
|
309
|
-
multimodalEstimate:
|
|
368
|
+
multimodalEstimate: isMultimodal(),
|
|
310
369
|
originalToolCount: toolCount(tools),
|
|
311
370
|
retainedToolCount: toolCount(wire.tools),
|
|
312
|
-
historyModified
|
|
371
|
+
historyModified,
|
|
372
|
+
...(historyModified
|
|
373
|
+
? { historyUnitsRemoved, inputTokensBeforeTruncation }
|
|
374
|
+
: {}),
|
|
313
375
|
};
|
|
314
376
|
if (policy.maxInputTokens !== undefined &&
|
|
315
377
|
inputTokensEstimate > policy.maxInputTokens) {
|
|
@@ -323,9 +385,9 @@ export function prepareProxyRequestContext(args) {
|
|
|
323
385
|
}
|
|
324
386
|
if (contextWindow !== undefined &&
|
|
325
387
|
policy.enforceDiscoveredLimits !== false &&
|
|
326
|
-
!
|
|
388
|
+
!isMultimodal() &&
|
|
327
389
|
inputTokensEstimate + outputTokensReserve > contextWindow) {
|
|
328
|
-
throw new ProxyContextPreflightError("proxy_context_window_exceeded",
|
|
390
|
+
throw new ProxyContextPreflightError("proxy_context_window_exceeded", `Estimated input plus output/reasoning reserve exceeds the serving model context window; ${historyModified ? "history was truncated and still does not fit" : "no history was truncated"}`, evidence);
|
|
329
391
|
}
|
|
330
392
|
return {
|
|
331
393
|
body,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic pre-dispatch history truncation.
|
|
3
|
+
*
|
|
4
|
+
* Runs inside the shared context preflight, so one implementation bounds input
|
|
5
|
+
* cost on every dispatch path: native Anthropic, the translated/SDK fallback
|
|
6
|
+
* that serves Vertex, and native or bridged Codex.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately provider-free. Summarizing history would add an outbound model
|
|
9
|
+
* call to the very requests this exists to make cheaper, so reduction is pure
|
|
10
|
+
* removal of the oldest complete units.
|
|
11
|
+
*/
|
|
12
|
+
import type { ProxyHistoryEstimate, ProxyHistoryTruncationResult } from "../types/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Remove the oldest complete units until the estimate reaches `targetTokens`.
|
|
15
|
+
*
|
|
16
|
+
* Returns the body unchanged when there is nothing safe to remove. The newest
|
|
17
|
+
* unit is always retained: it carries the turn being answered.
|
|
18
|
+
*/
|
|
19
|
+
export declare function truncateHistoryForBudget<T extends object>(args: {
|
|
20
|
+
body: T;
|
|
21
|
+
inputTokensEstimate: number;
|
|
22
|
+
targetTokens: number;
|
|
23
|
+
estimate: ProxyHistoryEstimate;
|
|
24
|
+
}): ProxyHistoryTruncationResult<T>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic pre-dispatch history truncation.
|
|
3
|
+
*
|
|
4
|
+
* Runs inside the shared context preflight, so one implementation bounds input
|
|
5
|
+
* cost on every dispatch path: native Anthropic, the translated/SDK fallback
|
|
6
|
+
* that serves Vertex, and native or bridged Codex.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately provider-free. Summarizing history would add an outbound model
|
|
9
|
+
* call to the very requests this exists to make cheaper, so reduction is pure
|
|
10
|
+
* removal of the oldest complete units.
|
|
11
|
+
*/
|
|
12
|
+
function record(value) {
|
|
13
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A turn that cannot legally open a history.
|
|
17
|
+
*
|
|
18
|
+
* The Messages API rejects a conversation whose first message is not from the
|
|
19
|
+
* user, so truncation that stops on an assistant turn would trade a local
|
|
20
|
+
* refusal for an upstream 400. Items with no role (Codex `function_call` and
|
|
21
|
+
* `function_call_output`) are not turns and are left alone.
|
|
22
|
+
*/
|
|
23
|
+
function isNonUserTurn(item) {
|
|
24
|
+
return record(item) && typeof item.role === "string" && item.role !== "user";
|
|
25
|
+
}
|
|
26
|
+
function blocksOf(item) {
|
|
27
|
+
return Array.isArray(item.content) ? item.content : [item];
|
|
28
|
+
}
|
|
29
|
+
/** Tool-call ids this item opens. */
|
|
30
|
+
function definedCallIds(item) {
|
|
31
|
+
if (!record(item)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const ids = [];
|
|
35
|
+
if (item.type === "function_call" && typeof item.call_id === "string") {
|
|
36
|
+
ids.push(item.call_id);
|
|
37
|
+
}
|
|
38
|
+
for (const block of blocksOf(item)) {
|
|
39
|
+
if (record(block) &&
|
|
40
|
+
block.type === "tool_use" &&
|
|
41
|
+
typeof block.id === "string") {
|
|
42
|
+
ids.push(block.id);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return ids;
|
|
46
|
+
}
|
|
47
|
+
/** Tool-call ids this item answers. */
|
|
48
|
+
function referencedCallIds(item) {
|
|
49
|
+
if (!record(item)) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const ids = [];
|
|
53
|
+
if (item.type === "function_call_output" &&
|
|
54
|
+
typeof item.call_id === "string") {
|
|
55
|
+
ids.push(item.call_id);
|
|
56
|
+
}
|
|
57
|
+
for (const block of blocksOf(item)) {
|
|
58
|
+
if (record(block) &&
|
|
59
|
+
block.type === "tool_result" &&
|
|
60
|
+
typeof block.tool_use_id === "string") {
|
|
61
|
+
ids.push(block.tool_use_id);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return ids;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Group items so a tool call and every answer to it stay in one unit.
|
|
68
|
+
*
|
|
69
|
+
* A unit stays open while any call it opened is still unanswered, so removing
|
|
70
|
+
* whole units can never strand a `tool_result` or a `function_call_output`.
|
|
71
|
+
*/
|
|
72
|
+
function groupIntoUnits(items) {
|
|
73
|
+
const units = [];
|
|
74
|
+
let current = [];
|
|
75
|
+
const awaiting = new Set();
|
|
76
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
77
|
+
const item = items[index];
|
|
78
|
+
current.push(index);
|
|
79
|
+
for (const id of referencedCallIds(item)) {
|
|
80
|
+
awaiting.delete(id);
|
|
81
|
+
}
|
|
82
|
+
for (const id of definedCallIds(item)) {
|
|
83
|
+
awaiting.add(id);
|
|
84
|
+
}
|
|
85
|
+
if (awaiting.size === 0) {
|
|
86
|
+
units.push(current);
|
|
87
|
+
current = [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (current.length > 0) {
|
|
91
|
+
// An unanswered trailing call still belongs with the turn that opened it.
|
|
92
|
+
units.push(current);
|
|
93
|
+
}
|
|
94
|
+
return units;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Pick the history container.
|
|
98
|
+
*
|
|
99
|
+
* `input` is overloaded across paths: the translated/SDK shape carries the
|
|
100
|
+
* current turn there as an object, while Codex carries the item array. Only an
|
|
101
|
+
* array is history, so the object form is never touched.
|
|
102
|
+
*/
|
|
103
|
+
function historyFieldOf(wire) {
|
|
104
|
+
if (Array.isArray(wire.messages)) {
|
|
105
|
+
return "messages";
|
|
106
|
+
}
|
|
107
|
+
if (Array.isArray(wire.conversationMessages)) {
|
|
108
|
+
return "conversationMessages";
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(wire.input)) {
|
|
111
|
+
return "input";
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Remove the oldest complete units until the estimate reaches `targetTokens`.
|
|
117
|
+
*
|
|
118
|
+
* Returns the body unchanged when there is nothing safe to remove. The newest
|
|
119
|
+
* unit is always retained: it carries the turn being answered.
|
|
120
|
+
*/
|
|
121
|
+
export function truncateHistoryForBudget(args) {
|
|
122
|
+
const unchanged = {
|
|
123
|
+
body: args.body,
|
|
124
|
+
historyModified: false,
|
|
125
|
+
unitsRemoved: 0,
|
|
126
|
+
itemsRemoved: 0,
|
|
127
|
+
};
|
|
128
|
+
const wire = args.body;
|
|
129
|
+
const field = historyFieldOf(wire);
|
|
130
|
+
if (field === undefined) {
|
|
131
|
+
return unchanged;
|
|
132
|
+
}
|
|
133
|
+
const items = wire[field];
|
|
134
|
+
const units = groupIntoUnits(items);
|
|
135
|
+
if (units.length <= 1) {
|
|
136
|
+
return unchanged;
|
|
137
|
+
}
|
|
138
|
+
// Everything outside the history container — tools, instructions, schema —
|
|
139
|
+
// is fixed cost that truncation must never touch, so only unit costs are
|
|
140
|
+
// subtracted from the running projection.
|
|
141
|
+
const unitTokens = units.map((unit) => args.estimate(unit.map((index) => items[index])));
|
|
142
|
+
// The translated shape keeps the current turn in object-valued `input`, so
|
|
143
|
+
// every `conversationMessages` unit is prior history and all of them may go.
|
|
144
|
+
// The other shapes carry the current turn in the array itself, so the newest
|
|
145
|
+
// unit there is the turn being answered and must survive.
|
|
146
|
+
const removable = field === "conversationMessages" ? units.length : units.length - 1;
|
|
147
|
+
let removedUnits = 0;
|
|
148
|
+
let projected = args.inputTokensEstimate;
|
|
149
|
+
while (removedUnits < removable && projected > args.targetTokens) {
|
|
150
|
+
projected -= unitTokens[removedUnits];
|
|
151
|
+
removedUnits += 1;
|
|
152
|
+
}
|
|
153
|
+
if (removedUnits === 0) {
|
|
154
|
+
return unchanged;
|
|
155
|
+
}
|
|
156
|
+
// Advance by whole units so the kept history opens on a user turn without
|
|
157
|
+
// stranding a tool call from its answer.
|
|
158
|
+
while (removedUnits < removable &&
|
|
159
|
+
isNonUserTurn(items[units[removedUnits][0]])) {
|
|
160
|
+
removedUnits += 1;
|
|
161
|
+
}
|
|
162
|
+
const keptIndices = units.slice(removedUnits).flat();
|
|
163
|
+
const kept = keptIndices.map((index) => items[index]);
|
|
164
|
+
const itemsRemoved = items.length - kept.length;
|
|
165
|
+
return {
|
|
166
|
+
body: { ...args.body, [field]: kept },
|
|
167
|
+
historyModified: true,
|
|
168
|
+
unitsRemoved: removedUnits,
|
|
169
|
+
itemsRemoved,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Claude-to-Vertex passthrough.
|
|
3
|
+
*
|
|
4
|
+
* Claude on Vertex speaks the Anthropic Messages API, so a fallback to it needs
|
|
5
|
+
* no translation at all — the client's body goes upstream essentially verbatim
|
|
6
|
+
* and the SSE comes back in the shape the client already expects.
|
|
7
|
+
*
|
|
8
|
+
* This exists because the SDK/translation path cannot carry an agentic turn:
|
|
9
|
+
* it runs its own tool loop, and a request whose tools have no executor (every
|
|
10
|
+
* proxied request, since the caller executes them) ends with the pending tool
|
|
11
|
+
* call discarded and a step-cap apology returned as assistant text. Tools,
|
|
12
|
+
* `tool_use`, `tool_result`, `cache_control` and thinking blocks all survive
|
|
13
|
+
* here because nothing reshapes them.
|
|
14
|
+
*/
|
|
15
|
+
import type { UsageContext, VertexPassthroughTerminal, VertexAnthropicPassthroughRequest, VertexAnthropicPassthroughTarget } from "../types/index.js";
|
|
16
|
+
/**
|
|
17
|
+
* Translate `output_config` for Vertex.
|
|
18
|
+
*
|
|
19
|
+
* An effort level Vertex knows passes through; one it does not is mapped onto
|
|
20
|
+
* the nearest level it does. A level with no mapping at all loses only the
|
|
21
|
+
* `effort` key, never the sibling settings beside it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function mapVertexOutputConfig(value: unknown): Record<string, unknown> | undefined;
|
|
24
|
+
/** Strip what Vertex rejects and pin the API version it requires. */
|
|
25
|
+
export declare function buildVertexAnthropicPayload(body: Readonly<Record<string, unknown>>): Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Publisher endpoint for a Claude model on Vertex.
|
|
28
|
+
*
|
|
29
|
+
* Streaming and non-streaming are different verbs on the same resource, so the
|
|
30
|
+
* caller's `stream` flag selects the verb rather than changing the body.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildVertexAnthropicUrl(target: VertexAnthropicPassthroughTarget): string;
|
|
33
|
+
/**
|
|
34
|
+
* Send one Anthropic-shaped request to Vertex and return the upstream response
|
|
35
|
+
* untouched, so the caller can stream its bytes straight to the client.
|
|
36
|
+
*/
|
|
37
|
+
export declare function dispatchVertexAnthropicPassthrough(request: VertexAnthropicPassthroughRequest): Promise<Response>;
|
|
38
|
+
/** Claude models are the only publisher models this passthrough addresses. */
|
|
39
|
+
export declare function isVertexAnthropicModel(model: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve where Vertex traffic is addressed, using the same environment the
|
|
42
|
+
* Vertex provider reads so a fallback lands on the project and region the rest
|
|
43
|
+
* of the process already uses.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveVertexTarget(): {
|
|
46
|
+
projectId: string;
|
|
47
|
+
location: string;
|
|
48
|
+
} | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Run one Claude-on-Vertex fallback attempt and hand back a response the route
|
|
51
|
+
* can serve directly.
|
|
52
|
+
*
|
|
53
|
+
* A non-2xx upstream throws so the caller's attempt loop records the failure
|
|
54
|
+
* and moves to the next hop, exactly as the other providers behave.
|
|
55
|
+
*/
|
|
56
|
+
export declare function executeVertexAnthropicFallback(args: {
|
|
57
|
+
body: Readonly<Record<string, unknown>>;
|
|
58
|
+
model: string;
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
/** Fires exactly once on every exit, so the hop is finalized like any other. */
|
|
61
|
+
onTerminal?: (terminal: VertexPassthroughTerminal) => void;
|
|
62
|
+
}): Promise<Response>;
|
|
63
|
+
/**
|
|
64
|
+
* Read usage out of a non-streaming Anthropic reply.
|
|
65
|
+
*
|
|
66
|
+
* `rawPredict` returns a single `{"type":"message","usage":{...}}` object, which
|
|
67
|
+
* carries the whole turn's counts at once rather than splitting them across
|
|
68
|
+
* `message_start` and `message_delta`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function readJsonUsage(text: string): UsageContext;
|
|
71
|
+
/**
|
|
72
|
+
* Forward a stream unchanged while observing its usage events.
|
|
73
|
+
*
|
|
74
|
+
* The bytes are passed through untouched — parsing happens on a copy of each
|
|
75
|
+
* chunk — so the client sees exactly what Vertex sent.
|
|
76
|
+
*
|
|
77
|
+
* A transform's `flush` only runs on a clean end, which would leave a stream
|
|
78
|
+
* error or a client disconnect with no terminal record at all. An explicit
|
|
79
|
+
* reader loop is used instead, so `onTerminal` fires exactly once on every
|
|
80
|
+
* exit: normal end, upstream failure, and cancellation.
|
|
81
|
+
*/
|
|
82
|
+
export declare function observeVertexUsage(body: ReadableStream<Uint8Array>, onTerminal: (terminal: VertexPassthroughTerminal) => void): ReadableStream<Uint8Array>;
|