@jaypie/llm 1.3.13 → 1.3.15

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.
Files changed (34) hide show
  1. package/dist/cjs/Llm.d.ts +6 -0
  2. package/dist/cjs/index.cjs +813 -344
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/index.d.cts +112 -1
  5. package/dist/cjs/index.d.ts +1 -1
  6. package/dist/cjs/observability/exchangeStore.d.ts +20 -0
  7. package/dist/cjs/operate/adapters/BedrockAdapter.d.ts +6 -0
  8. package/dist/cjs/operate/adapters/OpenAiAdapter.d.ts +5 -0
  9. package/dist/cjs/operate/adapters/OpenRouterAdapter.d.ts +11 -0
  10. package/dist/cjs/operate/exchange/buildExchangeEnvelope.d.ts +16 -0
  11. package/dist/cjs/operate/exchange/emitExchange.d.ts +20 -0
  12. package/dist/cjs/operate/exchange/index.d.ts +2 -0
  13. package/dist/cjs/operate/types.d.ts +10 -1
  14. package/dist/cjs/providers/anthropic/types.d.ts +14 -1
  15. package/dist/cjs/types/LlmProvider.interface.d.ts +106 -1
  16. package/dist/cjs/util/cacheControl.d.ts +21 -0
  17. package/dist/cjs/util/index.d.ts +1 -0
  18. package/dist/esm/Llm.d.ts +6 -0
  19. package/dist/esm/index.d.ts +112 -1
  20. package/dist/esm/index.js +813 -344
  21. package/dist/esm/index.js.map +1 -1
  22. package/dist/esm/observability/exchangeStore.d.ts +20 -0
  23. package/dist/esm/operate/adapters/BedrockAdapter.d.ts +6 -0
  24. package/dist/esm/operate/adapters/OpenAiAdapter.d.ts +5 -0
  25. package/dist/esm/operate/adapters/OpenRouterAdapter.d.ts +11 -0
  26. package/dist/esm/operate/exchange/buildExchangeEnvelope.d.ts +16 -0
  27. package/dist/esm/operate/exchange/emitExchange.d.ts +20 -0
  28. package/dist/esm/operate/exchange/index.d.ts +2 -0
  29. package/dist/esm/operate/types.d.ts +10 -1
  30. package/dist/esm/providers/anthropic/types.d.ts +14 -1
  31. package/dist/esm/types/LlmProvider.interface.d.ts +106 -1
  32. package/dist/esm/util/cacheControl.d.ts +21 -0
  33. package/dist/esm/util/index.d.ts +1 -0
  34. package/package.json +5 -1
@@ -525,7 +525,97 @@ interface LlmProgressEvent {
525
525
  usage?: LlmUsage;
526
526
  }
527
527
  type LlmProgressCallback = (event: LlmProgressEvent) => unknown | Promise<unknown>;
