refine ui further
This commit is contained in:
@@ -1294,26 +1294,59 @@ It's better to have 3 complete files than 10 incomplete files.`
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await streamText(streamOptions);
|
||||
} catch (streamError) {
|
||||
console.error('[generate-ai-code-stream] Error calling streamText:', streamError);
|
||||
|
||||
// Send specific error for debugging
|
||||
await sendProgress({
|
||||
type: 'error',
|
||||
message: `Failed to initialize ${isGoogle ? 'Gemini' : isAnthropic ? 'Claude' : isOpenAI ? 'GPT-5' : 'Groq'} streaming: ${(streamError as Error).message}`
|
||||
});
|
||||
|
||||
// If this is a Google model error, provide helpful info
|
||||
if (isGoogle) {
|
||||
await sendProgress({
|
||||
type: 'info',
|
||||
message: 'Tip: Make sure your GEMINI_API_KEY is set correctly and has proper permissions.'
|
||||
});
|
||||
let retryCount = 0;
|
||||
const maxRetries = 2;
|
||||
|
||||
while (retryCount <= maxRetries) {
|
||||
try {
|
||||
result = await streamText(streamOptions);
|
||||
break; // Success, exit retry loop
|
||||
} catch (streamError: any) {
|
||||
console.error(`[generate-ai-code-stream] Error calling streamText (attempt ${retryCount + 1}/${maxRetries + 1}):`, streamError);
|
||||
|
||||
// Check if this is a Groq service unavailable error
|
||||
const isGroqServiceError = isKimiGroq && streamError.message?.includes('Service unavailable');
|
||||
const isRetryableError = streamError.message?.includes('Service unavailable') ||
|
||||
streamError.message?.includes('rate limit') ||
|
||||
streamError.message?.includes('timeout');
|
||||
|
||||
if (retryCount < maxRetries && isRetryableError) {
|
||||
retryCount++;
|
||||
console.log(`[generate-ai-code-stream] Retrying in ${retryCount * 2} seconds...`);
|
||||
|
||||
// Send progress update about retry
|
||||
await sendProgress({
|
||||
type: 'info',
|
||||
message: `Service temporarily unavailable, retrying (attempt ${retryCount + 1}/${maxRetries + 1})...`
|
||||
});
|
||||
|
||||
// Wait before retry with exponential backoff
|
||||
await new Promise(resolve => setTimeout(resolve, retryCount * 2000));
|
||||
|
||||
// If Groq fails, try switching to a fallback model
|
||||
if (isGroqServiceError && retryCount === maxRetries) {
|
||||
console.log('[generate-ai-code-stream] Groq service unavailable, falling back to GPT-4');
|
||||
streamOptions.model = openai('gpt-4-turbo');
|
||||
actualModel = 'gpt-4-turbo';
|
||||
}
|
||||
} else {
|
||||
// Final error, send to user
|
||||
await sendProgress({
|
||||
type: 'error',
|
||||
message: `Failed to initialize ${isGoogle ? 'Gemini' : isAnthropic ? 'Claude' : isOpenAI ? 'GPT-5' : isKimiGroq ? 'Kimi (Groq)' : 'Groq'} streaming: ${streamError.message}`
|
||||
});
|
||||
|
||||
// If this is a Google model error, provide helpful info
|
||||
if (isGoogle) {
|
||||
await sendProgress({
|
||||
type: 'info',
|
||||
message: 'Tip: Make sure your GEMINI_API_KEY is set correctly and has proper permissions.'
|
||||
});
|
||||
}
|
||||
|
||||
throw streamError;
|
||||
}
|
||||
}
|
||||
|
||||
throw streamError;
|
||||
}
|
||||
|
||||
// Stream the response and parse in real-time
|
||||
|
||||
@@ -21,8 +21,11 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
const app = new FirecrawlApp({ apiKey });
|
||||
|
||||
// Use Firecrawl SDK to capture screenshot with the latest API
|
||||
const scrapeResult = await app.scrapeUrl(url, {
|
||||
console.log('[scrape-screenshot] Attempting to capture screenshot for:', url);
|
||||
console.log('[scrape-screenshot] Using Firecrawl API key:', apiKey ? 'Present' : 'Missing');
|
||||
|
||||
// Use the new v4 scrape method (not scrapeUrl)
|
||||
const scrapeResult = await app.scrape(url, {
|
||||
formats: ['screenshot'], // Request screenshot format
|
||||
waitFor: 3000, // Wait for page to fully load
|
||||
timeout: 30000,
|
||||
@@ -35,7 +38,12 @@ export async function POST(req: NextRequest) {
|
||||
]
|
||||
});
|
||||
|
||||
console.log('[scrape-screenshot] Scrape result success:', scrapeResult.success);
|
||||
console.log('[scrape-screenshot] Scrape result data:', scrapeResult.data ? Object.keys(scrapeResult.data) : 'No data');
|
||||
|
||||
if (!scrapeResult.success) {
|
||||
console.error('[scrape-screenshot] Firecrawl API error:', scrapeResult.error);
|
||||
console.error('[scrape-screenshot] Full scrapeResult:', JSON.stringify(scrapeResult, null, 2));
|
||||
throw new Error(scrapeResult.error || 'Failed to capture screenshot');
|
||||
}
|
||||
|
||||
@@ -50,9 +58,22 @@ export async function POST(req: NextRequest) {
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Screenshot capture error:', error);
|
||||
console.error('[scrape-screenshot] Screenshot capture error:', error);
|
||||
console.error('[scrape-screenshot] Error stack:', error.stack);
|
||||
|
||||
// Provide fallback response for development
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.warn('[scrape-screenshot] Returning placeholder screenshot for development');
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
screenshot: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
|
||||
metadata: { error: 'Screenshot capture failed, using placeholder' }
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
error: error.message || 'Failed to capture screenshot'
|
||||
error: error.message || 'Failed to capture screenshot',
|
||||
details: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export async function POST(request: NextRequest) {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
url,
|
||||
formats: ['markdown', 'html'],
|
||||
formats: ['markdown', 'html', 'screenshot'],
|
||||
waitFor: 3000,
|
||||
timeout: 30000,
|
||||
blockAds: true,
|
||||
@@ -52,6 +52,10 @@ export async function POST(request: NextRequest) {
|
||||
{
|
||||
type: 'wait',
|
||||
milliseconds: 2000
|
||||
},
|
||||
{
|
||||
type: 'screenshot',
|
||||
fullPage: false // Just visible viewport for performance
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -68,7 +72,10 @@ export async function POST(request: NextRequest) {
|
||||
throw new Error('Failed to scrape content');
|
||||
}
|
||||
|
||||
const { markdown, html, metadata } = data.data;
|
||||
const { markdown, html, metadata, screenshot, actions } = data.data;
|
||||
|
||||
// Get screenshot from either direct field or actions result
|
||||
const screenshotUrl = screenshot || actions?.screenshots?.[0] || null;
|
||||
|
||||
// Sanitize the markdown content
|
||||
const sanitizedMarkdown = sanitizeQuotes(markdown || '');
|
||||
@@ -91,11 +98,13 @@ ${sanitizedMarkdown}
|
||||
success: true,
|
||||
url,
|
||||
content: formattedContent,
|
||||
screenshot: screenshotUrl,
|
||||
structured: {
|
||||
title: sanitizeQuotes(title),
|
||||
description: sanitizeQuotes(description),
|
||||
content: sanitizedMarkdown,
|
||||
url
|
||||
url,
|
||||
screenshot: screenshotUrl
|
||||
},
|
||||
metadata: {
|
||||
scraper: 'firecrawl-enhanced',
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// Scrape the website using the latest SDK patterns
|
||||
// Include screenshot if requested in formats
|
||||
const scrapeResult = await app.scrapeUrl(url, {
|
||||
const scrapeResult = await app.scrape(url, {
|
||||
formats: formats,
|
||||
onlyMainContent: options.onlyMainContent !== false, // Default to true for cleaner content
|
||||
waitFor: options.waitFor || 2000, // Wait for dynamic content
|
||||
|
||||
Reference in New Issue
Block a user