Skip to content

Commit 0476a7e

Browse files
committed
Updates to validation
1 parent 28b6d4a commit 0476a7e

8 files changed

Lines changed: 1992 additions & 1407 deletions

File tree

next.config.ts

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,60 @@
11
import type { NextConfig } from "next";
22

3-
// Get backend URL from environment variables
3+
// Main backend URL (default: 8000)
44
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
5+
// Validation agent URL (default: 8096)
6+
const validateBackendUrl = process.env.NEXT_PUBLIC_VALIDATE_BACKEND_URL || 'http://localhost:8096';
57

68
console.log(`[next.config.ts] Backend URL: ${backendUrl}`);
9+
console.log(`[next.config.ts] Validate Backend URL: ${validateBackendUrl}`);
710
console.log(`[next.config.ts] Environment: ${process.env.NODE_ENV}`);
811

912
const nextConfig: NextConfig = {
10-
// Use Next.js server mode for full functionality
1113
images: {
1214
unoptimized: false,
13-
domains: ['localhost', '127.0.0.1'], // Add your OpenShift domain here when deploying
15+
domains: ['localhost', '127.0.0.1'], // Add your OpenShift domain here
1416
},
15-
16-
// Environment variables are automatically loaded from .env.local
17-
// No need to explicitly define them here since they're already NEXT_PUBLIC_*
18-
19-
// API rewrites to your backend
2017
async rewrites() {
21-
console.log(`[next.config.ts] Setting up API rewrites to: ${backendUrl}`);
22-
18+
console.log(`[next.config.ts] Setting up API rewrites:`);
2319
return [
24-
// File operations
20+
// File operations (main backend)
2521
{
2622
source: '/api/files/:path*',
2723
destination: `${backendUrl}/api/files/:path*`,
2824
},
29-
30-
// Agent endpoints
25+
// Chef agent (main backend)
3126
{
3227
source: '/api/chef/:path*',
3328
destination: `${backendUrl}/api/chef/:path*`,
3429
},
30+
// Validate agent (validation backend - 8096 by default)
3531
{
3632
source: '/api/validate/:path*',
37-
destination: `${backendUrl}/api/validate/:path*`,
33+
destination: `${validateBackendUrl}/api/validate/:path*`,
3834
},
35+
// Context agent (main backend)
3936
{
4037
source: '/api/context/:path*',
4138
destination: `${backendUrl}/api/context/:path*`,
4239
},
40+
// Generate agent (main backend)
4341
{
4442
source: '/api/generate/:path*',
4543
destination: `${backendUrl}/api/generate/:path*`,
4644
},
45+
// Admin (main backend)
4746
{
4847
source: '/api/admin/:path*',
4948
destination: `${backendUrl}/api/admin/:path*`,
5049
},
50+
// Vector DB (main backend)
5151
{
5252
source: '/api/vector-db/:path*',
5353
destination: `${backendUrl}/api/vector-db/:path*`,
5454
},
55-
56-
// Keep /api/auth/* for NextAuth - don't proxy these
55+
// (leave /api/auth/* alone for NextAuth or authentication services)
5756
];
5857
},
59-
60-
// Build configuration
6158
typescript: {
6259
ignoreBuildErrors: process.env.NODE_ENV === 'development',
6360
},
@@ -66,4 +63,4 @@ const nextConfig: NextConfig = {
6663
},
6764
};
6865

