ClauseGuard / web /app /api /redline /route.ts
gaurv007's picture
🔧 v4.3: Web app deep audit — 12 bugs fixed (4 critical) (#6)
bf51166
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";
/**
* FIX v4.3: Redline route now works through the Gradio Space directly.
* The old code pointed to a non-existent FastAPI Space (gaurv007-clauseguard-api.hf.space).
* Since redlining is already part of the analyze pipeline (returned in analysis results),
* this endpoint is primarily for re-running redlines on existing text.
*/
const GRADIO_URL = process.env.CLAUSEGUARD_GRADIO_URL || "https://gaurv007-clauseguard.hf.space";
const API_URL = process.env.CLAUSEGUARD_API_URL || "";
export async function POST(req: NextRequest) {
try {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized. Please log in." }, { status: 401 });
}
const body = await req.json();
const { session_id, text, use_llm } = body;
if (!session_id && !text) {
return NextResponse.json(
{ error: "Provide session_id or text" },
{ status: 400 }
);
}
// Try FastAPI backend first (if configured and available)
if (API_URL) {
try {
const response = await fetch(`${API_URL}/api/redline`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_id, text, use_llm: use_llm ?? true }),
});
if (response.ok) {
const result = await response.json();
return NextResponse.json(result);
}
} catch {
// Fall through to Gradio approach
}
}
// Fallback: If text is provided, run full analysis via Gradio (includes redlines)
if (text) {
if (text.trim().length < 50) {
return NextResponse.json({ error: "Text too short (min 50 chars)" }, { status: 400 });
}
const submitRes = await fetch(`${GRADIO_URL}/gradio_api/call/analyze`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ data: [text] }),
});
if (!submitRes.ok) {
throw new Error(`Gradio submit failed: ${submitRes.status}`);
}
const { event_id } = await submitRes.json();
if (!event_id) throw new Error("No event_id from Gradio");
let resultText = "";
let attempts = 0;
while (attempts < 90) {
const resultRes = await fetch(
`${GRADIO_URL}/gradio_api/call/analyze/${event_id}`,
{ headers: { Accept: "text/event-stream" } }
);
resultText = await resultRes.text();
if (resultText.includes("event: complete")) break;
if (resultText.includes("event: error")) throw new Error("Redline analysis failed");
await new Promise(r => setTimeout(r, 1000));
attempts++;
}
if (!resultText.includes("event: complete")) {
throw new Error("Analysis timed out");
}
// Parse the result to extract redlines from the JSON report
const completeIdx = resultText.indexOf("event: complete");
const dataIdx = resultText.indexOf("data: ", completeIdx);
if (dataIdx === -1) throw new Error("No data in response");
const dataStr = resultText.substring(dataIdx + 6).trim();
const gradioData = JSON.parse(dataStr);
// Download JSON report file
const jsonFileObj = gradioData[8];
if (jsonFileObj?.url) {
const jsonRes = await fetch(jsonFileObj.url);
if (jsonRes.ok) {
const analysisData = await jsonRes.json();
if (analysisData.redlines) {
return NextResponse.json({ redlines: analysisData.redlines, count: analysisData.redlines.length });
}
}
}
return NextResponse.json({ redlines: [], count: 0 });
}
// No FastAPI backend and only session_id provided (can't access Gradio sessions)
return NextResponse.json({
error: "Redline by session_id requires the FastAPI backend. Provide contract text instead, or use the analysis results which already include redline suggestions.",
}, { status: 400 });
} catch (error: any) {
console.error("Redline error:", error.message);
return NextResponse.json(
{ error: error.message || "Redlining failed" },
{ status: 500 }
);
}
}