File size: 4,421 Bytes
f782685
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";

const GRADIO_URL = process.env.CLAUSEGUARD_GRADIO_URL || "https://gaurv007-clauseguard.hf.space";

/**
 * FIX v4.1: The chat endpoint now properly documents its limitations.
 *
 * ARCHITECTURE NOTE:
 * The Gradio ChatInterface uses gr.State to store RAG embeddings per-session.
 * This state is NOT accessible via the Gradio API from an external caller —
 * each API call creates a new session with empty state.
 *
 * For the Next.js web app, chat should either:
 * 1. Use the FastAPI backend (/api/chat) which manages its own RAG sessions, OR
 * 2. Embed the Gradio Space in an iframe for direct interaction
 *
 * This route attempts to use the Gradio API as a best-effort fallback,
 * but will clearly communicate to the user if the session is unavailable.
 */
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 { message, history, session_id } = body;

    if (!message) {
      return NextResponse.json(
        { error: "message is required" },
        { status: 400 }
      );
    }

    // FIX v4.1: Input validation
    if (message.length > 2000) {
      return NextResponse.json(
        { error: "Message too long (max 2000 characters)" },
        { status: 400 }
      );
    }

    // Try the FastAPI backend first (it has proper RAG session management)
    const apiUrl = process.env.CLAUSEGUARD_API_URL || "";
    if (apiUrl && session_id) {
      try {
        const apiRes = await fetch(`${apiUrl}/api/chat`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ message, session_id, history: history || [] }),
        });
        if (apiRes.ok) {
          const data = await apiRes.json();
          return NextResponse.json({ response: data.response });
        }
      } catch {
        // Fall through to Gradio attempt
      }
    }

    // Fallback: Try the Gradio API
    const submitRes = await fetch(`${GRADIO_URL}/gradio_api/call/chat`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: [message] }),
    });

    if (!submitRes.ok) {
      const errText = await submitRes.text().catch(() => "");
      throw new Error(`Chat submit failed (${submitRes.status}): ${errText}`);
    }

    const { event_id } = await submitRes.json();
    if (!event_id) throw new Error("No event_id from Gradio chat");

    // Poll for result with timeout
    let resultText = "";
    let attempts = 0;
    const maxAttempts = 30;

    while (attempts < maxAttempts) {
      const resultRes = await fetch(
        `${GRADIO_URL}/gradio_api/call/chat/${event_id}`,
        { headers: { Accept: "text/event-stream" } }
      );

      if (!resultRes.ok) {
        throw new Error(`Chat result failed: ${resultRes.status}`);
      }

      resultText = await resultRes.text();

      if (resultText.includes("event: complete")) break;
      if (resultText.includes("event: error")) {
        const errMatch = resultText.match(/event:\s*error\s*\ndata:\s*(.+)/);
        if (errMatch) throw new Error(`Chat error: ${errMatch[1]}`);
        throw new Error("Chat error from backend");
      }

      await new Promise(r => setTimeout(r, 1000));
      attempts++;
    }

    // Find the complete event data
    const dataMatch = resultText.match(/event:\s*complete\s*\ndata:\s*(.+)/);
    if (!dataMatch) {
      return NextResponse.json({
        response: "⚠️ Chat is unavailable. The contract needs to be analyzed in the same session. " +
          "Please analyze a contract first in the Gradio Space, then use the chat tab there directly."
      });
    }

    const responseData = JSON.parse(dataMatch[1]);
    const responseText = typeof responseData === "string" ? responseData : responseData[0] || "";

    return NextResponse.json({ response: responseText });
  } catch (error: any) {
    console.error("Chat error:", error.message);
    return NextResponse.json(
      { error: error.message || "Chat failed. Make sure you analyzed a contract first." },
      { status: 500 }
    );
  }
}