528
+ /**
529
+ * Serializable snapshot of the request side of one operate() call.
530
+ * `input` is the raw, pre-interpolation input with multimodal parts preserved.
531
+ */
532
+ interface LlmExchangeRequest {
533
+ cache?: LlmCache;
534
+ data?: NaturalMap;
535
+ effort?: LlmEffort;
536
+ explain?: boolean;
537
+ /** Format normalized to JSON Schema (Zod/Natural already rendered) */
538
+ format?: JsonObject;
539
+ input: string | LlmHistory | LlmInputMessage | LlmOperateInput;
540
+ instructions?: string;
541
+ /** Requested model (pre-fallback) */
542
+ model?: string;
543
+ placeholders?: {
544
+ input?: boolean;
545
+ instructions?: boolean;
546
+ system?: boolean;
547
+ };
548
+ providerOptions?: JsonObject;
549
+ system?: string;
550
+ temperature?: number;
551
+ /** Names of tools offered to the model */
552
+ tools?: string[];
553
+ turns?: boolean | number;
554
+ user?: string;
555
+ }
556
+ /**
557
+ * Serializable snapshot of the response side of one operate() call.
558
+ */
559
+ interface LlmExchangeResponse {
560
+ content?: string | JsonObject;
561
+ error?: LlmError$1;
562
+ /** Turns this call added to history (tool calls/results included); never the resent history */
563
+ historyDelta: LlmHistory;
564
+ reasoning?: string[];
565
+ status: LlmResponseStatus;
566
+ stopReason?: string;
567
+ /** Per-model-call usage items (provider/model on each item) */
568
+ usage: LlmUsage;
569
+ /** Cumulative usage keyed `provider:model` */
570
+ usageTotals?: Record<string, LlmUsageItem>;
571
+ }
572
+ /**
573
+ * How the call was actually served after fallback and retry resolution.
574
+ */
575
+ interface LlmExchangeResolution {
576
+ fallbackAttempts?: number;
577
+ fallbackUsed?: boolean;
578
+ /** Served model */
579
+ model?: string;
580
+ /** Served provider */
581
+ provider?: string;
582
+ /** Model-request retries within the served attempt */
583
+ retries?: number;
584
+ }
585
+ interface LlmExchangeTiming {
586
+ /** Milliseconds from start to settlement */
587
+ duration: number;
588
+ /** ISO 8601 */
589
+ startedAt: string;
590
+ }
591
+ /**
592
+ * One serializable request/response envelope per operate() settlement
593
+ * (success or failure). Contains no functions.
594
+ */
595
+ interface LlmExchangeEnvelope {
596
+ /** Provider response id(s) per model turn, when available */
597
+ ids?: string[];
598
+ request: LlmExchangeRequest;
599
+ resolution: LlmExchangeResolution;
600
+ response: LlmExchangeResponse;
601
+ timing: LlmExchangeTiming;
602
+ }
603
+ type LlmExchangeCallback = (envelope: LlmExchangeEnvelope) => unknown | Promise<unknown>;
604
+ /**
605
+ * Prompt-caching control for operate()/stream().
606
+ * - `true` / omitted → caching enabled at the default `"5m"` TTL
607
+ * - `false` / `0` → caching disabled
608
+ * - `"5m"` / `"1h"` → enabled at that TTL (TTL honored by Anthropic/OpenRouter;
609
+ * other providers ignore it and cache with their own defaults)
610
+ */
611
+ type LlmCache = boolean | 0 | "5m" | "1h";
528
612
  interface LlmOperateOptions {
613
+ /**
614
+ * Prompt caching for the stable request prefix (system prompt + tools).
615
+ * Enabled by default; pass `false`/`0` to opt out, or `"5m"`/`"1h"` to set
616
+ * the TTL. See {@link LlmCache}.
617
+ */
618
+ cache?: LlmCache;
529
619
  data?: NaturalMap;
530
620
  /**
531
621
  * Provider-neutral reasoning effort (lowest | low | medium | high | highest).
@@ -589,6 +679,12 @@ interface LlmOperateOptions {
589
679
  };
590
680
  instructions?: string;
591
681
  model?: string;
682
+ /**
683
+ * Fires once per operate() settlement (success or failure) with a fully
684
+ * serializable request/response envelope. Errors thrown by the callback
685
+ * are logged and never interrupt the call.
686
+ */
687
+ onExchange?: LlmExchangeCallback;
592
688
  /**
593
689
  * Receives lightweight progress events as the operate loop runs:
594
690
  * start, model_request, model_response, tool_call, tool_result,
@@ -628,6 +724,10 @@ interface LlmUsageItem {
628
724
  output: number;
629
725
  reasoning: number;
630
726
  total: number;
727
+ /** Prompt-cache tokens served from cache this call (billed at ~0.1x input) */
728
+ cacheRead?: number;
729
+ /** Prompt-cache tokens written to cache this call (billed at ~1.25x input) */
730
+ cacheWrite?: number;
631
731
  provider?: string;
632
732
  model?: string;
633
733
  }
