@jaypie/llm 1.2.5 → 1.2.6

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/cjs/Llm.d.ts CHANGED
@@ -1,14 +1,25 @@
1
1
  import { JsonObject } from "@jaypie/types";
2
2
  import { LlmProviderName } from "./constants.js";
3
- import { LlmHistory, LlmInputMessage, LlmMessageOptions, LlmOperateInput, LlmOperateOptions, LlmOperateResponse, LlmOptions, LlmProvider } from "./types/LlmProvider.interface.js";
3
+ import { LlmFallbackConfig, LlmHistory, LlmInputMessage, LlmMessageOptions, LlmOperateInput, LlmOperateOptions, LlmOperateResponse, LlmOptions, LlmProvider } from "./types/LlmProvider.interface.js";
4
4
  import { LlmStreamChunk } from "./types/LlmStreamChunk.interface.js";
5
5
  declare class Llm implements LlmProvider {
6
- private _provider;
6
+ private _fallbackConfig?;
7
7
  private _llm;
8
8
  private _options;
9
+ private _provider;
9
10
  constructor(providerName?: LlmProviderName | string, options?: LlmOptions);
10
11
  private createProvider;
11
12
  send(message: string, options?: LlmMessageOptions): Promise<string | JsonObject>;
13
+ /**
14
+ * Resolves the fallback chain from instance config and per-call options.
15
+ * Per-call options take precedence over instance config.
16
+ * Returns empty array if fallback is disabled.
17
+ */
18
+ private resolveFallbackChain;
19
+ /**
20
+ * Creates a fallback Llm instance lazily when needed.
21
+ */
22
+ private createFallbackInstance;
12
23
  operate(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: LlmOperateOptions): Promise<LlmOperateResponse>;
13
24
  stream(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: LlmOperateOptions): AsyncIterable<LlmStreamChunk>;
14
25
  static send(message: string, options?: LlmMessageOptions & {
@@ -17,8 +28,9 @@ declare class Llm implements LlmProvider {
17
28
  model?: string;
18
29
  }): Promise<string | JsonObject>;
19
30
  static operate(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: LlmOperateOptions & {
20
- llm?: LlmProviderName;
21
31
  apiKey?: string;
32
+ fallback?: LlmFallbackConfig[] | false;
33
+ llm?: LlmProviderName;
22
34
  model?: string;
23
35
  }): Promise<LlmOperateResponse>;
24
36
  static stream(input: string | LlmHistory | LlmInputMessage | LlmOperateInput, options?: LlmOperateOptions & {
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var errors = require('@jaypie/errors');
4
+ var log$2 = require('@jaypie/logger');
4
5
  var v4 = require('zod/v4');
5
6
  var kit = require('@jaypie/kit');
6
- var logger = require('@jaypie/logger');
7
7
  var RandomLib = require('random');
8
8
  var openai = require('openai');
9
9
  var zod = require('openai/helpers/zod');
@@ -481,7 +481,7 @@ function formatOperateInput(input, options) {
481
481
  return [input];
482
482
  }
483
483
 
484
- const getLogger$4 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
484
+ const getLogger$4 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
485
485
  const log$1 = getLogger$4();
486
486
 
487
487
  // Turn policy constants
@@ -2368,16 +2368,16 @@ function convertContentToOpenRouter(content) {
2368
2368
  }
2369
2369
  // Image content - warn and discard
2370
2370
  if (item.type === exports.LlmMessageType.InputImage) {
2371
- logger.log.warn("OpenRouter does not support image uploads; image discarded");
2371
+ log$2.log.warn("OpenRouter does not support image uploads; image discarded");
2372
2372
  continue;
2373
2373
  }
2374
2374
  // File/Document content - warn and discard
2375
2375
  if (item.type === exports.LlmMessageType.InputFile) {
2376
- logger.log.warn({ filename: item.filename }, "OpenRouter does not support file uploads; file discarded");
2376
+ log$2.log.warn({ filename: item.filename }, "OpenRouter does not support file uploads; file discarded");
2377
2377
  continue;
2378
2378
  }
2379
2379
  // Unknown type - warn and skip
2380
- logger.log.warn({ item }, "Unknown content type for OpenRouter; discarded");
2380
+ log$2.log.warn({ item }, "Unknown content type for OpenRouter; discarded");
2381
2381
  }
2382
2382
  // If no text parts remain, return empty string to avoid empty array
2383
2383
  if (parts.length === 0) {
@@ -2893,7 +2893,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
2893
2893
  const openRouterAdapter = new OpenRouterAdapter();
2894
2894
 
2895
2895
  const DEFAULT_TOOL_TYPE = "function";
2896
- const log = logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
2896
+ const log = log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
2897
2897
  function logToolMessage(message, context) {
2898
2898
  log.trace.var({ [context.name]: message });
2899
2899
  }
@@ -4495,7 +4495,7 @@ async function loadSdk$2() {
4495
4495
  }
4496
4496
  }
4497
4497
  // Logger
4498
- const getLogger$3 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4498
+ const getLogger$3 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4499
4499
  // Client initialization
4500
4500
  async function initializeClient$3({ apiKey, } = {}) {
4501
4501
  const logger = getLogger$3();
@@ -4534,7 +4534,7 @@ function prepareMessages$3(message, { data, placeholders } = {}) {
4534
4534
  }
4535
4535
  // Basic text completion
4536
4536
  async function createTextCompletion$1(client, messages, model, systemMessage) {
4537
- logger.log.trace("Using text output (unstructured)");
4537
+ log$2.log.trace("Using text output (unstructured)");
4538
4538
  const params = {
4539
4539
  model,
4540
4540
  messages,
@@ -4543,17 +4543,17 @@ async function createTextCompletion$1(client, messages, model, systemMessage) {
4543
4543
  // Add system instruction if provided
4544
4544
  if (systemMessage) {
4545
4545
  params.system = systemMessage;
4546
- logger.log.trace(`System message: ${systemMessage.length} characters`);
4546
+ log$2.log.trace(`System message: ${systemMessage.length} characters`);
4547
4547
  }
4548
4548
  const response = await client.messages.create(params);
4549
4549
  const firstContent = response.content[0];
4550
4550
  const text = firstContent && "text" in firstContent ? firstContent.text : "";
4551
- logger.log.trace(`Assistant reply: ${text.length} characters`);
4551
+ log$2.log.trace(`Assistant reply: ${text.length} characters`);
4552
4552
  return text;
4553
4553
  }
4554
4554
  // Structured output completion
4555
4555
  async function createStructuredCompletion$1(client, messages, model, responseSchema, systemMessage) {
4556
- logger.log.trace("Using structured output");
4556
+ log$2.log.trace("Using structured output");
4557
4557
  // Get the JSON schema for the response
4558
4558
  const schema = responseSchema instanceof v4.z.ZodType
4559
4559
  ? responseSchema
@@ -4586,7 +4586,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
4586
4586
  if (!schema.parse(result)) {
4587
4587
  throw new Error(`JSON response from Anthropic does not match schema: ${responseText}`);
4588
4588
  }
4589
- logger.log.trace("Received structured response", { result });
4589
+ log$2.log.trace("Received structured response", { result });
4590
4590
  return result;
4591
4591
  }
4592
4592
  catch {
@@ -4597,7 +4597,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
4597
4597
  throw new Error("Failed to parse structured response from Anthropic");
4598
4598
  }
4599
4599
  catch (error) {
4600
- logger.log.error("Error creating structured completion", { error });
4600
+ log$2.log.error("Error creating structured completion", { error });
4601
4601
  throw error;
4602
4602
  }
4603
4603
  }
@@ -4711,7 +4711,7 @@ async function loadSdk$1() {
4711
4711
  }
4712
4712
  }
4713
4713
  // Logger
4714
- const getLogger$2 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4714
+ const getLogger$2 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4715
4715
  // Client initialization
4716
4716
  async function initializeClient$2({ apiKey, } = {}) {
4717
4717
  const logger = getLogger$2();
@@ -4851,7 +4851,7 @@ class GeminiProvider {
4851
4851
  }
4852
4852
 
4853
4853
  // Logger
4854
- const getLogger$1 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4854
+ const getLogger$1 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4855
4855
  // Client initialization
4856
4856
  async function initializeClient$1({ apiKey, } = {}) {
4857
4857
  const logger = getLogger$1();
@@ -5035,7 +5035,7 @@ async function loadSdk() {
5035
5035
  }
5036
5036
  }
5037
5037
  // Logger
5038
- const getLogger = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
5038
+ const getLogger = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
5039
5039
  // Client initialization
5040
5040
  async function initializeClient({ apiKey, } = {}) {
5041
5041
  const logger = getLogger();
@@ -5185,7 +5185,7 @@ class OpenRouterProvider {
5185
5185
 
5186
5186
  class Llm {
5187
5187
  constructor(providerName = DEFAULT.PROVIDER.NAME, options = {}) {
5188
- const { model } = options;
5188
+ const { fallback, model } = options;
5189
5189
  let finalProvider = providerName;
5190
5190
  let finalModel = model;
5191
5191
  if (model) {
@@ -5214,6 +5214,7 @@ class Llm {
5214
5214
  finalModel = undefined;
5215
5215
  }
5216
5216
  }
5217
+ this._fallbackConfig = fallback;
5217
5218
  this._provider = finalProvider;
5218
5219
  this._options = { ...options, model: finalModel };
5219
5220
  this._llm = this.createProvider(finalProvider, this._options);
@@ -5242,11 +5243,81 @@ class Llm {
5242
5243
  async send(message, options) {
5243
5244
  return this._llm.send(message, options);
5244
5245
  }
5246
+ /**
5247
+ * Resolves the fallback chain from instance config and per-call options.
5248
+ * Per-call options take precedence over instance config.
5249
+ * Returns empty array if fallback is disabled.
5250
+ */
5251
+ resolveFallbackChain(options) {
5252
+ // Per-call `fallback: false` disables fallback entirely
5253
+ if (options.fallback === false) {
5254
+ return [];
5255
+ }
5256
+ // Per-call fallback array overrides instance config
5257
+ if (Array.isArray(options.fallback)) {
5258
+ return options.fallback;
5259
+ }
5260
+ // Use instance config if available
5261
+ return this._fallbackConfig || [];
5262
+ }
5263
+ /**
5264
+ * Creates a fallback Llm instance lazily when needed.
5265
+ */
5266
+ createFallbackInstance(config) {
5267
+ return new Llm(config.provider, {
5268
+ apiKey: config.apiKey,
5269
+ model: config.model,
5270
+ });
5271
+ }
5245
5272
  async operate(input, options = {}) {
5246
5273
  if (!this._llm.operate) {
5247
5274
  throw new errors.NotImplementedError(`Provider ${this._provider} does not support operate method`);
5248
5275
  }
5249
- return this._llm.operate(input, options);
5276
+ const fallbackChain = this.resolveFallbackChain(options);
5277
+ const optionsWithoutFallback = { ...options, fallback: false };
5278
+ let lastError;
5279
+ let attempts = 0;
5280
+ // Try primary provider first
5281
+ attempts++;
5282
+ try {
5283
+ const response = await this._llm.operate(input, optionsWithoutFallback);
5284
+ return {
5285
+ ...response,
5286
+ fallbackAttempts: attempts,
5287
+ fallbackUsed: false,
5288
+ provider: response.provider || this._provider,
5289
+ };
5290
+ }
5291
+ catch (error) {
5292
+ lastError = error;
5293
+ log$2.warn(`Provider ${this._provider} failed`, {
5294
+ error: lastError.message,
5295
+ fallbacksRemaining: fallbackChain.length,
5296
+ });
5297
+ }
5298
+ // Try fallback providers
5299
+ for (const fallbackConfig of fallbackChain) {
5300
+ attempts++;
5301
+ try {
5302
+ const fallbackInstance = this.createFallbackInstance(fallbackConfig);
5303
+ const response = await fallbackInstance.operate(input, optionsWithoutFallback);
5304
+ return {
5305
+ ...response,
5306
+ fallbackAttempts: attempts,
5307
+ fallbackUsed: true,
5308
+ provider: response.provider || fallbackConfig.provider,
5309
+ };
5310
+ }
5311
+ catch (error) {
5312
+ lastError = error;
5313
+ log$2.warn(`Fallback provider ${fallbackConfig.provider} failed`, {
5314
+ error: lastError.message,
5315
+ fallbacksRemaining: fallbackChain.length - attempts + 1,
5316
+ });
5317
+ }
5318
+ }
5319
+ // All providers failed, throw the last error
5320
+ throw lastError;
5250
5321
  }
5251
5322
  async *stream(input, options = {}) {
5252
5323
  if (!this._llm.stream) {
@@ -5260,7 +5331,7 @@ class Llm {
5260
5331
  return instance.send(message, messageOptions);
5261
5332
  }
5262
5333
  static async operate(input, options) {
5263
- const { llm, apiKey, model, ...operateOptions } = options || {};
5334
+ const { apiKey, fallback, llm, model, ...operateOptions } = options || {};
5264
5335
  let finalLlm = llm;
5265
5336
  let finalModel = model;
5266
5337
  if (!llm && model) {
@@ -5277,8 +5348,18 @@ class Llm {
5277
5348
  finalModel = undefined;
5278
5349
  }
5279
5350
  }
5280
- const instance = new Llm(finalLlm, { apiKey, model: finalModel });
5281
- return instance.operate(input, operateOptions);
5351
+ // Resolve fallback for static method: pass to instance if array, pass to operate options if false
5352
+ const instanceFallback = Array.isArray(fallback) ? fallback : undefined;
5353
+ const operateFallback = fallback === false ? false : undefined;
5354
+ const instance = new Llm(finalLlm, {
5355
+ apiKey,
5356
+ fallback: instanceFallback,
5357
+ model: finalModel,
5358
+ });
5359
+ return instance.operate(input, {
5360
+ ...operateOptions,
5361
+ ...(operateFallback !== undefined && { fallback: operateFallback }),
5362
+ });
5282
5363
  }
5283
5364
  static stream(input, options) {
5284
5365
  const { llm, apiKey, model, ...streamOptions } = options || {};