@jaypie/llm 1.3.10 → 1.3.11
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 +19 -11
- package/dist/cjs/errors/LlmError.d.ts +62 -0
- package/dist/cjs/errors/toLlmError.d.ts +11 -0
- package/dist/cjs/index.cjs +388 -55
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +113 -14
- package/dist/cjs/index.d.ts +4 -1
- package/dist/cjs/operate/retry/RetryExecutor.d.ts +13 -0
- package/dist/cjs/operate/types.d.ts +12 -1
- package/dist/cjs/types/LlmProvider.interface.d.ts +9 -0
- package/dist/cjs/util/classifyProviderError.d.ts +13 -0
- package/dist/cjs/util/resolveModelChain.d.ts +17 -0
- package/dist/esm/Llm.d.ts +19 -11
- package/dist/esm/errors/LlmError.d.ts +62 -0
- package/dist/esm/errors/toLlmError.d.ts +11 -0
- package/dist/esm/index.d.ts +113 -14
- package/dist/esm/index.js +354 -26
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/retry/RetryExecutor.d.ts +13 -0
- package/dist/esm/operate/types.d.ts +12 -1
- package/dist/esm/types/LlmProvider.interface.d.ts +9 -0
- package/dist/esm/util/classifyProviderError.d.ts +13 -0
- package/dist/esm/util/resolveModelChain.d.ts +17 -0
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfigurationError, BadGatewayError, TooManyRequestsError, NotImplementedError } from '@jaypie/errors';
|
|
1
|
+
import { ConfigurationError, JaypieError, BadGatewayError, TooManyRequestsError, NotImplementedError } from '@jaypie/errors';
|
|
2
2
|
import log$1, { log as log$2 } from '@jaypie/logger';
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
4
|
import { placeholders, JAYPIE, resolveValue, sleep } from '@jaypie/kit';
|
|
@@ -401,6 +401,36 @@ function determineModelProvider(input) {
|
|
|
401
401
|
};
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Normalizes a `model` option that may be a single model name or a
|
|
406
|
+
* preference-ordered array of model names.
|
|
407
|
+
*
|
|
408
|
+
* A `string[]` is interpreted as a fallback chain: index `0` is the primary
|
|
409
|
+
* model, indices `1..` become fallback entries with an auto-detected provider
|
|
410
|
+
* (reusing `determineModelProvider`, exactly as the constructor's first
|
|
411
|
+
* argument does).
|
|
412
|
+
*
|
|
413
|
+
* Returns the primary model plus the derived fallback chain. A bare string
|
|
414
|
+
* yields an empty chain and is unchanged.
|
|
415
|
+
*/
|
|
416
|
+
function resolveModelChain(model) {
|
|
417
|
+
if (!Array.isArray(model)) {
|
|
418
|
+
return { fallback: [], model };
|
|
419
|
+
}
|
|
420
|
+
if (model.length === 0) {
|
|
421
|
+
throw new ConfigurationError("model array must contain at least one model name");
|
|
422
|
+
}
|
|
423
|
+
const [primary, ...rest] = model;
|
|
424
|
+
const fallback = rest.map((name) => {
|
|
425
|
+
const determined = determineModelProvider(name);
|
|
426
|
+
if (!determined.provider) {
|
|
427
|
+
throw new ConfigurationError(`Unable to determine provider from model: ${name}`);
|
|
428
|
+
}
|
|
429
|
+
return { model: determined.model, provider: determined.provider };
|
|
430
|
+
});
|
|
431
|
+
return { fallback, model: primary };
|
|
432
|
+
}
|
|
433
|
+
|
|
404
434
|
//
|
|
405
435
|
//
|
|
406
436
|
// Abstract Base Adapter
|
|
@@ -1490,8 +1520,14 @@ var ErrorCategory;
|
|
|
1490
1520
|
(function (ErrorCategory) {
|
|
1491
1521
|
/** Error is transient and can be retried */
|
|
1492
1522
|
ErrorCategory["Retryable"] = "retryable";
|
|
1493
|
-
/** Error is due to rate limiting */
|
|
1523
|
+
/** Error is due to short-term rate limiting (retry after a delay) */
|
|
1494
1524
|
ErrorCategory["RateLimit"] = "rate_limit";
|
|
1525
|
+
/**
|
|
1526
|
+
* Provider quota is exhausted or the account cannot be billed
|
|
1527
|
+
* (insufficient funds, daily quota, plan limit). Terminal and actionable —
|
|
1528
|
+
* retrying within the request budget will not help.
|
|
1529
|
+
*/
|
|
1530
|
+
ErrorCategory["Quota"] = "quota";
|
|
1495
1531
|
/** Error cannot be recovered from */
|
|
1496
1532
|
ErrorCategory["Unrecoverable"] = "unrecoverable";
|
|
1497
1533
|
/** Error type is unknown */
|
|
@@ -1582,6 +1618,99 @@ function isTransientNetworkError(error) {
|
|
|
1582
1618
|
return false;
|
|
1583
1619
|
}
|
|
1584
1620
|
|
|
1621
|
+
//
|
|
1622
|
+
//
|
|
1623
|
+
// Constants
|
|
1624
|
+
//
|
|
1625
|
+
/**
|
|
1626
|
+
* Transient structured-output compile failures. The provider caches the
|
|
1627
|
+
* compiled grammar after a successful compile, so an immediate retry of the
|
|
1628
|
+
* identical request typically succeeds (issue #422).
|
|
1629
|
+
*/
|
|
1630
|
+
const RETRYABLE_MESSAGE_PATTERNS = [
|
|
1631
|
+
"grammar compilation timed out",
|
|
1632
|
+
"grammar compilation timeout",
|
|
1633
|
+
];
|
|
1634
|
+
/** Billing / insufficient-funds signals — the account cannot be charged. */
|
|
1635
|
+
const BILLING_MESSAGE_PATTERNS = [
|
|
1636
|
+
"insufficient_quota",
|
|
1637
|
+
"insufficient funds",
|
|
1638
|
+
"insufficient credit",
|
|
1639
|
+
"credit balance",
|
|
1640
|
+
"billing",
|
|
1641
|
+
"payment required",
|
|
1642
|
+
"plan and billing",
|
|
1643
|
+
];
|
|
1644
|
+
/** Exhausted usage quota (per-day / per-model limits). */
|
|
1645
|
+
const QUOTA_MESSAGE_PATTERNS = [
|
|
1646
|
+
"quota exceeded",
|
|
1647
|
+
"exceeded your current quota",
|
|
1648
|
+
"resource_exhausted",
|
|
1649
|
+
"quota_exceeded",
|
|
1650
|
+
];
|
|
1651
|
+
//
|
|
1652
|
+
//
|
|
1653
|
+
// Helpers
|
|
1654
|
+
//
|
|
1655
|
+
function extractMessage(error) {
|
|
1656
|
+
if (error instanceof Error)
|
|
1657
|
+
return error.message.toLowerCase();
|
|
1658
|
+
if (typeof error === "string")
|
|
1659
|
+
return error.toLowerCase();
|
|
1660
|
+
if (error && typeof error === "object") {
|
|
1661
|
+
const message = error.message;
|
|
1662
|
+
if (typeof message === "string")
|
|
1663
|
+
return message.toLowerCase();
|
|
1664
|
+
}
|
|
1665
|
+
return "";
|
|
1666
|
+
}
|
|
1667
|
+
//
|
|
1668
|
+
//
|
|
1669
|
+
// Main
|
|
1670
|
+
//
|
|
1671
|
+
/**
|
|
1672
|
+
* Shared, provider-agnostic first pass over an error. Returns a
|
|
1673
|
+
* {@link ClassifiedError} when the message unambiguously identifies a
|
|
1674
|
+
* cross-provider condition (retryable structured-output timeout, exhausted
|
|
1675
|
+
* quota, or a billing failure), or `undefined` to defer to the adapter's own
|
|
1676
|
+
* status/name classification.
|
|
1677
|
+
*
|
|
1678
|
+
* Adapters call this before their existing logic so that, for example, a
|
|
1679
|
+
* `429` carrying a daily-quota message is classified as {@link
|
|
1680
|
+
* ErrorCategory.Quota} rather than {@link ErrorCategory.RateLimit}.
|
|
1681
|
+
*/
|
|
1682
|
+
function classifyProviderError(error) {
|
|
1683
|
+
const message = extractMessage(error);
|
|
1684
|
+
if (!message)
|
|
1685
|
+
return undefined;
|
|
1686
|
+
for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
|
|
1687
|
+
if (message.includes(pattern)) {
|
|
1688
|
+
return { category: ErrorCategory.Retryable, error, shouldRetry: true };
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
for (const pattern of BILLING_MESSAGE_PATTERNS) {
|
|
1692
|
+
if (message.includes(pattern)) {
|
|
1693
|
+
return {
|
|
1694
|
+
category: ErrorCategory.Quota,
|
|
1695
|
+
error,
|
|
1696
|
+
reason: "billing",
|
|
1697
|
+
shouldRetry: false,
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
for (const pattern of QUOTA_MESSAGE_PATTERNS) {
|
|
1702
|
+
if (message.includes(pattern)) {
|
|
1703
|
+
return {
|
|
1704
|
+
category: ErrorCategory.Quota,
|
|
1705
|
+
error,
|
|
1706
|
+
reason: "quota",
|
|
1707
|
+
shouldRetry: false,
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
return undefined;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1585
1714
|
//
|
|
1586
1715
|
//
|
|
1587
1716
|
// Constants
|
|
@@ -2355,6 +2484,11 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2355
2484
|
// Error Classification
|
|
2356
2485
|
//
|
|
2357
2486
|
classifyError(error) {
|
|
2487
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
2488
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
2489
|
+
const shared = classifyProviderError(error);
|
|
2490
|
+
if (shared)
|
|
2491
|
+
return shared;
|
|
2358
2492
|
const errorName = error?.constructor?.name;
|
|
2359
2493
|
// Check for rate limit error
|
|
2360
2494
|
if (errorName === "RateLimitError") {
|
|
@@ -2963,6 +3097,11 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2963
3097
|
// Error Classification
|
|
2964
3098
|
//
|
|
2965
3099
|
classifyError(error) {
|
|
3100
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
3101
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
3102
|
+
const shared = classifyProviderError(error);
|
|
3103
|
+
if (shared)
|
|
3104
|
+
return shared;
|
|
2966
3105
|
const errorName = error?.constructor?.name;
|
|
2967
3106
|
const errorMessage = error?.message ?? "";
|
|
2968
3107
|
if (errorName === "ThrottlingException" ||
|
|
@@ -3632,6 +3771,11 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3632
3771
|
// Error Classification
|
|
3633
3772
|
//
|
|
3634
3773
|
classifyError(error) {
|
|
3774
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
3775
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
3776
|
+
const shared = classifyProviderError(error);
|
|
3777
|
+
if (shared)
|
|
3778
|
+
return shared;
|
|
3635
3779
|
const geminiError = error;
|
|
3636
3780
|
// Extract status code from error
|
|
3637
3781
|
const statusCode = geminiError.status || geminiError.code;
|
|
@@ -4580,6 +4724,11 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4580
4724
|
// Error Classification
|
|
4581
4725
|
//
|
|
4582
4726
|
classifyError(error) {
|
|
4727
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
4728
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
4729
|
+
const shared = classifyProviderError(error);
|
|
4730
|
+
if (shared)
|
|
4731
|
+
return shared;
|
|
4583
4732
|
// Check for rate limit error
|
|
4584
4733
|
if (error instanceof RateLimitError$1) {
|
|
4585
4734
|
return {
|
|
@@ -5348,6 +5497,11 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5348
5497
|
// Error Classification
|
|
5349
5498
|
//
|
|
5350
5499
|
classifyError(error) {
|
|
5500
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
5501
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
5502
|
+
const shared = classifyProviderError(error);
|
|
5503
|
+
if (shared)
|
|
5504
|
+
return shared;
|
|
5351
5505
|
// Check if error has a status code (HTTP error)
|
|
5352
5506
|
const errorWithStatus = error;
|
|
5353
5507
|
const statusCode = errorWithStatus.status || errorWithStatus.statusCode;
|
|
@@ -6712,6 +6866,122 @@ function createStaleRejectionGuard() {
|
|
|
6712
6866
|
};
|
|
6713
6867
|
}
|
|
6714
6868
|
|
|
6869
|
+
//
|
|
6870
|
+
//
|
|
6871
|
+
// Base
|
|
6872
|
+
//
|
|
6873
|
+
/**
|
|
6874
|
+
* Normalized, provider-agnostic LLM error. Thrown by the operate/stream retry
|
|
6875
|
+
* layer when a request cannot be completed, so consumers can `catch` a stable
|
|
6876
|
+
* type regardless of which provider failed. Extends {@link JaypieError} so
|
|
6877
|
+
* `isJaypieError()` and `.status` continue to work; the original provider error
|
|
6878
|
+
* is preserved on `.cause`.
|
|
6879
|
+
*/
|
|
6880
|
+
class LlmError extends JaypieError {
|
|
6881
|
+
constructor(message, category, { status = 502, title = "Bad Gateway", provider, model, retryAfterMs, cause, } = {}) {
|
|
6882
|
+
super(message, { status, title }, { _type: "LlmError" });
|
|
6883
|
+
this.name = "LlmError";
|
|
6884
|
+
this.category = category;
|
|
6885
|
+
this.provider = provider;
|
|
6886
|
+
this.model = model;
|
|
6887
|
+
this.retryAfterMs = retryAfterMs;
|
|
6888
|
+
this.cause = cause;
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
//
|
|
6892
|
+
//
|
|
6893
|
+
// Subclasses
|
|
6894
|
+
//
|
|
6895
|
+
/**
|
|
6896
|
+
* Short-term rate limiting (per-minute 429). Terminal within the request
|
|
6897
|
+
* budget; `retryAfterMs` carries the provider's suggested wait when available.
|
|
6898
|
+
*/
|
|
6899
|
+
class LlmRateLimitError extends LlmError {
|
|
6900
|
+
constructor(message, options = {}) {
|
|
6901
|
+
super(message, ErrorCategory.RateLimit, {
|
|
6902
|
+
status: 429,
|
|
6903
|
+
title: "Too Many Requests",
|
|
6904
|
+
...options,
|
|
6905
|
+
});
|
|
6906
|
+
this.name = "LlmRateLimitError";
|
|
6907
|
+
}
|
|
6908
|
+
}
|
|
6909
|
+
/**
|
|
6910
|
+
* Provider quota is exhausted or the account cannot be billed. Terminal and
|
|
6911
|
+
* actionable. `reason` distinguishes an exhausted usage quota from insufficient
|
|
6912
|
+
* funds.
|
|
6913
|
+
*/
|
|
6914
|
+
class LlmQuotaError extends LlmError {
|
|
6915
|
+
constructor(message, { reason = "quota", ...options } = {}) {
|
|
6916
|
+
super(message, ErrorCategory.Quota, {
|
|
6917
|
+
status: 402,
|
|
6918
|
+
title: "Payment Required",
|
|
6919
|
+
...options,
|
|
6920
|
+
});
|
|
6921
|
+
this.name = "LlmQuotaError";
|
|
6922
|
+
this.reason = reason;
|
|
6923
|
+
}
|
|
6924
|
+
}
|
|
6925
|
+
/**
|
|
6926
|
+
* The request cannot be recovered from (bad request, authentication,
|
|
6927
|
+
* permission, not found). Terminal.
|
|
6928
|
+
*/
|
|
6929
|
+
class LlmUnrecoverableError extends LlmError {
|
|
6930
|
+
constructor(message, options = {}) {
|
|
6931
|
+
super(message, ErrorCategory.Unrecoverable, {
|
|
6932
|
+
status: 502,
|
|
6933
|
+
title: "Bad Gateway",
|
|
6934
|
+
...options,
|
|
6935
|
+
});
|
|
6936
|
+
this.name = "LlmUnrecoverableError";
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6939
|
+
/**
|
|
6940
|
+
* A transient or unknown error survived the retry budget. Terminal only because
|
|
6941
|
+
* retries were exhausted; the underlying condition may succeed later.
|
|
6942
|
+
*/
|
|
6943
|
+
class LlmTransientError extends LlmError {
|
|
6944
|
+
constructor(message, options = {}) {
|
|
6945
|
+
super(message, ErrorCategory.Retryable, {
|
|
6946
|
+
status: 504,
|
|
6947
|
+
title: "Gateway Timeout",
|
|
6948
|
+
...options,
|
|
6949
|
+
});
|
|
6950
|
+
this.name = "LlmTransientError";
|
|
6951
|
+
}
|
|
6952
|
+
}
|
|
6953
|
+
|
|
6954
|
+
/**
|
|
6955
|
+
* Map a {@link ClassifiedError} to the matching {@link LlmError} subclass,
|
|
6956
|
+
* preserving the original error as `cause`. Used by the retry layer to throw a
|
|
6957
|
+
* stable, catchable type instead of a bare gateway error.
|
|
6958
|
+
*/
|
|
6959
|
+
function toLlmError(classified, context = {}) {
|
|
6960
|
+
const original = classified.error;
|
|
6961
|
+
const message = original instanceof Error ? original.message : String(original);
|
|
6962
|
+
const options = {
|
|
6963
|
+
cause: original,
|
|
6964
|
+
model: context.model,
|
|
6965
|
+
provider: context.provider,
|
|
6966
|
+
retryAfterMs: classified.suggestedDelayMs,
|
|
6967
|
+
};
|
|
6968
|
+
switch (classified.category) {
|
|
6969
|
+
case ErrorCategory.RateLimit:
|
|
6970
|
+
return new LlmRateLimitError(message, options);
|
|
6971
|
+
case ErrorCategory.Quota:
|
|
6972
|
+
return new LlmQuotaError(message, {
|
|
6973
|
+
...options,
|
|
6974
|
+
reason: classified.reason ?? "quota",
|
|
6975
|
+
});
|
|
6976
|
+
case ErrorCategory.Unrecoverable:
|
|
6977
|
+
return new LlmUnrecoverableError(message, options);
|
|
6978
|
+
case ErrorCategory.Retryable:
|
|
6979
|
+
case ErrorCategory.Unknown:
|
|
6980
|
+
default:
|
|
6981
|
+
return new LlmTransientError(message, options);
|
|
6982
|
+
}
|
|
6983
|
+
}
|
|
6984
|
+
|
|
6715
6985
|
//
|
|
6716
6986
|
//
|
|
6717
6987
|
// Main
|
|
@@ -6726,6 +6996,18 @@ class RetryExecutor {
|
|
|
6726
6996
|
this.hookRunner = config.hookRunner ?? hookRunner;
|
|
6727
6997
|
this.errorClassifier = config.errorClassifier;
|
|
6728
6998
|
}
|
|
6999
|
+
/**
|
|
7000
|
+
* Build the typed, provider-agnostic error thrown when a request cannot be
|
|
7001
|
+
* completed — classified (rate limit / quota / unrecoverable / transient)
|
|
7002
|
+
* and carrying the provider, model, and original error as `cause`.
|
|
7003
|
+
*/
|
|
7004
|
+
toTerminalError(error, context) {
|
|
7005
|
+
const classified = this.errorClassifier.classify(error);
|
|
7006
|
+
return toLlmError(classified, {
|
|
7007
|
+
model: context.model,
|
|
7008
|
+
provider: context.provider,
|
|
7009
|
+
});
|
|
7010
|
+
}
|
|
6729
7011
|
/**
|
|
6730
7012
|
* Execute an operation with retry logic.
|
|
6731
7013
|
* Each attempt receives an AbortSignal. On failure, the signal is aborted
|
|
@@ -6769,8 +7051,7 @@ class RetryExecutor {
|
|
|
6769
7051
|
providerRequest: options.context.providerRequest,
|
|
6770
7052
|
error,
|
|
6771
7053
|
});
|
|
6772
|
-
|
|
6773
|
-
throw new BadGatewayError(errorMessage);
|
|
7054
|
+
throw this.toTerminalError(error, options.context);
|
|
6774
7055
|
}
|
|
6775
7056
|
// Check if error is not retryable
|
|
6776
7057
|
if (!this.errorClassifier.isRetryable(error)) {
|
|
@@ -6782,8 +7063,7 @@ class RetryExecutor {
|
|
|
6782
7063
|
providerRequest: options.context.providerRequest,
|
|
6783
7064
|
error,
|
|
6784
7065
|
});
|
|
6785
|
-
|
|
6786
|
-
throw new BadGatewayError(errorMessage);
|
|
7066
|
+
throw this.toTerminalError(error, options.context);
|
|
6787
7067
|
}
|
|
6788
7068
|
// Warn if this is an unknown error type
|
|
6789
7069
|
if (!this.errorClassifier.isKnownError(error)) {
|
|
@@ -6826,6 +7106,7 @@ const MAX_CONSECUTIVE_TOOL_ERRORS = 6;
|
|
|
6826
7106
|
*/
|
|
6827
7107
|
function createErrorClassifier(adapter) {
|
|
6828
7108
|
return {
|
|
7109
|
+
classify: (error) => adapter.classifyError(error),
|
|
6829
7110
|
isRetryable: (error) => adapter.isRetryableError(error),
|
|
6830
7111
|
isKnownError: (error) => {
|
|
6831
7112
|
const classified = adapter.classifyError(error);
|
|
@@ -7079,7 +7360,9 @@ class OperateLoop {
|
|
|
7079
7360
|
const response = await retryExecutor.execute((signal) => this.adapter.executeRequest(this.client, providerRequest, signal), {
|
|
7080
7361
|
context: {
|
|
7081
7362
|
input: state.currentInput,
|
|
7363
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
7082
7364
|
options,
|
|
7365
|
+
provider: this.adapter.name,
|
|
7083
7366
|
providerRequest,
|
|
7084
7367
|
},
|
|
7085
7368
|
hooks: hooksWithProgress,
|
|
@@ -7702,9 +7985,11 @@ class StreamLoop {
|
|
|
7702
7985
|
!this.adapter.isRetryableError(error)) {
|
|
7703
7986
|
log.error(`Stream request failed after ${this.retryPolicy.maxRetries} retries`);
|
|
7704
7987
|
log.var({ error });
|
|
7705
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7706
7988
|
llmSpan?.finish();
|
|
7707
|
-
throw
|
|
7989
|
+
throw toLlmError(this.adapter.classifyError(error), {
|
|
7990
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
7991
|
+
provider: this.adapter.name,
|
|
7992
|
+
});
|
|
7708
7993
|
}
|
|
7709
7994
|
const delay = this.retryPolicy.getDelayForAttempt(attempt);
|
|
7710
7995
|
log.warn(`Stream request failed. Retrying in ${delay}ms...`);
|
|
@@ -9175,7 +9460,14 @@ class XaiProvider {
|
|
|
9175
9460
|
|
|
9176
9461
|
class Llm {
|
|
9177
9462
|
constructor(providerName = DEFAULT.PROVIDER.NAME, options = {}) {
|
|
9178
|
-
const { fallback, model } = options;
|
|
9463
|
+
const { fallback: fallbackOption, model: modelOption } = options;
|
|
9464
|
+
// A `model` array is sugar for a preference-ordered fallback chain:
|
|
9465
|
+
// index 0 is primary, the rest become fallback entries (provider
|
|
9466
|
+
// auto-detected). Any explicit `fallback` is appended after the chain.
|
|
9467
|
+
const { fallback: modelFallback, model } = resolveModelChain(modelOption);
|
|
9468
|
+
const fallback = modelFallback.length || fallbackOption
|
|
9469
|
+
? [...modelFallback, ...(fallbackOption ?? [])]
|
|
9470
|
+
: undefined;
|
|
9179
9471
|
let finalProvider = providerName;
|
|
9180
9472
|
let finalModel = model;
|
|
9181
9473
|
// Legacy: accept "gemini" but warn
|
|
@@ -9247,6 +9539,11 @@ class Llm {
|
|
|
9247
9539
|
}
|
|
9248
9540
|
}
|
|
9249
9541
|
async send(message, options) {
|
|
9542
|
+
if (options && Array.isArray(options.model)) {
|
|
9543
|
+
// `send` has no fallback machinery; use the primary model only
|
|
9544
|
+
const { model } = resolveModelChain(options.model);
|
|
9545
|
+
return this._llm.send(message, { ...options, model });
|
|
9546
|
+
}
|
|
9250
9547
|
return this._llm.send(message, options);
|
|
9251
9548
|
}
|
|
9252
9549
|
/**
|
|
@@ -9279,8 +9576,21 @@ class Llm {
|
|
|
9279
9576
|
if (!this._llm.operate) {
|
|
9280
9577
|
throw new NotImplementedError(`Provider ${this._provider} does not support operate method`);
|
|
9281
9578
|
}
|
|
9282
|
-
|
|
9283
|
-
|
|
9579
|
+
// A per-call `model` array becomes primary + a derived fallback chain
|
|
9580
|
+
// prepended to any resolved chain.
|
|
9581
|
+
const { fallback: modelFallback, model: perCallModel } = resolveModelChain(options.model);
|
|
9582
|
+
const resolvedOptions = {
|
|
9583
|
+
...options,
|
|
9584
|
+
model: perCallModel,
|
|
9585
|
+
};
|
|
9586
|
+
const fallbackChain = [
|
|
9587
|
+
...modelFallback,
|
|
9588
|
+
...this.resolveFallbackChain(resolvedOptions),
|
|
9589
|
+
];
|
|
9590
|
+
const optionsWithoutFallback = {
|
|
9591
|
+
...resolvedOptions,
|
|
9592
|
+
fallback: false,
|
|
9593
|
+
};
|
|
9284
9594
|
let lastError;
|
|
9285
9595
|
let attempts = 0;
|
|
9286
9596
|
// Try primary provider first
|
|
@@ -9329,33 +9639,45 @@ class Llm {
|
|
|
9329
9639
|
if (!this._llm.stream) {
|
|
9330
9640
|
throw new NotImplementedError(`Provider ${this._provider} does not support stream method`);
|
|
9331
9641
|
}
|
|
9332
|
-
|
|
9642
|
+
// `stream` has no instance-level fallback machinery; an array model uses
|
|
9643
|
+
// the primary and ignores the rest.
|
|
9644
|
+
const { model } = resolveModelChain(options.model);
|
|
9645
|
+
const streamOptions = { ...options, model };
|
|
9646
|
+
yield* this._llm.stream(input, streamOptions);
|
|
9333
9647
|
}
|
|
9334
9648
|
static async send(message, options) {
|
|
9335
9649
|
const { llm, apiKey, model, ...messageOptions } = options || {};
|
|
9336
|
-
|
|
9650
|
+
// A `model` array derives a fallback chain; `send` has no fallback
|
|
9651
|
+
// machinery, so the primary model is used and the chain is ignored.
|
|
9652
|
+
const { model: primaryModel } = resolveModelChain(model);
|
|
9653
|
+
const instance = new Llm(llm, { apiKey, model: primaryModel });
|
|
9337
9654
|
return instance.send(message, messageOptions);
|
|
9338
9655
|
}
|
|
9339
9656
|
static async operate(input, options) {
|
|
9340
9657
|
const { apiKey, fallback, llm, model, ...operateOptions } = options || {};
|
|
9658
|
+
// A `model` array becomes primary + derived fallback chain
|
|
9659
|
+
const { fallback: modelFallback, model: primaryModel } = resolveModelChain(model);
|
|
9341
9660
|
let finalLlm = llm;
|
|
9342
|
-
let finalModel =
|
|
9343
|
-
if (!llm &&
|
|
9344
|
-
const determined = determineModelProvider(
|
|
9661
|
+
let finalModel = primaryModel;
|
|
9662
|
+
if (!llm && primaryModel) {
|
|
9663
|
+
const determined = determineModelProvider(primaryModel);
|
|
9345
9664
|
if (determined.provider) {
|
|
9346
9665
|
finalLlm = determined.provider;
|
|
9347
9666
|
}
|
|
9348
9667
|
}
|
|
9349
|
-
else if (llm &&
|
|
9668
|
+
else if (llm && primaryModel) {
|
|
9350
9669
|
// When both llm and model are provided, check if they conflict
|
|
9351
|
-
const determined = determineModelProvider(
|
|
9670
|
+
const determined = determineModelProvider(primaryModel);
|
|
9352
9671
|
if (determined.provider && determined.provider !== llm) {
|
|
9353
9672
|
// Don't pass the conflicting model to the constructor
|
|
9354
9673
|
finalModel = undefined;
|
|
9355
9674
|
}
|
|
9356
9675
|
}
|
|
9357
9676
|
// Resolve fallback for static method: pass to instance if array, pass to operate options if false
|
|
9358
|
-
const
|
|
9677
|
+
const explicitFallback = Array.isArray(fallback) ? fallback : [];
|
|
9678
|
+
const instanceFallback = modelFallback.length || explicitFallback.length
|
|
9679
|
+
? [...modelFallback, ...explicitFallback]
|
|
9680
|
+
: undefined;
|
|
9359
9681
|
const operateFallback = fallback === false ? false : undefined;
|
|
9360
9682
|
const instance = new Llm(finalLlm, {
|
|
9361
9683
|
apiKey,
|
|
@@ -9369,23 +9691,29 @@ class Llm {
|
|
|
9369
9691
|
}
|
|
9370
9692
|
static stream(input, options) {
|
|
9371
9693
|
const { llm, apiKey, model, ...streamOptions } = options || {};
|
|
9694
|
+
// A `model` array becomes primary + derived fallback chain
|
|
9695
|
+
const { fallback: modelFallback, model: primaryModel } = resolveModelChain(model);
|
|
9372
9696
|
let finalLlm = llm;
|
|
9373
|
-
let finalModel =
|
|
9374
|
-
if (!llm &&
|
|
9375
|
-
const determined = determineModelProvider(
|
|
9697
|
+
let finalModel = primaryModel;
|
|
9698
|
+
if (!llm && primaryModel) {
|
|
9699
|
+
const determined = determineModelProvider(primaryModel);
|
|
9376
9700
|
if (determined.provider) {
|
|
9377
9701
|
finalLlm = determined.provider;
|
|
9378
9702
|
}
|
|
9379
9703
|
}
|
|
9380
|
-
else if (llm &&
|
|
9704
|
+
else if (llm && primaryModel) {
|
|
9381
9705
|
// When both llm and model are provided, check if they conflict
|
|
9382
|
-
const determined = determineModelProvider(
|
|
9706
|
+
const determined = determineModelProvider(primaryModel);
|
|
9383
9707
|
if (determined.provider && determined.provider !== llm) {
|
|
9384
9708
|
// Don't pass the conflicting model to the constructor
|
|
9385
9709
|
finalModel = undefined;
|
|
9386
9710
|
}
|
|
9387
9711
|
}
|
|
9388
|
-
const instance = new Llm(finalLlm, {
|
|
9712
|
+
const instance = new Llm(finalLlm, {
|
|
9713
|
+
apiKey,
|
|
9714
|
+
fallback: modelFallback.length ? modelFallback : undefined,
|
|
9715
|
+
model: finalModel,
|
|
9716
|
+
});
|
|
9389
9717
|
return instance.stream(input, streamOptions);
|
|
9390
9718
|
}
|
|
9391
9719
|
}
|
|
@@ -9683,5 +10011,5 @@ const toolkit = new JaypieToolkit(tools);
|
|
|
9683
10011
|
[LlmMessageRole.Developer]: "user",
|
|
9684
10012
|
});
|
|
9685
10013
|
|
|
9686
|
-
export { BedrockProvider, GoogleProvider as GeminiProvider, GoogleProvider, JaypieToolkit, constants as LLM, Llm, LlmMessageRole, LlmMessageType, LlmProgressEventType, LlmStreamChunkType, OpenRouterProvider, Toolkit, XaiProvider, extractReasoning, isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, jsonSchemaToNaturalSchema, naturalSchemaToJsonSchema, toolkit, tools };
|
|
10014
|
+
export { BedrockProvider, ErrorCategory, 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 };
|
|
9687
10015
|
//# sourceMappingURL=index.js.map
|