@vincemakes/kiso-provider-anthropic 0.13.0 → 0.15.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/dist/index.d.ts CHANGED
@@ -18,6 +18,8 @@ import type { Adapter } from "@vincemakes/kiso-core";
18
18
  export interface AnthropicProviderConfig {
19
19
  readonly apiKey?: string;
20
20
  readonly baseUrl?: string;
21
+ /** PH-1c.1: opt-in prompt caching (see AnthropicAdapterOptions). */
22
+ readonly promptCaching?: boolean;
21
23
  }
22
24
  /**
23
25
  * High-level factory (round 7): builds the adapter FROM CONFIG, owning the SDK
@@ -27,4 +29,14 @@ export interface AnthropicProviderConfig {
27
29
  * never through a hoisted root.
28
30
  */
29
31
  export declare function createAnthropicProvider(config?: AnthropicProviderConfig): Adapter;
30
- export declare function createAnthropicAdapter(client: Anthropic): Adapter;
32
+ /** PH-1c.1 (finding PH-F13): opt-in prompt caching. OFF by default —
33
+ * injecting cache_control changes the request bytes, and a cost
34
+ * behavior does not flip silently (the default flips only after the
35
+ * paired bench proves it at a release). ON places exactly two
36
+ * ephemeral breakpoints: the system block, and a ROLLING one on the
37
+ * last message's last content block — the next request's stable
38
+ * prefix reaches it, which is the whole trick. */
39
+ export interface AnthropicAdapterOptions {
40
+ readonly promptCaching?: boolean;
41
+ }
42
+ export declare function createAnthropicAdapter(client: Anthropic, adapterOpts?: AnthropicAdapterOptions): Adapter;
package/dist/index.js CHANGED
@@ -26,9 +26,10 @@ export function createAnthropicProvider(config = {}) {
26
26
  ...(config.apiKey !== undefined ? { apiKey: config.apiKey } : {}),
27
27
  ...(config.baseUrl !== undefined ? { baseURL: config.baseUrl } : {}),
28
28
  });
29
- return createAnthropicAdapter(client);
29
+ return createAnthropicAdapter(client, { ...(config.promptCaching !== undefined ? { promptCaching: config.promptCaching } : {}) });
30
30
  }
31
- export function createAnthropicAdapter(client) {
31
+ export function createAnthropicAdapter(client, adapterOpts = {}) {
32
+ const caching = adapterOpts.promptCaching === true;
32
33
  return {
33
34
  async *stream(options) {
34
35
  // D4: the stream CREATION is inside the error normalization —
@@ -38,9 +39,15 @@ export function createAnthropicAdapter(client) {
38
39
  try {
39
40
  stream = client.messages.stream({
40
41
  model: options.model,
41
- ...(options.systemPrompt !== undefined ? { system: options.systemPrompt } : {}),
42
+ ...(options.systemPrompt !== undefined
43
+ ? {
44
+ system: caching
45
+ ? [{ type: "text", text: options.systemPrompt, cache_control: { type: "ephemeral" } }]
46
+ : options.systemPrompt,
47
+ }
48
+ : {}),
42
49
  max_tokens: options.maxTokens ?? 4096,
43
- messages: toAnthropicMessages(options.messages),
50
+ messages: caching ? withCacheBreakpoint(toAnthropicMessages(options.messages)) : toAnthropicMessages(options.messages),
44
51
  ...(options.tools?.length ? { tools: toAnthropicTools(options.tools) } : {}),
45
52
  ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
46
53
  },
@@ -262,6 +269,22 @@ function toAnthropicMessages(messages) {
262
269
  }
263
270
  return out;
264
271
  }
272
+ /** PH-1c.1 — the rolling conversation breakpoint: the LAST message's
273
+ * LAST content block gains cache_control, so the next request's
274
+ * stable prefix (this whole conversation) is a cache hit. String
275
+ * content normalizes to one text block; an empty message is left
276
+ * alone. Pure — the input array is never mutated. */
277
+ function withCacheBreakpoint(messages) {
278
+ const last = messages[messages.length - 1];
279
+ if (last === undefined)
280
+ return messages;
281
+ const blocks = typeof last.content === "string" ? [{ type: "text", text: last.content }] : [...last.content];
282
+ const tail = blocks[blocks.length - 1];
283
+ if (tail === undefined)
284
+ return messages;
285
+ blocks[blocks.length - 1] = { ...tail, cache_control: { type: "ephemeral" } };
286
+ return [...messages.slice(0, -1), { ...last, content: blocks }];
287
+ }
265
288
  function toAnthropicBlock(block) {
266
289
  return block.type === "text"
267
290
  ? { type: "text", text: block.text }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-provider-anthropic",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "kiso Anthropic adapter — official SDK wire events mapped to the kiso event union.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@anthropic-ai/sdk": "^0.115.0",
25
- "@vincemakes/kiso-core": "0.13.0"
25
+ "@vincemakes/kiso-core": "0.15.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^26.1.2",