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.
Files changed (2) hide show
  1. package/dist/api.js +119 -56
  2. 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
- // Enhanced error handling with more debugging - MUST check !response.ok
51
- if (!response.ok) {
52
- console.log(`[callAI:${PACKAGE_VERSION}] Detected error response with status:`, response.status);
53
- if (options.debug) {
54
- console.error(`[callAI:${PACKAGE_VERSION}] HTTP Error:`, response.status, response.statusText, response.url);
55
- }
56
- // Check if this is an invalid model error
57
- console.log(`[callAI:${PACKAGE_VERSION}] Checking for invalid model error...`);
58
- let isInvalidModel = false;
59
- let errorData = null;
60
- try {
61
- const result = await checkForInvalidModelError(response.clone(), // Clone response since we'll need to read body twice
62
- model, false, options.skipRetry);
63
- isInvalidModel = result.isInvalidModel;
64
- errorData = result.errorData;
65
- console.log(`[callAI:${PACKAGE_VERSION}] Invalid model check result:`, isInvalidModel);
66
- }
67
- catch (checkError) {
68
- console.error(`[callAI:${PACKAGE_VERSION}] Error during invalid model check:`, checkError);
69
- }
70
- if (isInvalidModel && !options.skipRetry) {
71
- if (options.debug) {
72
- console.log(`[callAI:${PACKAGE_VERSION}] Retrying with fallback model: ${FALLBACK_MODEL}`);
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
- // Get full error text from body
79
- console.log(`[callAI:${PACKAGE_VERSION}] Reading error response body...`);
80
- let errorText = "";
98
+ // Extract error details from response
81
99
  try {
82
- errorText = await response.text();
83
- console.log(`[callAI:${PACKAGE_VERSION}] Error response body:`, errorText);
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 (error) {
86
- const textError = error;
87
- console.error(`[callAI:${PACKAGE_VERSION}] Error reading response body:`, textError);
88
- errorText = `Failed to read error details: ${textError.message || 'Unknown error'}`;
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
- // Simple debug logging of raw SSE events with no processing
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
- chunkCount++;
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
- const json = JSON.parse(jsonLine);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "call-ai",
3
- "version": "0.7.0-dev-preview-9",
3
+ "version": "0.7.0-dev-preview-12",
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",