@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
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/llm",
3
- "version": "1.3.13",
3
+ "version": "1.3.15",
4
4
  "description": "Large language model utilities",
5
5
  "repository": {
6
6
  "type": "git",
@@ -55,12 +55,16 @@
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@aws-sdk/client-bedrock-runtime": "^3.1045.0",
58
+ "@jaypie/dynamodb": "*",
58
59
  "@jaypie/kit": "*",
59
60
  "@jaypie/logger": "*"
60
61
  },
61
62
  "peerDependenciesMeta": {
62
63
  "@aws-sdk/client-bedrock-runtime": {
63
64
  "optional": true
65
+ },
66
+ "@jaypie/dynamodb": {
67
+ "optional": true
64
68
  }
65
69
  },
66
70
  "publishConfig": {