Spaces:
Running
Running
refactor: Stream raw AI response and signal type via header
Browse files
server.js
CHANGED
|
@@ -467,6 +467,13 @@ ONLY output the changes in this format. Do NOT output the full HTML file again.`
|
|
| 467 |
let completeResponse = "";
|
| 468 |
|
| 469 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
const chatCompletion = client.chatCompletionStream({
|
| 471 |
model: MODEL_ID,
|
| 472 |
provider: "fireworks-ai", // Ensure provider is correct if needed
|
|
@@ -475,70 +482,27 @@ ONLY output the changes in this format. Do NOT output the full HTML file again.`
|
|
| 475 |
// temperature: 0.7, // Adjust temperature if needed
|
| 476 |
});
|
| 477 |
|
| 478 |
-
// ---
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
}
|
| 486 |
-
// Optional: Add a timeout or check response length to prevent infinite loops
|
| 487 |
-
if (completeResponse.length > 50000) { // Example limit
|
| 488 |
-
console.error("AI response exceeded length limit during accumulation.");
|
| 489 |
-
throw new Error("AI response too long during accumulation.");
|
| 490 |
-
}
|
| 491 |
-
}
|
| 492 |
-
|
| 493 |
-
// Check if the response seems truncated (didn't finish properly)
|
| 494 |
-
// This is heuristic - might need refinement
|
| 495 |
-
if (!chatCompletion?.controller?.signal?.aborted && !completeResponse.trim()) {
|
| 496 |
-
console.warn("AI stream finished but response is empty.");
|
| 497 |
-
// Return original HTML as maybe no changes were needed or AI failed silently.
|
| 498 |
-
return res.status(200).type('text/html').send(html);
|
| 499 |
-
}
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
console.log("--- AI Raw Diff Response ---");
|
| 503 |
-
console.log(completeResponse);
|
| 504 |
-
console.log("--------------------------");
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
// Apply the diffs
|
| 508 |
-
console.log("[Diff Apply] Attempting to apply diffs...");
|
| 509 |
-
const modifiedHtml = applyDiffs(html, completeResponse);
|
| 510 |
-
console.log("[Diff Apply] Diffs applied successfully.");
|
| 511 |
-
res.status(200).type('text/html').send(modifiedHtml); // Send the fully modified HTML
|
| 512 |
-
|
| 513 |
-
} else {
|
| 514 |
-
// **Stream response directly (Initial Request)**
|
| 515 |
-
console.log("[AI Request] Starting direct stream for initial request.");
|
| 516 |
-
res.setHeader("Content-Type", "text/html"); // Send as HTML
|
| 517 |
-
res.setHeader("Cache-Control", "no-cache");
|
| 518 |
-
res.setHeader("Connection", "keep-alive");
|
| 519 |
-
|
| 520 |
-
for await (const value of chatCompletion) {
|
| 521 |
-
const chunk = value.choices[0]?.delta?.content;
|
| 522 |
-
if (chunk) {
|
| 523 |
-
res.write(chunk);
|
| 524 |
-
completeResponse += chunk; // Still useful for checking completion
|
| 525 |
-
}
|
| 526 |
}
|
|
|
|
| 527 |
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
console.log("[AI Request] Stream finished successfully.");
|
| 534 |
-
}
|
| 535 |
|
| 536 |
-
|
| 537 |
-
}
|
| 538 |
|
| 539 |
} catch (error) {
|
| 540 |
-
console.error("Error during AI interaction or diff application
|
| 541 |
-
// If we haven't sent
|
| 542 |
if (!res.headersSent) {
|
| 543 |
// Check if it's an AbortError which might happen if the client disconnects
|
| 544 |
if (error.name === 'AbortError') {
|
|
@@ -549,14 +513,14 @@ ONLY output the changes in this format. Do NOT output the full HTML file again.`
|
|
| 549 |
res.status(500).send({
|
| 550 |
ok: false,
|
| 551 |
// Provide a more user-friendly message, but keep details for logs
|
|
|
|
| 552 |
message: `Error processing AI request: ${error.message}. You might need to start a new conversation by refreshing the page.`,
|
| 553 |
});
|
| 554 |
-
} else if (!
|
| 555 |
// If streaming failed mid-stream and stream hasn't been ended yet
|
| 556 |
-
console.error("Error occurred mid-stream.");
|
| 557 |
res.end(); // End the stream abruptly if error occurs during streaming
|
| 558 |
}
|
| 559 |
-
// If diff application failed, error was already sent by applyDiffs throwing.
|
| 560 |
// If streaming failed *after* res.end() was called (unlikely but possible), do nothing more.
|
| 561 |
}
|
| 562 |
});
|
|
|
|
| 467 |
let completeResponse = "";
|
| 468 |
|
| 469 |
try {
|
| 470 |
+
// Set response type header *before* starting the stream
|
| 471 |
+
res.setHeader("Content-Type", "text/plain; charset=utf-8"); // Stream raw text
|
| 472 |
+
res.setHeader("Cache-Control", "no-cache");
|
| 473 |
+
res.setHeader("Connection", "keep-alive");
|
| 474 |
+
res.setHeader("X-Response-Type", isFollowUp ? "diff" : "full"); // Signal type to client
|
| 475 |
+
console.log(`[AI Request] Set X-Response-Type: ${isFollowUp ? 'diff' : 'full'}`);
|
| 476 |
+
|
| 477 |
const chatCompletion = client.chatCompletionStream({
|
| 478 |
model: MODEL_ID,
|
| 479 |
provider: "fireworks-ai", // Ensure provider is correct if needed
|
|
|
|
| 482 |
// temperature: 0.7, // Adjust temperature if needed
|
| 483 |
});
|
| 484 |
|
| 485 |
+
// --- Unified Streaming Logic ---
|
| 486 |
+
console.log("[AI Request] Starting stream to client...");
|
| 487 |
+
for await (const value of chatCompletion) {
|
| 488 |
+
const chunk = value.choices[0]?.delta?.content;
|
| 489 |
+
if (chunk) {
|
| 490 |
+
res.write(chunk); // Stream raw AI response chunk
|
| 491 |
+
completeResponse += chunk; // Accumulate for logging completion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
}
|
| 493 |
+
}
|
| 494 |
|
| 495 |
+
console.log("[AI Request] Stream finished.");
|
| 496 |
+
// Log the complete raw response for debugging if needed
|
| 497 |
+
// console.log("--- AI Raw Full Response ---");
|
| 498 |
+
// console.log(completeResponse);
|
| 499 |
+
// console.log("--------------------------");
|
|
|
|
|
|
|
| 500 |
|
| 501 |
+
res.end(); // End the stream
|
|
|
|
| 502 |
|
| 503 |
} catch (error) {
|
| 504 |
+
console.error("Error during AI interaction:", error); // Removed "or diff application"
|
| 505 |
+
// If we haven't sent headers/started streaming yet
|
| 506 |
if (!res.headersSent) {
|
| 507 |
// Check if it's an AbortError which might happen if the client disconnects
|
| 508 |
if (error.name === 'AbortError') {
|
|
|
|
| 513 |
res.status(500).send({
|
| 514 |
ok: false,
|
| 515 |
// Provide a more user-friendly message, but keep details for logs
|
| 516 |
+
// Provide a more user-friendly message, but keep details for logs
|
| 517 |
message: `Error processing AI request: ${error.message}. You might need to start a new conversation by refreshing the page.`,
|
| 518 |
});
|
| 519 |
+
} else if (!res.writableEnded) {
|
| 520 |
// If streaming failed mid-stream and stream hasn't been ended yet
|
| 521 |
+
console.error("Error occurred mid-stream. Ending response.");
|
| 522 |
res.end(); // End the stream abruptly if error occurs during streaming
|
| 523 |
}
|
|
|
|
| 524 |
// If streaming failed *after* res.end() was called (unlikely but possible), do nothing more.
|
| 525 |
}
|
| 526 |
});
|