@jaypie/llm 1.2.4 → 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
  }
@@ -3716,7 +3716,8 @@ class RetryExecutor {
3716
3716
  providerRequest: options.context.providerRequest,
3717
3717
  error,
3718
3718
  });
3719
- throw new errors.BadGatewayError();
3719
+ const errorMessage = error instanceof Error ? error.message : String(error);
3720
+ throw new errors.BadGatewayError(errorMessage);
3720
3721
  }
3721
3722
  // Check if error is not retryable
3722
3723
  if (!this.errorClassifier.isRetryable(error)) {
@@ -3728,7 +3729,8 @@ class RetryExecutor {
3728
3729
  providerRequest: options.context.providerRequest,
3729
3730
  error,
3730
3731
  });
3731
- throw new errors.BadGatewayError();
3732
+ const errorMessage = error instanceof Error ? error.message : String(error);
3733
+ throw new errors.BadGatewayError(errorMessage);
3732
3734
  }
3733
3735
  // Warn if this is an unknown error type
3734
3736
  if (!this.errorClassifier.isKnownError(error)) {
@@ -4493,7 +4495,7 @@ async function loadSdk$2() {
4493
4495
  }
4494
4496
  }
4495
4497
  // Logger
4496
- const getLogger$3 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4498
+ const getLogger$3 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4497
4499
  // Client initialization
4498
4500
  async function initializeClient$3({ apiKey, } = {}) {
4499
4501
  const logger = getLogger$3();
@@ -4532,7 +4534,7 @@ function prepareMessages$3(message, { data, placeholders } = {}) {
4532
4534
  }
4533
4535
  // Basic text completion
4534
4536
  async function createTextCompletion$1(client, messages, model, systemMessage) {
4535
- logger.log.trace("Using text output (unstructured)");
4537
+ log$2.log.trace("Using text output (unstructured)");
4536
4538
  const params = {
4537
4539
  model,
4538
4540
  messages,
@@ -4541,17 +4543,17 @@ async function createTextCompletion$1(client, messages, model, systemMessage) {
4541
4543
  // Add system instruction if provided
4542
4544
  if (systemMessage) {
4543
4545
  params.system = systemMessage;
4544
- logger.log.trace(`System message: ${systemMessage.length} characters`);
4546
+ log$2.log.trace(`System message: ${systemMessage.length} characters`);
4545
4547
  }
4546
4548
  const response = await client.messages.create(params);
4547
4549
  const firstContent = response.content[0];
4548
4550
  const text = firstContent && "text" in firstContent ? firstContent.text : "";
4549
- logger.log.trace(`Assistant reply: ${text.length} characters`);
4551
+ log$2.log.trace(`Assistant reply: ${text.length} characters`);
4550
4552
  return text;
4551
4553
  }
4552
4554
  // Structured output completion
4553
4555
  async function createStructuredCompletion$1(client, messages, model, responseSchema, systemMessage) {
4554
- logger.log.trace("Using structured output");
4556
+ log$2.log.trace("Using structured output");
4555
4557
  // Get the JSON schema for the response
4556
4558
  const schema = responseSchema instanceof v4.z.ZodType
4557
4559
  ? responseSchema
@@ -4584,7 +4586,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
4584
4586
  if (!schema.parse(result)) {
4585
4587
  throw new Error(`JSON response from Anthropic does not match schema: ${responseText}`);
4586
4588
  }
4587
- logger.log.trace("Received structured response", { result });
4589
+ log$2.log.trace("Received structured response", { result });
4588
4590
  return result;
4589
4591
  }
4590
4592
  catch {
@@ -4595,7 +4597,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
4595
4597
  throw new Error("Failed to parse structured response from Anthropic");
4596
4598
  }
4597
4599
  catch (error) {
4598
- logger.log.error("Error creating structured completion", { error });
4600
+ log$2.log.error("Error creating structured completion", { error });
4599
4601
  throw error;
4600
4602
  }
4601
4603
  }
@@ -4709,7 +4711,7 @@ async function loadSdk$1() {
4709
4711
  }
4710
4712
  }
4711
4713
  // Logger
4712
- const getLogger$2 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4714
+ const getLogger$2 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4713
4715
  // Client initialization
4714
4716
  async function initializeClient$2({ apiKey, } = {}) {
4715
4717
  const logger = getLogger$2();
@@ -4849,7 +4851,7 @@ class GeminiProvider {
4849
4851
  }
4850
4852
 
4851
4853
  // Logger
4852
- const getLogger$1 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4854
+ const getLogger$1 = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
4853
4855
  // Client initialization
4854
4856
  async function initializeClient$1({ apiKey, } = {}) {
4855
4857
  const logger = getLogger$1();
@@ -5033,7 +5035,7 @@ async function loadSdk() {
5033
5035
  }
5034
5036
  }
5035
5037
  // Logger
5036
- const getLogger = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
5038
+ const getLogger = () => log$2.log.lib({ lib: kit.JAYPIE.LIB.LLM });
5037
5039
  // Client initialization
5038
5040
  async function initializeClient({ apiKey, } = {}) {
5039
5041
  const logger = getLogger();
@@ -5183,7 +5185,7 @@ class OpenRouterProvider {
5183
5185
 
5184
5186
  class Llm {
5185
5187
  constructor(providerName = DEFAULT.PROVIDER.NAME, options = {}) {
5186
- const { model } = options;
5188
+ const { fallback, model } = options;
5187
5189
  let finalProvider = providerName;
5188
5190
  let finalModel = model;
5189
5191
  if (model) {
@@ -5212,6 +5214,7 @@ class Llm {
5212
5214
  finalModel = undefined;
5213
5215
  }
5214
5216
  }
5217
+ this._fallbackConfig = fallback;
5215
5218
  this._provider = finalProvider;
5216
5219
  this._options = { ...options, model: finalModel };
5217
5220
  this._llm = this.createProvider(finalProvider, this._options);
@@ -5240,11 +5243,81 @@ class Llm {
5240
5243
  async send(message, options) {
5241
5244
  return this._llm.send(message, options);
5242
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
+ }
5243
5272
  async operate(input, options = {}) {
5244
5273
  if (!this._llm.operate) {
5245
5274
  throw new errors.NotImplementedError(`Provider ${this._provider} does not support operate method`);
5246
5275
  }
5247
- 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;
5248
5321
  }
5249
5322
  async *stream(input, options = {}) {
5250
5323
  if (!this._llm.stream) {
@@ -5258,7 +5331,7 @@ class Llm {
5258
5331
  return instance.send(message, messageOptions);
5259
5332
  }
5260
5333
  static async operate(input, options) {
5261
- const { llm, apiKey, model, ...operateOptions } = options || {};
5334
+ const { apiKey, fallback, llm, model, ...operateOptions } = options || {};
5262
5335
  let finalLlm = llm;
5263
5336
  let finalModel = model;
5264
5337
  if (!llm && model) {
@@ -5275,8 +5348,18 @@ class Llm {
5275
5348
  finalModel = undefined;
5276
5349
  }
5277
5350
  }
5278
- const instance = new Llm(finalLlm, { apiKey, model: finalModel });
5279
- 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
+ });
5280
5363
  }
5281
5364
  static stream(input, options) {
5282
5365
  const { llm, apiKey, model, ...streamOptions } = options || {};