From 6c82b80ca5ea19a80e19899bce7e6652b922b905 Mon Sep 17 00:00:00 2001 From: Han Xiao Date: Fri, 7 Feb 2025 10:02:00 +0800 Subject: [PATCH] fix: #40 --- src/tools/jinaSearch.ts | 51 ++++++++++++++++++++++++++--------- src/tools/read.ts | 60 ++++++++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/tools/jinaSearch.ts b/src/tools/jinaSearch.ts index ded15f3..fdcc570 100644 --- a/src/tools/jinaSearch.ts +++ b/src/tools/jinaSearch.ts @@ -1,8 +1,7 @@ import https from 'https'; import { TokenTracker } from "../utils/token-tracker"; - import { SearchResponse } from '../types'; -import {JINA_API_KEY} from "../config"; +import { JINA_API_KEY } from "../config"; export function jinaSearch(query: string, tracker?: TokenTracker): Promise<{ response: SearchResponse, tokens: number }> { return new Promise((resolve, reject) => { @@ -25,17 +24,33 @@ export function jinaSearch(query: string, tracker?: TokenTracker): Promise<{ res const req = https.request(options, (res) => { let responseData = ''; - res.on('data', (chunk) => responseData += chunk); - res.on('end', () => { - const response = JSON.parse(responseData) as SearchResponse; - if (!query.trim()) { - reject(new Error('Query cannot be empty')); + res.on('data', (chunk) => responseData += chunk); + + res.on('end', () => { + // Check HTTP status code first + if (res.statusCode && res.statusCode >= 400) { + try { + // Try to parse error message from response if available + const errorResponse = JSON.parse(responseData); + if (res.statusCode === 402) { + reject(new Error(errorResponse.readableMessage || 'Insufficient balance')); + return; + } + reject(new Error(errorResponse.readableMessage || `HTTP Error ${res.statusCode}`)); + } catch { + // If parsing fails, just return the status code + reject(new Error(`HTTP Error ${res.statusCode}`)); + } return; } - if (response.code === 402) { - reject(new Error(response.readableMessage || 'Insufficient balance')); + // Only parse JSON for successful responses + let response: SearchResponse; + try { + response = JSON.parse(responseData) as SearchResponse; + } catch (error: unknown) { + reject(new Error(`Failed to parse response: ${error instanceof Error ? error.message : 'Unknown error'}`)); return; } @@ -46,12 +61,24 @@ export function jinaSearch(query: string, tracker?: TokenTracker): Promise<{ res const totalTokens = response.data.reduce((sum, item) => sum + (item.usage?.tokens || 0), 0); console.log('Total URLs:', response.data.length); - (tracker || new TokenTracker()).trackUsage('search', totalTokens); + + const tokenTracker = tracker || new TokenTracker(); + tokenTracker.trackUsage('search', totalTokens); + resolve({ response, tokens: totalTokens }); }); }); - req.on('error', reject); + // Add timeout handling + req.setTimeout(30000, () => { + req.destroy(); + reject(new Error('Request timed out')); + }); + + req.on('error', (error) => { + reject(new Error(`Request failed: ${error.message}`)); + }); + req.end(); }); -} +} \ No newline at end of file diff --git a/src/tools/read.ts b/src/tools/read.ts index ac8723e..2f9c28b 100644 --- a/src/tools/read.ts +++ b/src/tools/read.ts @@ -1,12 +1,16 @@ import https from 'https'; import { TokenTracker } from "../utils/token-tracker"; - import { ReadResponse } from '../types'; -import {JINA_API_KEY} from "../config"; +import { JINA_API_KEY } from "../config"; export function readUrl(url: string, tracker?: TokenTracker): Promise<{ response: ReadResponse, tokens: number }> { return new Promise((resolve, reject) => { - const data = JSON.stringify({url}); + if (!url.trim()) { + reject(new Error('URL cannot be empty')); + return; + } + + const data = JSON.stringify({ url }); const options = { hostname: 'r.jina.ai', @@ -25,13 +29,33 @@ export function readUrl(url: string, tracker?: TokenTracker): Promise<{ response const req = https.request(options, (res) => { let responseData = ''; - res.on('data', (chunk) => responseData += chunk); - res.on('end', () => { - const response = JSON.parse(responseData) as ReadResponse; - // console.log('Raw read response:', response); - if (response.code === 402) { - reject(new Error(response.readableMessage || 'Insufficient balance')); + res.on('data', (chunk) => responseData += chunk); + + res.on('end', () => { + // Check HTTP status code first + if (res.statusCode && res.statusCode >= 400) { + try { + // Try to parse error message from response if available + const errorResponse = JSON.parse(responseData); + if (res.statusCode === 402) { + reject(new Error(errorResponse.readableMessage || 'Insufficient balance')); + return; + } + reject(new Error(errorResponse.readableMessage || `HTTP Error ${res.statusCode}`)); + } catch (error: unknown) { + // If parsing fails, just return the status code + reject(new Error(`HTTP Error ${res.statusCode}`)); + } + return; + } + + // Only parse JSON for successful responses + let response: ReadResponse; + try { + response = JSON.parse(responseData) as ReadResponse; + } catch (error: unknown) { + reject(new Error(`Failed to parse response: ${error instanceof Error ? error.message : 'Unknown error'}`)); return; } @@ -45,14 +69,26 @@ export function readUrl(url: string, tracker?: TokenTracker): Promise<{ response url: response.data.url, tokens: response.data.usage?.tokens || 0 }); + const tokens = response.data.usage?.tokens || 0; - (tracker || new TokenTracker()).trackUsage('read', tokens); + const tokenTracker = tracker || new TokenTracker(); + tokenTracker.trackUsage('read', tokens); + resolve({ response, tokens }); }); }); - req.on('error', reject); + // Add timeout handling + req.setTimeout(30000, () => { + req.destroy(); + reject(new Error('Request timed out')); + }); + + req.on('error', (error: Error) => { + reject(new Error(`Request failed: ${error.message}`)); + }); + req.write(data); req.end(); }); -} +} \ No newline at end of file