call-ai 0.7.0-dev-preview-9 → 0.7.0-dev-preview-12
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 +119 -56
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -35,10 +35,26 @@ function callAI(prompt, options = {}) {
|
|
|
35
35
|
// Make the fetch request and handle errors before creating the generator
|
|
36
36
|
console.log(`[callAI:${PACKAGE_VERSION}] Making fetch request to: ${endpoint}`);
|
|
37
37
|
console.log(`[callAI:${PACKAGE_VERSION}] With model: ${model}`);
|
|
38
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Request headers:`, JSON.stringify(requestOptions.headers));
|
|
38
39
|
let response;
|
|
39
40
|
try {
|
|
40
41
|
response = await fetch(endpoint, requestOptions);
|
|
41
42
|
console.log(`[callAI:${PACKAGE_VERSION}] Fetch completed with status:`, response.status, response.statusText);
|
|
43
|
+
// Log all headers
|
|
44
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Response headers:`);
|
|
45
|
+
response.headers.forEach((value, name) => {
|
|
46
|
+
console.log(`[callAI:${PACKAGE_VERSION}] ${name}: ${value}`);
|
|
47
|
+
});
|
|
48
|
+
// Clone response for diagnostic purposes only
|
|
49
|
+
const diagnosticResponse = response.clone();
|
|
50
|
+
try {
|
|
51
|
+
// Try to get the response as text for debugging
|
|
52
|
+
const responseText = await diagnosticResponse.text();
|
|
53
|
+
console.log(`[callAI:${PACKAGE_VERSION}] First 500 chars of response body:`, responseText.substring(0, 500) + (responseText.length > 500 ? '...' : ''));
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Could not read response body for diagnostics:`, e);
|
|
57
|
+
}
|
|
42
58
|
}
|
|
43
59
|
catch (fetchError) {
|
|
44
60
|
console.error(`[callAI:${PACKAGE_VERSION}] Network error during fetch:`, fetchError);
|
|
@@ -47,63 +63,84 @@ function callAI(prompt, options = {}) {
|
|
|
47
63
|
// Explicitly check for HTTP error status and log extensively
|
|
48
64
|
console.log(`[callAI:${PACKAGE_VERSION}] Response.ok =`, response.ok);
|
|
49
65
|
console.log(`[callAI:${PACKAGE_VERSION}] Response.status =`, response.status);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Response.statusText =`, response.statusText);
|
|
67
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Response.type =`, response.type);
|
|
68
|
+
// Double check for content-type to see if there's a mismatch in error response handling
|
|
69
|
+
const contentType = response.headers.get('content-type') || '';
|
|
70
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Content-Type =`, contentType);
|
|
71
|
+
// Browser-compatible error handling - must check BOTH status code AND content-type
|
|
72
|
+
// Some browsers will report status 200 for SSE streams even when server returns 400
|
|
73
|
+
const hasHttpError = !response.ok || response.status >= 400;
|
|
74
|
+
const hasJsonError = contentType.includes('application/json');
|
|
75
|
+
if (hasHttpError || hasJsonError) {
|
|
76
|
+
console.log(`[callAI:${PACKAGE_VERSION}] ⚠️ Error detected - HTTP Status: ${response.status}, Content-Type: ${contentType}`);
|
|
77
|
+
// Handle the error with fallback model if appropriate
|
|
78
|
+
if (!options.skipRetry) {
|
|
79
|
+
const clonedResponse = response.clone();
|
|
80
|
+
let isInvalidModel = false;
|
|
81
|
+
try {
|
|
82
|
+
// Check if this is an invalid model error
|
|
83
|
+
const modelCheckResult = await checkForInvalidModelError(clonedResponse, model, false, options.skipRetry);
|
|
84
|
+
isInvalidModel = modelCheckResult.isInvalidModel;
|
|
85
|
+
if (isInvalidModel) {
|
|
86
|
+
if (options.debug) {
|
|
87
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Retrying with fallback model: ${FALLBACK_MODEL}`);
|
|
88
|
+
}
|
|
89
|
+
// Retry with fallback model
|
|
90
|
+
return await callAI(prompt, { ...options, model: FALLBACK_MODEL });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (modelCheckError) {
|
|
94
|
+
console.error(`[callAI:${PACKAGE_VERSION}] Error during model check:`, modelCheckError);
|
|
95
|
+
// Continue with normal error handling
|
|
73
96
|
}
|
|
74
|
-
// Retry with fallback model - it will return a promise
|
|
75
|
-
const result = await callAI(prompt, { ...options, model: FALLBACK_MODEL });
|
|
76
|
-
return result;
|
|
77
97
|
}
|
|
78
|
-
//
|
|
79
|
-
console.log(`[callAI:${PACKAGE_VERSION}] Reading error response body...`);
|
|
80
|
-
let errorText = "";
|
|
98
|
+
// Extract error details from response
|
|
81
99
|
try {
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
// Try to get error details from the response body
|
|
101
|
+
const errorBody = await response.text();
|
|
102
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Error body:`, errorBody);
|
|
103
|
+
try {
|
|
104
|
+
// Try to parse JSON error
|
|
105
|
+
const errorJson = JSON.parse(errorBody);
|
|
106
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Parsed error:`, errorJson);
|
|
107
|
+
// Extract message from OpenRouter error format
|
|
108
|
+
const errorMessage = ((errorJson.error && typeof errorJson.error === 'object') ?
|
|
109
|
+
errorJson.error.message :
|
|
110
|
+
errorJson.error || errorJson.message ||
|
|
111
|
+
`API returned ${response.status}: ${response.statusText}`);
|
|
112
|
+
// Create error with standard format
|
|
113
|
+
const error = new Error(errorMessage);
|
|
114
|
+
// Add useful metadata
|
|
115
|
+
error.status = response.status;
|
|
116
|
+
error.statusText = response.statusText;
|
|
117
|
+
error.details = errorJson;
|
|
118
|
+
error.contentType = contentType;
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
catch (jsonError) {
|
|
122
|
+
// If JSON parsing fails, throw a simpler error
|
|
123
|
+
console.log(`[callAI:${PACKAGE_VERSION}] JSON parse error:`, jsonError);
|
|
124
|
+
const error = new Error(`API error: ${response.status} ${response.statusText}`);
|
|
125
|
+
error.status = response.status;
|
|
126
|
+
error.statusText = response.statusText;
|
|
127
|
+
error.details = errorBody;
|
|
128
|
+
error.contentType = contentType;
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
84
131
|
}
|
|
85
|
-
catch (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
132
|
+
catch (responseError) {
|
|
133
|
+
if (responseError instanceof Error) {
|
|
134
|
+
// Re-throw if it's already properly formatted
|
|
135
|
+
throw responseError;
|
|
136
|
+
}
|
|
137
|
+
// Fallback error
|
|
138
|
+
const error = new Error(`API returned ${response.status}: ${response.statusText}`);
|
|
139
|
+
error.status = response.status;
|
|
140
|
+
error.statusText = response.statusText;
|
|
141
|
+
error.contentType = contentType;
|
|
142
|
+
throw error;
|
|
89
143
|
}
|
|
90
|
-
// Create a detailed error with status information
|
|
91
|
-
console.log(`[callAI:${PACKAGE_VERSION}] Creating error object with status ${response.status}`);
|
|
92
|
-
const errorMessage = `API returned error ${response.status}: ${response.statusText}`;
|
|
93
|
-
const error = new Error(errorMessage);
|
|
94
|
-
// Add extra properties for more context
|
|
95
|
-
error.status = response.status;
|
|
96
|
-
error.statusText = response.statusText;
|
|
97
|
-
error.details = errorText;
|
|
98
|
-
// Ensure this error is thrown and caught properly in the Promise chain
|
|
99
|
-
console.error(`[callAI:${PACKAGE_VERSION}] THROWING API ERROR:`, {
|
|
100
|
-
message: errorMessage,
|
|
101
|
-
status: response.status,
|
|
102
|
-
statusText: response.statusText,
|
|
103
|
-
details: errorText
|
|
104
|
-
});
|
|
105
|
-
// This MUST throw the error from the promise
|
|
106
|
-
return Promise.reject(error);
|
|
107
144
|
}
|
|
108
145
|
// Only if response is OK, create and return the streaming generator
|
|
109
146
|
console.log(`[callAI:${PACKAGE_VERSION}] Response OK, creating streaming generator`);
|
|
@@ -345,7 +382,7 @@ async function callAINonStreaming(prompt, options = {}, isRetry = false) {
|
|
|
345
382
|
const { endpoint, requestOptions, model, schemaStrategy } = prepareRequestParams(prompt, options);
|
|
346
383
|
const response = await fetch(endpoint, requestOptions);
|
|
347
384
|
// Handle HTTP errors, with potential fallback for invalid model
|
|
348
|
-
if (!response.ok) {
|
|
385
|
+
if (!response.ok || response.status >= 400) {
|
|
349
386
|
const { isInvalidModel } = await checkForInvalidModelError(response, model, isRetry, options.skipRetry);
|
|
350
387
|
if (isInvalidModel) {
|
|
351
388
|
// Retry with fallback model
|
|
@@ -468,6 +505,10 @@ async function extractClaudeResponse(response) {
|
|
|
468
505
|
* return a 200 OK initially but then deliver error information in the stream.
|
|
469
506
|
*/
|
|
470
507
|
async function* createStreamingGenerator(response, options, schemaStrategy, model) {
|
|
508
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Starting streaming generator with model: ${model}`);
|
|
509
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Response status:`, response.status);
|
|
510
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Response type:`, response.type);
|
|
511
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Response Content-Type:`, response.headers.get('content-type'));
|
|
471
512
|
try {
|
|
472
513
|
// Handle streaming response
|
|
473
514
|
if (!response.body) {
|
|
@@ -481,16 +522,26 @@ async function* createStreamingGenerator(response, options, schemaStrategy, mode
|
|
|
481
522
|
while (true) {
|
|
482
523
|
const { done, value } = await reader.read();
|
|
483
524
|
if (done) {
|
|
525
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Stream done=true after ${chunkCount} chunks`);
|
|
484
526
|
if (options.debug) {
|
|
485
527
|
console.log(`[callAI-streaming:complete v${PACKAGE_VERSION}] Stream finished after ${chunkCount} chunks`);
|
|
486
528
|
}
|
|
487
529
|
break;
|
|
488
530
|
}
|
|
531
|
+
// Increment chunk counter before processing
|
|
532
|
+
chunkCount++;
|
|
489
533
|
const chunk = decoder.decode(value);
|
|
534
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Raw chunk #${chunkCount} (${chunk.length} bytes):`, chunk.length > 200 ? chunk.substring(0, 200) + '...' : chunk);
|
|
490
535
|
const lines = chunk.split("\n").filter((line) => line.trim() !== "");
|
|
536
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Chunk #${chunkCount} contains ${lines.length} non-empty lines`);
|
|
491
537
|
for (const line of lines) {
|
|
538
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Processing line:`, line.length > 100 ? line.substring(0, 100) + '...' : line);
|
|
492
539
|
if (line.startsWith("data: ")) {
|
|
493
|
-
|
|
540
|
+
let data = line.slice(6);
|
|
541
|
+
if (data === "[DONE]") {
|
|
542
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Received [DONE] marker`);
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
494
545
|
if (options.debug) {
|
|
495
546
|
console.log(`[callAI:raw] ${line}`);
|
|
496
547
|
}
|
|
@@ -502,11 +553,23 @@ async function* createStreamingGenerator(response, options, schemaStrategy, mode
|
|
|
502
553
|
try {
|
|
503
554
|
const jsonLine = line.replace("data: ", "");
|
|
504
555
|
if (!jsonLine.trim()) {
|
|
556
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Empty JSON line after data: prefix`);
|
|
505
557
|
continue;
|
|
506
558
|
}
|
|
507
|
-
|
|
559
|
+
console.log(`[callAI:${PACKAGE_VERSION}] JSON line (first 100 chars):`, jsonLine.length > 100 ? jsonLine.substring(0, 100) + '...' : jsonLine);
|
|
508
560
|
// Parse the JSON chunk
|
|
509
|
-
|
|
561
|
+
let json;
|
|
562
|
+
try {
|
|
563
|
+
json = JSON.parse(jsonLine);
|
|
564
|
+
console.log(`[callAI:${PACKAGE_VERSION}] Parsed JSON:`, JSON.stringify(json).length > 100 ?
|
|
565
|
+
JSON.stringify(json).substring(0, 100) + '...' :
|
|
566
|
+
JSON.stringify(json));
|
|
567
|
+
}
|
|
568
|
+
catch (parseError) {
|
|
569
|
+
console.error(`[callAI:${PACKAGE_VERSION}] JSON parse error:`, parseError);
|
|
570
|
+
console.error(`[callAI:${PACKAGE_VERSION}] Failed to parse:`, jsonLine);
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
510
573
|
// Enhanced error detection - check for BOTH error and json.error
|
|
511
574
|
// Some APIs return 200 OK but then deliver errors in the stream
|
|
512
575
|
if (json.error || (typeof json === 'object' && 'error' in json)) {
|
package/package.json
CHANGED