call-ai 0.6.0 → 0.6.2
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/api.js +55 -2
- package/dist/types.d.ts +5 -0
- package/package.json +3 -2
package/dist/api.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.callAI = callAI;
|
|
4
4
|
const strategies_1 = require("./strategies");
|
|
5
|
+
// Default fallback model when the primary model fails or is unavailable
|
|
6
|
+
const FALLBACK_MODEL = "openrouter/auto";
|
|
5
7
|
/**
|
|
6
8
|
* Make an AI API call with the given options
|
|
7
9
|
* @param prompt User prompt as string or an array of message objects
|
|
@@ -60,6 +62,33 @@ function handleApiError(error, context) {
|
|
|
60
62
|
message: `Sorry, I couldn't process that request: ${String(error)}`,
|
|
61
63
|
});
|
|
62
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Helper to check if an error indicates invalid model and handle fallback
|
|
67
|
+
*/
|
|
68
|
+
async function checkForInvalidModelError(response, model, isRetry, skipRetry = false) {
|
|
69
|
+
// Skip retry immediately if skipRetry is true
|
|
70
|
+
if (skipRetry || response.status !== 400 || isRetry) {
|
|
71
|
+
return { isInvalidModel: false };
|
|
72
|
+
}
|
|
73
|
+
// Clone the response so we can read the body
|
|
74
|
+
const clonedResponse = response.clone();
|
|
75
|
+
try {
|
|
76
|
+
const errorData = await clonedResponse.json();
|
|
77
|
+
// Check if the error message indicates an invalid model
|
|
78
|
+
if (errorData.error &&
|
|
79
|
+
errorData.error.message &&
|
|
80
|
+
errorData.error.message.toLowerCase().includes("not a valid model")) {
|
|
81
|
+
console.warn(`Model ${model} not valid, retrying with ${FALLBACK_MODEL}`);
|
|
82
|
+
return { isInvalidModel: true };
|
|
83
|
+
}
|
|
84
|
+
return { isInvalidModel: false, errorData };
|
|
85
|
+
}
|
|
86
|
+
catch (parseError) {
|
|
87
|
+
// If we can't parse the response as JSON, continue with original error
|
|
88
|
+
console.error("Failed to parse error response:", parseError);
|
|
89
|
+
return { isInvalidModel: false };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
63
92
|
/**
|
|
64
93
|
* Prepare request parameters common to both streaming and non-streaming calls
|
|
65
94
|
*/
|
|
@@ -110,6 +139,8 @@ function prepareRequestParams(prompt, options) {
|
|
|
110
139
|
method: "POST",
|
|
111
140
|
headers: {
|
|
112
141
|
Authorization: `Bearer ${apiKey}`,
|
|
142
|
+
"HTTP-Referer": "https://vibes.diy",
|
|
143
|
+
"X-Title": "Vibes",
|
|
113
144
|
"Content-Type": "application/json",
|
|
114
145
|
},
|
|
115
146
|
body: JSON.stringify(requestParams),
|
|
@@ -119,10 +150,19 @@ function prepareRequestParams(prompt, options) {
|
|
|
119
150
|
/**
|
|
120
151
|
* Internal implementation for non-streaming API calls
|
|
121
152
|
*/
|
|
122
|
-
async function callAINonStreaming(prompt, options = {}) {
|
|
153
|
+
async function callAINonStreaming(prompt, options = {}, isRetry = false) {
|
|
123
154
|
try {
|
|
124
155
|
const { endpoint, requestOptions, model, schemaStrategy } = prepareRequestParams(prompt, options);
|
|
125
156
|
const response = await fetch(endpoint, requestOptions);
|
|
157
|
+
// Handle HTTP errors, with potential fallback for invalid model
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
const { isInvalidModel } = await checkForInvalidModelError(response, model, isRetry, options.skipRetry);
|
|
160
|
+
if (isInvalidModel) {
|
|
161
|
+
// Retry with fallback model
|
|
162
|
+
return callAINonStreaming(prompt, { ...options, model: FALLBACK_MODEL }, true);
|
|
163
|
+
}
|
|
164
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
165
|
+
}
|
|
126
166
|
let result;
|
|
127
167
|
// For Claude, use text() instead of json() to avoid potential hanging
|
|
128
168
|
if (/claude/i.test(model)) {
|
|
@@ -139,6 +179,14 @@ async function callAINonStreaming(prompt, options = {}) {
|
|
|
139
179
|
// Handle error responses
|
|
140
180
|
if (result.error) {
|
|
141
181
|
console.error("API returned an error:", result.error);
|
|
182
|
+
// If it's a model error and not already a retry, try with fallback
|
|
183
|
+
if (!isRetry &&
|
|
184
|
+
!options.skipRetry &&
|
|
185
|
+
result.error.message &&
|
|
186
|
+
result.error.message.toLowerCase().includes("not a valid model")) {
|
|
187
|
+
console.warn(`Model ${model} error, retrying with ${FALLBACK_MODEL}`);
|
|
188
|
+
return callAINonStreaming(prompt, { ...options, model: FALLBACK_MODEL }, true);
|
|
189
|
+
}
|
|
142
190
|
return JSON.stringify({
|
|
143
191
|
error: result.error,
|
|
144
192
|
message: result.error.message || "API returned an error",
|
|
@@ -220,11 +268,16 @@ async function extractClaudeResponse(response) {
|
|
|
220
268
|
/**
|
|
221
269
|
* Internal implementation for streaming API calls
|
|
222
270
|
*/
|
|
223
|
-
async function* callAIStreaming(prompt, options = {}) {
|
|
271
|
+
async function* callAIStreaming(prompt, options = {}, isRetry = false) {
|
|
224
272
|
try {
|
|
225
273
|
const { endpoint, requestOptions, model, schemaStrategy } = prepareRequestParams(prompt, { ...options, stream: true });
|
|
226
274
|
const response = await fetch(endpoint, requestOptions);
|
|
227
275
|
if (!response.ok) {
|
|
276
|
+
const { isInvalidModel } = await checkForInvalidModelError(response, model, isRetry, options.skipRetry);
|
|
277
|
+
if (isInvalidModel) {
|
|
278
|
+
// Retry with fallback model
|
|
279
|
+
return yield* callAIStreaming(prompt, { ...options, model: FALLBACK_MODEL }, true);
|
|
280
|
+
}
|
|
228
281
|
const errorText = await response.text();
|
|
229
282
|
console.error(`API Error: ${response.status} ${response.statusText}`, errorText);
|
|
230
283
|
throw new Error(`API returned error ${response.status}: ${response.statusText}`);
|
package/dist/types.d.ts
CHANGED
|
@@ -90,6 +90,11 @@ export interface CallAIOptions {
|
|
|
90
90
|
* Used for multimodal models that can generate images
|
|
91
91
|
*/
|
|
92
92
|
modalities?: string[];
|
|
93
|
+
/**
|
|
94
|
+
* Whether to skip retry with fallback model when model errors occur
|
|
95
|
+
* Useful in testing and cases where retries should be suppressed
|
|
96
|
+
*/
|
|
97
|
+
skipRetry?: boolean;
|
|
93
98
|
/**
|
|
94
99
|
* Any additional options to pass to the API
|
|
95
100
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "call-ai",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Lightweight library for making AI API calls with streaming support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"test:integration": "jest --testMatch=\"**/test/integration.test.ts\" --testPathIgnorePatterns=''",
|
|
48
48
|
"typecheck": "tsc --noEmit",
|
|
49
49
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
50
|
-
"coverage": "jest --coverage"
|
|
50
|
+
"coverage": "jest --coverage",
|
|
51
|
+
"check": "npm run typecheck && npm run format && npm run test"
|
|
51
52
|
}
|
|
52
53
|
}
|