@@ -635,6 +735,11 @@ type LlmUsage = LlmUsageItem[];
635
735
  interface LlmOperateResponse {
636
736
  content?: string | JsonObject;
637
737
  error?: LlmError$1;
738
+ /**
739
+ * Serializable exchange envelope for this call. Present only when
740
+ * `onExchange` was passed or exchange persistence is enabled.
741
+ */
742
+ exchange?: LlmExchangeEnvelope;
638
743
  /** Number of providers attempted (1 = primary only, >1 = fallback(s) used) */
639
744
  fallbackAttempts?: number;
640
745
  /** Whether a fallback provider was used instead of the primary */
@@ -680,6 +785,12 @@ declare class Llm implements LlmProvider {
680
785
  operate(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: Omit<LlmOperateOptions, "model"> & {
681
786
  model?: LlmModelOption;
682
787
  }): Promise<LlmOperateResponse>;
788
+ /**
789
+ * Stamp fallback resolution onto the envelope the operate loop attached to
790
+ * the response and deliver it to the caller's onExchange. Fires once per
791
+ * operate() settlement; callback errors are logged and never thrown.
792
+ */
793
+ private settleExchange;
683
794
  stream(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: Omit<LlmOperateOptions, "model"> & {
684
795
  model?: LlmModelOption;
685
796
  }): AsyncIterable<LlmStreamChunk>;
@@ -931,4 +1042,4 @@ declare class XaiProvider implements LlmProvider {
931
1042
  }
932
1043
 
933
1044
  export { BedrockProvider, ErrorCategory, FireworksProvider, GoogleProvider as GeminiProvider, GoogleProvider, JaypieToolkit, constants as LLM, Llm, LlmError, LlmMessageRole, LlmMessageType, LlmProgressEventType, LlmQuotaError, LlmRateLimitError, LlmStreamChunkType, LlmTransientError, LlmUnrecoverableError, OpenRouterProvider, Toolkit, XaiProvider, extractReasoning, isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, jsonSchemaToNaturalSchema, naturalSchemaToJsonSchema, toolkit, tools };
934
- export type { LlmEffort, LlmErrorOptions, LlmFallbackConfig, LlmHistory, LlmInputContent, LlmInputContentFile, LlmInputContentImage, LlmInputContentText, LlmInputMessage, LlmMessageOptions, LlmModelOption, LlmOperateInput, LlmOperateInputContent, LlmOperateInputFile, LlmOperateInputImage, LlmOperateOptions, LlmOperateResponse, LlmOptions, LlmProgressCallback, LlmProgressEvent, LlmProgressToolCall, LlmProvider, LlmStreamChunk, LlmStreamChunkDone, LlmStreamChunkError, LlmStreamChunkText, LlmStreamChunkToolCall, LlmStreamChunkToolResult, LlmTool };
1045
+ export type { LlmCache, LlmEffort, LlmErrorOptions, LlmExchangeCallback, LlmExchangeEnvelope, LlmExchangeRequest, LlmExchangeResolution, LlmExchangeResponse, LlmExchangeTiming, LlmFallbackConfig, LlmHistory, LlmInputContent, LlmInputContentFile, LlmInputContentImage, LlmInputContentText, LlmInputMessage, LlmMessageOptions, LlmModelOption, LlmOperateInput, LlmOperateInputContent, LlmOperateInputFile, LlmOperateInputImage, LlmOperateOptions, LlmOperateResponse, LlmOptions, LlmProgressCallback, LlmProgressEvent, LlmProgressToolCall, LlmProvider, LlmStreamChunk, LlmStreamChunkDone, LlmStreamChunkError, LlmStreamChunkText, LlmStreamChunkToolCall, LlmStreamChunkToolResult, LlmTool };
@@ -1,7 +1,7 @@
1
1
  export { default as Llm } from "./Llm.js";
2
2
  export * as LLM from "./constants.js";
3
3
  export type { LlmEffort } from "./constants.js";
4
- export type { LlmFallbackConfig, LlmHistory, LlmInputContent, LlmInputContentFile, LlmInputContentImage, LlmInputContentText, LlmInputMessage, LlmMessageOptions, LlmModelOption, LlmOperateInput, LlmOperateInputContent, LlmOperateInputFile, LlmOperateInputImage, LlmOperateOptions, LlmOperateResponse, LlmOptions, LlmProgressCallback, LlmProgressEvent, LlmProgressToolCall, LlmProvider, } from "./types/LlmProvider.interface.js";
4
+ export type { LlmCache, LlmExchangeCallback, LlmExchangeEnvelope, LlmExchangeRequest, LlmExchangeResolution, LlmExchangeResponse, LlmExchangeTiming, LlmFallbackConfig, LlmHistory, LlmInputContent, LlmInputContentFile, LlmInputContentImage, LlmInputContentText, LlmInputMessage, LlmMessageOptions, LlmModelOption, LlmOperateInput, LlmOperateInputContent, LlmOperateInputFile, LlmOperateInputImage, LlmOperateOptions, LlmOperateResponse, LlmOptions, LlmProgressCallback, LlmProgressEvent, LlmProgressToolCall, LlmProvider, } from "./types/LlmProvider.interface.js";
5
5
  export { LlmMessageRole, LlmMessageType, LlmProgressEventType, } from "./types/LlmProvider.interface.js";
6
6
  export { isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, } from "./types/LlmOperateInput.guards.js";
7
7
  export type { LlmTool } from "./types/LlmTool.interface.js";
@@ -0,0 +1,20 @@
1
+ import { LlmExchangeEnvelope } from "../types/LlmProvider.interface.js";
2
+ /** Minimal shape of the @jaypie/dynamodb surface this module uses. */
3
+ interface ExchangeStoreSdk {
4
+ storeExchange: (envelope: LlmExchangeEnvelope) => Promise<unknown>;
5
+ }
6
+ /** Reset the cached resolution. Exposed for tests. */
7
+ export declare function _resetExchangeStore(): void;
8
+ /**
9
+ * Inject a store to bypass @jaypie/dynamodb resolution. Test-only: the peer
10
+ * is optional, so the enabled path cannot otherwise be exercised in unit
11
+ * tests without it installed.
12
+ */
13
+ export declare function _setExchangeStore(sdk: ExchangeStoreSdk | null): void;
14
+ /**
15
+ * Persist an exchange envelope via @jaypie/dynamodb when
16
+ * LLM_EXCHANGE_ENABLED is set. Silent no-op when the flag is unset or the
17
+ * peer is absent; persister failures are logged and never thrown.
18
+ */
19
+ export declare function persistExchange(envelope: LlmExchangeEnvelope): Promise<void>;
20
+ export {};
@@ -52,11 +52,17 @@ export declare class BedrockAdapter extends BaseProviderAdapter {
52
52
  readonly defaultModel: "amazon.nova-pro-v1:0";
53
53
  private _modelsFallbackToStructuredOutputTool;
54
54
  private _modelsWithoutTemperature;
55
+ private _modelsWithoutCachePoint;
56
+ private rememberModelRejectsCachePoint;
57
+ private supportsCachePoint;
55
58
  private rememberModelRejectsOutputConfig;
56
59
  private useFakeToolForStructuredOutput;
57
60
  private rememberModelRejectsTemperature;
58
61
  private supportsTemperature;
59
62
  buildRequest(request: OperateRequest): BedrockRequest;
63
+ /** Remove any cachePoint blocks so the request can be retried unsupported. */
64
+ private stripCachePoints;
65
+ private requestHasCachePoints;
60
66
  formatTools(toolkit: Toolkit): ProviderToolDefinition[];
61
67
  formatOutputSchema(schema: JsonObject | NaturalSchema | z.ZodType): JsonObject;
62
68
  executeRequest(client: unknown, request: unknown, signal?: AbortSignal): Promise<AnnotatedBedrockResponse>;
@@ -21,6 +21,11 @@ export declare class OpenAiAdapter extends BaseProviderAdapter {
21
21
  private supportsTemperature;
22
22
  /** Whether `reasoning.effort` may be sent for this model. Overridden by xAI. */
23
23
  protected supportsReasoningEffort(model: string): boolean;
24
+ /**
25
+ * Whether to emit `prompt_cache_key` (OpenAI Responses API). Overridable by
26
+ * OpenAI-compatible subclasses whose backend rejects the field.
27
+ */
28
+ protected supportsPromptCacheKey(): boolean;
24
29
  /** Translate a normalized effort to this provider's `reasoning.effort` value. */
25
30
  protected mapReasoningEffort(effort: LlmEffort, model: string): LlmEffortMapping;
26
31
  buildRequest(request: OperateRequest): unknown;
@@ -42,6 +42,13 @@ interface OpenRouterUsage {
42
42
  completionTokensDetails?: {
43
43
  reasoningTokens?: number;
44
44
  };
45
+ promptTokensDetails?: {
46
+ cachedTokens?: number;
47
+ cached_tokens?: number;
48
+ };
49
+ prompt_tokens_details?: {
50
+ cached_tokens?: number;
51
+ };
45
52
  }
46
53
  interface OpenRouterResponse {
47
54
  id: string;
@@ -95,6 +102,10 @@ interface OpenRouterRequest {
95
102
  type OpenRouterContentPart = {
96
103
  type: "text";
97
104
  text: string;
105
+ cache_control?: {
106
+ type: "ephemeral";
107
+ ttl?: "5m" | "1h";
108
+ };
98
109
  } | {
99
110
  type: "image_url";
100
111
  imageUrl: {
@@ -0,0 +1,16 @@
1
+ import { LlmExchangeEnvelope, LlmHistory, LlmInputMessage, LlmOperateInput, LlmOperateOptions, LlmOperateResponse } from "../../types/LlmProvider.interface.js";
2
+ import { OperateLoopState } from "../types.js";
3
+ /**
4
+ * Assemble the serializable exchange envelope for one operate() settlement.
5
+ * The `resolution` block is stamped by the Llm facade, which is the layer
6
+ * that knows fallback outcome; the loop fills provider/model best-effort.
7
+ */
8
+ export declare function buildExchangeEnvelope({ duration, initialHistoryLength, input, options, response, startedAt, state, }: {
9
+ duration: number;
10
+ initialHistoryLength: number;
11
+ input: string | LlmHistory | LlmInputMessage | LlmOperateInput;
12
+ options: LlmOperateOptions;
13
+ response: LlmOperateResponse;
14
+ startedAt: string;
15
+ state: OperateLoopState;
16
+ }): LlmExchangeEnvelope;
@@ -0,0 +1,20 @@
1
+ import { LlmExchangeCallback, LlmExchangeEnvelope } from "../../types/LlmProvider.interface.js";
2
+ /**
3
+ * Truthy-except-"false"/"0" gate on LLM_EXCHANGE_ENABLED, matching the
4
+ * DD_LLMOBS_ENABLED convention in observability/llmobs.ts.
5
+ */
6
+ export declare function isExchangeStoreEnabled(): boolean;
7
+ /**
8
+ * Whether the loop should assemble an exchange envelope at settlement.
9
+ */
10
+ export declare function isExchangeRequested({ onExchange, }: {
11
+ onExchange?: LlmExchangeCallback;
12
+ }): boolean;
13
+ /**
14
+ * Deliver an exchange envelope to a callback. Errors thrown by the callback
15
+ * are logged and swallowed — exchange capture must never interrupt the call.
16
+ */
17
+ export declare function emitExchange({ envelope, onExchange, }: {
18
+ envelope: LlmExchangeEnvelope;
19
+ onExchange?: LlmExchangeCallback;
20
+ }): Promise<void>;
@@ -0,0 +1,2 @@
1
+ export { buildExchangeEnvelope } from "./buildExchangeEnvelope.js";
2
+ export { emitExchange, isExchangeRequested, isExchangeStoreEnabled, } from "./emitExchange.js";
@@ -1,6 +1,6 @@
1
1
  import { JsonObject } from "@jaypie/types";
2
2
  import { type LlmEffort } from "../constants.js";
3
- import { LlmHistory, LlmMessageType, LlmOperateOptions, LlmUsageItem } from "../types/LlmProvider.interface.js";
3
+ import { LlmCache, LlmHistory, LlmMessageType, LlmOperateOptions, LlmUsageItem } from "../types/LlmProvider.interface.js";
4
4
  import { Toolkit } from "../tools/Toolkit.class.js";
5
5
  /**
6
6
  * Standardized tool call representation across providers
@@ -73,6 +73,11 @@ export interface OperateRequest {
73
73
  effort?: LlmEffort;
74
74
  /** Provider-specific options */
75
75
  providerOptions?: JsonObject;
76
+ /**
77
+ * Prompt-caching control for the stable prefix (system + tools). `true`/
78
+ * omitted = enabled@5m, `false`/`0` = disabled, `"5m"`/`"1h"` = enabled@ttl.
79
+ */
80
+ cache?: LlmCache;
76
81
  /** Whether the request will execute over a streaming transport */
77
82
  stream?: boolean;
78
83
  /** Sampling temperature (0-2 for most providers) */
@@ -150,6 +155,10 @@ export interface OperateLoopState {
150
155
  currentTurn: number;
151
156
  /** Formatted output schema for structured output */
152
157
  formattedFormat?: JsonObject;
158
+ /** Stop reason from the most recent model response */
159
+ lastStopReason?: string;
160
+ /** Model-request retries across all turns (exchange envelope) */
161
+ retries: number;
153
162
  /** Formatted tools for the provider */
154
163
  formattedTools?: ProviderToolDefinition[];
155
164
  /** Maximum allowed turns */
@@ -2,10 +2,20 @@ import { JsonObject } from "@jaypie/types";
2
2
  import { LlmMessageRole } from "../../types/LlmProvider.interface.js";
3
3
  export declare const ROLE_MAP: Record<LlmMessageRole, string>;
4
4
  export declare namespace Anthropic {
5
+ interface CacheControlEphemeral {
6
+ type: "ephemeral";
7
+ ttl?: "5m" | "1h";
8
+ }
5
9
  interface TextBlock {
6
10
  type: "text";
7
11
  text: string;
8
12
  }
13
+ /** Top-level `system` block form that can carry a cache breakpoint */
14
+ interface SystemTextBlockParam {
15
+ type: "text";
16
+ text: string;
17
+ cache_control?: CacheControlEphemeral;
18
+ }
9
19
  interface ToolUseBlock {
10
20
  type: "tool_use";
11
21
  id: string;
@@ -63,17 +73,20 @@ export declare namespace Anthropic {
63
73
  description?: string;
64
74
  input_schema: Messages.Tool.InputSchema;
65
75
  type?: "custom";
76
+ cache_control?: CacheControlEphemeral;
66
77
  }
67
78
  interface Usage {
68
79
  input_tokens: number;
69
80
  output_tokens: number;
70
81
  thinking_tokens?: number;
82
+ cache_read_input_tokens?: number;
83
+ cache_creation_input_tokens?: number;
71
84
  }
72
85
  interface MessageCreateParams {
73
86
  model: string;
74
87
  messages: MessageParam[];
75
88
  max_tokens: number;
76
- system?: string;
89
+ system?: string | SystemTextBlockParam[];
77
90
  tools?: Tool[];
78
91
  tool_choice?: {
79
92
  type: "auto" | "any" | "tool";
@@ -26,7 +26,7 @@ export declare enum LlmResponseStatus {
26
26
  Incomplete = "incomplete",
27
27
  InProgress = "in_progress"
28
28
  }
29
- interface LlmError {
29
+ export interface LlmError {
30
30
  detail?: string;
31
31
  status: number | string;
32
32
  title: string;
@@ -212,7 +212,97 @@ export interface LlmProgressEvent {
212
212
  usage?: LlmUsage;
213
213
  }
214
214
  export type LlmProgressCallback = (event: LlmProgressEvent) => unknown | Promise<unknown>;
215
+ /**
216
+ * Serializable snapshot of the request side of one operate() call.
217
+ * `input` is the raw, pre-interpolation input with multimodal parts preserved.
218
+ */
219
+ export interface LlmExchangeRequest {
220
+ cache?: LlmCache;
221
+ data?: NaturalMap;
222
+ effort?: LlmEffort;
223
+ explain?: boolean;
224
+ /** Format normalized to JSON Schema (Zod/Natural already rendered) */
225
+ format?: JsonObject;
226
+ input: string | LlmHistory | LlmInputMessage | LlmOperateInput;
227
+ instructions?: string;
228
+ /** Requested model (pre-fallback) */
229
+ model?: string;
230
+ placeholders?: {
231
+ input?: boolean;
232
+ instructions?: boolean;
233
+ system?: boolean;
234
+ };
235
+ providerOptions?: JsonObject;
236
+ system?: string;
237
+ temperature?: number;
238
+ /** Names of tools offered to the model */
239
+ tools?: string[];
240
+ turns?: boolean | number;
241
+ user?: string;
242
+ }
243
+ /**
244
+ * Serializable snapshot of the response side of one operate() call.
245
+ */
246
+ export interface LlmExchangeResponse {
247
+ content?: string | JsonObject;
248
+ error?: LlmError;
249
+ /** Turns this call added to history (tool calls/results included); never the resent history */
250
+ historyDelta: LlmHistory;
251
+ reasoning?: string[];
252
+ status: LlmResponseStatus;
253
+ stopReason?: string;
254
+ /** Per-model-call usage items (provider/model on each item) */
255
+ usage: LlmUsage;
256
+ /** Cumulative usage keyed `provider:model` */
257
+ usageTotals?: Record<string, LlmUsageItem>;
258
+ }
259
+ /**
260
+ * How the call was actually served after fallback and retry resolution.
261
+ */
262
+ export interface LlmExchangeResolution {
263
+ fallbackAttempts?: number;
264
+ fallbackUsed?: boolean;
265
+ /** Served model */
266
+ model?: string;
267
+ /** Served provider */
268
+ provider?: string;
269
+ /** Model-request retries within the served attempt */
270
+ retries?: number;
271
+ }
272
+ export interface LlmExchangeTiming {
273
+ /** Milliseconds from start to settlement */
274
+ duration: number;
275
+ /** ISO 8601 */
276
+ startedAt: string;
277
+ }
278
+ /**
279
+ * One serializable request/response envelope per operate() settlement
280
+ * (success or failure). Contains no functions.
281
+ */
282
+ export interface LlmExchangeEnvelope {
283
+ /** Provider response id(s) per model turn, when available */
284
+ ids?: string[];
285
+ request: LlmExchangeRequest;
286
+ resolution: LlmExchangeResolution;
287
+ response: LlmExchangeResponse;
288
+ timing: LlmExchangeTiming;
289
+ }
290
+ export type LlmExchangeCallback = (envelope: LlmExchangeEnvelope) => unknown | Promise<unknown>;
291
+ /**
292
+ * Prompt-caching control for operate()/stream().
293
+ * - `true` / omitted → caching enabled at the default `"5m"` TTL
294
+ * - `false` / `0` → caching disabled
295
+ * - `"5m"` / `"1h"` → enabled at that TTL (TTL honored by Anthropic/OpenRouter;
296
+ * other providers ignore it and cache with their own defaults)
297
+ */
298
+ export type LlmCache = boolean | 0 | "5m" | "1h";
215
299
  export interface LlmOperateOptions {
300
+ /**
301
+ * Prompt caching for the stable request prefix (system prompt + tools).
302
+ * Enabled by default; pass `false`/`0` to opt out, or `"5m"`/`"1h"` to set
303
+ * the TTL. See {@link LlmCache}.
304
+ */
305
+ cache?: LlmCache;
216
306
  data?: NaturalMap;
217
307
  /**
218
308
  * Provider-neutral reasoning effort (lowest | low | medium | high | highest).
@@ -276,6 +366,12 @@ export interface LlmOperateOptions {
276
366
  };
277
367
  instructions?: string;
278
368
  model?: string;
369
+ /**
370
+ * Fires once per operate() settlement (success or failure) with a fully
371
+ * serializable request/response envelope. Errors thrown by the callback
372
+ * are logged and never interrupt the call.
373
+ */
374
+ onExchange?: LlmExchangeCallback;
279
375
  /**
280
376
  * Receives lightweight progress events as the operate loop runs:
281
377
  * start, model_request, model_response, tool_call, tool_result,
@@ -315,6 +411,10 @@ export interface LlmUsageItem {
315
411
  output: number;
316
412
  reasoning: number;
317
413
  total: number;
414
+ /** Prompt-cache tokens served from cache this call (billed at ~0.1x input) */
415
+ cacheRead?: number;
416
+ /** Prompt-cache tokens written to cache this call (billed at ~1.25x input) */
417
+ cacheWrite?: number;
318
418
  provider?: string;
319
419
  model?: string;
320
420
  }
@@ -322,6 +422,11 @@ export type LlmUsage = LlmUsageItem[];
322
422
  export interface LlmOperateResponse {
323
423
  content?: string | JsonObject;
324
424
  error?: LlmError;
425
+ /**
426
+ * Serializable exchange envelope for this call. Present only when
427
+ * `onExchange` was passed or exchange persistence is enabled.
428
+ */
429
+ exchange?: LlmExchangeEnvelope;
325
430
  /** Number of providers attempted (1 = primary only, >1 = fallback(s) used) */
326
431
  fallbackAttempts?: number;
327
432
  /** Whether a fallback provider was used instead of the primary */
@@ -0,0 +1,21 @@
1
+ import { type LlmCache } from "../types/LlmProvider.interface.js";
2
+ export declare const CACHE_TTL_DEFAULT: "5m";
3
+ export type CacheTtl = "5m" | "1h";
4
+ export interface ResolvedCache {
5
+ enabled: boolean;
6
+ ttl: CacheTtl;
7
+ }
8
+ /**
9
+ * Normalize the caller-facing `cache` option into a concrete decision.
10
+ * - `undefined` / `true` → enabled at the default TTL
11
+ * - `false` / `0` → disabled
12
+ * - `"5m"` / `"1h"` → enabled at that TTL
13
+ */
14
+ export declare function resolveCache(cache: LlmCache | undefined): ResolvedCache;
15
+ /**
16
+ * Deterministic short key for providers with automatic, prefix-based caching
17
+ * (e.g. OpenAI `prompt_cache_key`). Derived from the stable prefix so the same
18
+ * system prompt + tools + model always routes to the same cache. Dependency-
19
+ * free FNV-1a; not cryptographic.
20
+ */
21
+ export declare function promptCacheKey(seed: string): string;
@@ -1,3 +1,4 @@
1
+ export * from "./cacheControl.js";
1
2
  export * from "./determineModelProvider.js";
2
3
  export * from "./extractReasoning.js";
3
4
  export * from "./fillFormatArrays.js";
package/dist/esm/Llm.d.ts CHANGED
@@ -27,6 +27,12 @@ declare class Llm implements LlmProvider {
27
27
  operate(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: Omit<LlmOperateOptions, "model"> & {
28
28
  model?: LlmModelOption;
29
29
  }): Promise<LlmOperateResponse>;
30
+ /**
31
+ * Stamp fallback resolution onto the envelope the operate loop attached to
32
+ * the response and deliver it to the caller's onExchange. Fires once per
33
+ * operate() settlement; callback errors are logged and never thrown.
34
+ */
35
+ private settleExchange;
30
36
  stream(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: Omit<LlmOperateOptions, "model"> & {
31
37
  model?: LlmModelOption;
32
38
  }): AsyncIterable<LlmStreamChunk>;