69-
export default nextConfig;
66+
export default nextConfig;
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
3+
const BACKEND_URL = process.env.NEXT_PUBLIC_VALIDATE_BACKEND_URL || "http://localhost:8000";
4+
5+
export async function POST(request: NextRequest) {
6+
console.log("🚨 USING ENHANCED ROUTE HANDLER - VERSION 2 🚨");
7+
8+
try {
9+
const body = await request.json();
10+
const { playbook_content, profile = 'production' } = body;
11+
12+
if (!playbook_content || !playbook_content.trim()) {
13+
return NextResponse.json(
14+
{ error: "No playbook content provided" },
15+
{ status: 400 }
16+
);
17+
}
18+
19+
// Use the correct working endpoint
20+
const streamingUrl = `${BACKEND_URL}/api/validate/playbook/stream`;
21+
console.log("🔗 Connecting to validation service:", streamingUrl);
22+
console.log("📤 Request payload:", { playbook_length: playbook_content.length, profile });
23+
24+
try {
25+
const response = await fetch(streamingUrl, {
26+
method: "POST",
27+
headers: {
28+
"Content-Type": "application/json",
29+
"Accept": "text/event-stream",
30+
},
31+
body: JSON.stringify({
32+
playbook_content,
33+
profile,
34+
}),
35+
// Add timeout to prevent hanging
36+
signal: AbortSignal.timeout(120000), // 2 minute timeout
37+
});
38+
39+
console.log("📡 Backend response status:", response.status);
40+
console.log("📡 Backend response headers:", Object.fromEntries(response.headers.entries()));
41+
42+
if (!response.ok) {
43+
const errorText = await response.text();
44+
console.error("❌ Backend validation error:", errorText);
45+
46+
return NextResponse.json({
47+
passed: false,
48+
summary: `Validation service error: ${response.status}`,
49+
issues: [],
50+
raw_output: errorText,
51+
error_message: `Backend validation error: ${response.status} - ${errorText}`,
52+
debug_info: {
53+
status: "error",
54+
error_code: response.status,
55+
playbook_length: playbook_content.length
56+
}
57+
}, { status: 200 });
58+
}
59+
60+
const contentType = response.headers.get("content-type");
61+
62+
// Handle direct JSON response (fallback)
63+
if (contentType?.includes("application/json")) {
64+
console.log("📊 Received JSON response from streaming endpoint");
65+
const data = await response.json();
66+
return NextResponse.json(data);
67+
}
68+
69+
// Handle streaming response - the main path
70+
if (contentType?.includes("text/event-stream") || contentType?.includes("text/plain")) {
71+
console.log("🌊 Handling streaming response...");
72+
73+
const stream = new ReadableStream({
74+
async start(controller) {
75+
const reader = response.body?.getReader();
76+
if (!reader) {
77+
controller.close();
78+
return;
79+
}
80+
81+
const decoder = new TextDecoder();
82+
let buffer = '';
83+
84+
try {
85+
while (true) {
86+
const { done, value } = await reader.read();
87+
88+
if (done) {
89+
console.log("✅ Stream completed successfully");
90+
controller.close();
91+
break;
92+
}
93+
94+
if (!value) {
95+
continue;
96+
}
97+
98+
buffer += decoder.decode(value, { stream: true });
99+
100+
// Process complete lines
101+
const lines = buffer.split('\n');
102+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
103+
104+
for (const line of lines) {
105+
const trimmedLine = line.trim();
106+
if (!trimmedLine) continue;
107+
108+
try {
109+
let data;
110+
111+
// Handle SSE format
112+
if (trimmedLine.startsWith('data: ')) {
113+
const dataStr = trimmedLine.slice(6);
114+
if (dataStr === '[DONE]') {
115+
console.log("🏁 Received [DONE] signal");
116+
controller.close();
117+
return;
118+
}
119+
data = JSON.parse(dataStr);
120+
} else {
121+
// Try parsing as direct JSON
122+
data = JSON.parse(trimmedLine);
123+
}
124+
125+
// Forward the parsed data as SSE format
126+
const sseData = `data: ${JSON.stringify(data)}\n\n`;
127+
controller.enqueue(new TextEncoder().encode(sseData));
128+
129+
} catch (parseError) {
130+
console.warn("⚠️ Failed to parse line:", trimmedLine, parseError);
131+
continue;
132+
}
133+
}
134+
}
135+
} catch (error) {
136+
console.error("❌ Stream reading error:", error);
137+
controller.error(error);
138+
} finally {
139+
try {
140+
reader.releaseLock();
141+
} catch (lockError) {
142+
console.warn("⚠️ Error releasing reader lock:", lockError);
143+
}
144+
}
145+
}
146+
});
147+
148+
return new NextResponse(stream, {
149+
headers: {
150+
"Content-Type": "text/event-stream",
151+
"Cache-Control": "no-cache",
152+
"Connection": "keep-alive",
153+
"Access-Control-Allow-Origin": "*",
154+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
155+
"Access-Control-Allow-Headers": "Content-Type",
156+
},
157+
});
158+
}
159+
160+
// Unexpected content type, try to handle as text
161+
const textResponse = await response.text();
162+
console.warn("🤔 Unexpected content type, got text:", textResponse.substring(0, 200));
163+
164+
try {
165+
const jsonData = JSON.parse(textResponse);
166+
return NextResponse.json(jsonData);
167+
} catch {
168+
// If it's not JSON, treat as error
169+
throw new Error(`Unexpected response format: ${textResponse.substring(0, 100)}`);
170+
}
171+
172+
} catch (fetchError) {
173+
console.error("❌ Backend connection failed:", fetchError);
174+
175+
return NextResponse.json({
176+
passed: false,
177+
summary: "Validation service unavailable",
178+
issues: [],
179+
raw_output: `Service Error: ${fetchError instanceof Error ? fetchError.message : 'Unknown error'}`,
180+
error_message: "Unable to connect to validation service. Please check if the backend is running.",
181+
debug_info: {
182+
status: "service_unavailable",
183+
playbook_length: playbook_content.length,
184+
error: "Backend connection failed"
185+
}
186+
}, { status: 200 });
187+
}
188+
189+
} catch (error) {
190+
console.error("❌ Validation proxy error:", error);
191+
return NextResponse.json(
192+
{
193+
error: "Validation service unavailable",
194+
detail: error instanceof Error ? error.message : "Unknown error",
195+
passed: false,
196+
issues: [],
197+
raw_output: "",
198+
debug_info: {
199+
error: "Validation service error",
200+
status: "failed"
201+
}
202+
},
203+
{ status: 500 }
204+
);
205+
}
206+
}
207+
208+
// Handle OPTIONS requests for CORS
209+
export async function OPTIONS(request: NextRequest) {
210+
return new NextResponse(null, {
211+
status: 200,
212+
headers: {
213+
"Access-Control-Allow-Origin": "*",
214+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
215+
"Access-Control-Allow-Headers": "Content-Type",
216+
},
217+
});
218+
}

0 commit comments

Comments
 (0)