@juspay/neurolink 9.79.0 → 9.79.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/CHANGELOG.md +6 -0
- package/dist/browser/neurolink.min.js +314 -314
- package/dist/lib/providers/googleVertex.js +81 -3
- package/dist/lib/types/providers.d.ts +46 -0
- package/dist/lib/utils/anthropicCacheBreakpoints.d.ts +15 -0
- package/dist/lib/utils/anthropicCacheBreakpoints.js +98 -0
- package/dist/providers/googleVertex.js +81 -3
- package/dist/types/providers.d.ts +46 -0
- package/dist/utils/anthropicCacheBreakpoints.d.ts +15 -0
- package/dist/utils/anthropicCacheBreakpoints.js +97 -0
- package/package.json +2 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic prompt-cache breakpoint placement for the native Vertex+Claude
|
|
3
|
+
* request path.
|
|
4
|
+
*
|
|
5
|
+
* Vertex does NOT support automatic prompt caching — the only way the
|
|
6
|
+
* conversation prefix gets cached across turns is explicit `cache_control`
|
|
7
|
+
* markers in the request. Anthropic renders a request as `tools → system →
|
|
8
|
+
* messages` and caches the prefix up to each marker, with a hard ceiling of
|
|
9
|
+
* four markers.
|
|
10
|
+
*
|
|
11
|
+
* Without a marker on the message history, the entire (growing, often
|
|
12
|
+
* tool-result-heavy) conversation falls *after* the last breakpoint and is
|
|
13
|
+
* re-billed at full input price on every turn. That is the regression this
|
|
14
|
+
* fixes: it gives the history a rolling breakpoint so the stable prefix is
|
|
15
|
+
* cached at ~0.1x and only the newest turn is fresh.
|
|
16
|
+
*/
|
|
17
|
+
const EPHEMERAL = { type: "ephemeral" };
|
|
18
|
+
/** Anthropic allows at most four `cache_control` breakpoints per request. */
|
|
19
|
+
const MAX_BREAKPOINTS = 4;
|
|
20
|
+
/**
|
|
21
|
+
* Annotate a native Vertex+Claude request with prompt-cache breakpoints.
|
|
22
|
+
*
|
|
23
|
+
* Budget allocation (max 4 markers):
|
|
24
|
+
* 1. The stable prefix — the last system block when a system prompt is
|
|
25
|
+
* present (this single marker caches `tools + system`, since system
|
|
26
|
+
* renders after tools); otherwise the last tool definition.
|
|
27
|
+
* 2-4. A rolling breakpoint on the last few messages, so the
|
|
28
|
+
* growing-but-now-stable conversation history is cached and only the
|
|
29
|
+
* newest turn is billed as fresh input.
|
|
30
|
+
*
|
|
31
|
+
* Pure: the inputs are cloned, never mutated.
|
|
32
|
+
*/
|
|
33
|
+
export function applyVertexAnthropicCacheBreakpoints(input) {
|
|
34
|
+
let budget = MAX_BREAKPOINTS;
|
|
35
|
+
// 1. Stable prefix. A bare string `system` cannot carry cache_control, so
|
|
36
|
+
// convert it to block form. Marking the last system block caches the whole
|
|
37
|
+
// tools + system prefix; only fall back to marking the last tool when there
|
|
38
|
+
// is no system prompt to mark.
|
|
39
|
+
let system = input.system;
|
|
40
|
+
let tools = input.tools;
|
|
41
|
+
const hasSystem = !!input.system && input.system.trim().length > 0;
|
|
42
|
+
if (hasSystem) {
|
|
43
|
+
system = [
|
|
44
|
+
{ type: "text", text: input.system, cache_control: EPHEMERAL },
|
|
45
|
+
];
|
|
46
|
+
budget--;
|
|
47
|
+
}
|
|
48
|
+
else if (input.tools && input.tools.length > 0) {
|
|
49
|
+
const lastIndex = input.tools.length - 1;
|
|
50
|
+
tools = input.tools.map((tool, i) => i === lastIndex ? { ...tool, cache_control: EPHEMERAL } : tool);
|
|
51
|
+
budget--;
|
|
52
|
+
}
|
|
53
|
+
// 2. Rolling history breakpoints over the tail of the conversation.
|
|
54
|
+
const messages = input.messages.map((m) => ({ ...m }));
|
|
55
|
+
let remaining = Math.min(budget, input.maxHistoryBreakpoints ?? MAX_BREAKPOINTS);
|
|
56
|
+
for (let i = messages.length - 1; i >= 0 && remaining > 0; i--) {
|
|
57
|
+
if (markLastContentBlock(messages, i)) {
|
|
58
|
+
remaining--;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { system, tools, messages };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Place a cache breakpoint on the last content block of `messages[i]`.
|
|
65
|
+
* Anthropic attaches `cache_control` to a content block, not the message
|
|
66
|
+
* envelope, so a string content body is first converted to block form.
|
|
67
|
+
* Returns false when the message has no markable block (caller then walks to
|
|
68
|
+
* an earlier message), so an empty turn never silently consumes a breakpoint.
|
|
69
|
+
*/
|
|
70
|
+
function markLastContentBlock(messages, i) {
|
|
71
|
+
const message = messages[i];
|
|
72
|
+
if (typeof message.content === "string") {
|
|
73
|
+
if (message.content.length === 0) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
messages[i] = {
|
|
77
|
+
...message,
|
|
78
|
+
content: [
|
|
79
|
+
{ type: "text", text: message.content, cache_control: EPHEMERAL },
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(message.content) || message.content.length === 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// Shallow clone is sufficient: we only ever add a top-level `cache_control`
|
|
88
|
+
// field to the last block and never mutate nested members (e.g. an image's
|
|
89
|
+
// `source`). Those nested objects stay shared with the input by reference,
|
|
90
|
+
// which is safe because they are never written to. If a future block shape
|
|
91
|
+
// requires mutating nested members, deep-clone that block instead.
|
|
92
|
+
const content = message.content.map((block) => ({ ...block }));
|
|
93
|
+
const lastIndex = content.length - 1;
|
|
94
|
+
content[lastIndex] = { ...content[lastIndex], cache_control: EPHEMERAL };
|
|
95
|
+
messages[i] = { ...message, content };
|
|
96
|
+
return true;
|
|
97
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.79.
|
|
3
|
+
"version": "9.79.1",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
|
|
6
6
|
"author": {
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"test": "npx tsx test/continuous-test-suite.ts",
|
|
71
71
|
"test:client": "npx tsx test/continuous-test-suite-client.ts",
|
|
72
72
|
"test:context": "npx tsx test/continuous-test-suite-context.ts",
|
|
73
|
+
"test:cache": "npx tsx test/continuous-test-suite-cache-breakpoints.ts",
|
|
73
74
|
"test:evaluation": "npx tsx test/continuous-test-suite-evaluation.ts",
|
|
74
75
|
"test:mcp": "npx tsx test/continuous-test-suite-mcp-infra.ts",
|
|
75
76
|
"test:mcp:http": "npx tsx test/continuous-test-suite-mcp-http.ts",
|