Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 11 additions & 49 deletions src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,26 @@
import { getApp } from "@/actions/get-app";
import { freestyle } from "@/lib/freestyle";
import { NextRequest } from "next/server";
import { getAppIdFromHeaders } from "@/lib/utils";
import { UIMessage } from "ai";
import { builderAgent } from "@/mastra/agents/builder";
import { ChatService } from "@/lib/internal/chat-service";

// "fix" mastra mcp bug
import { EventEmitter } from "events";
import {
isStreamRunning,
stopStream,
waitForStreamToStop,
clearStreamState,
sendMessageWithStreaming,
} from "@/lib/internal/stream-manager";
EventEmitter.defaultMaxListeners = 1000;

import { NextRequest } from "next/server";

export async function POST(req: NextRequest) {
console.log("creating new chat stream");
const appId = getAppIdFromHeaders(req);
console.log("Creating new chat stream");

// Get app ID from headers
const appId = getAppIdFromHeaders(req);
if (!appId) {
return new Response("Missing App Id header", { status: 400 });
}

const app = await getApp(appId);
if (!app) {
return new Response("App not found", { status: 404 });
// Validate request
const validationError = ChatService.validateChatRequest(req);
if (validationError) {
return validationError;
}

// Check if a stream is already running and stop it if necessary
if (await isStreamRunning(appId)) {
console.log("Stopping previous stream for appId:", appId);
await stopStream(appId);

// Wait until stream state is cleared
const stopped = await waitForStreamToStop(appId);
if (!stopped) {
await clearStreamState(appId);
return new Response(
"Previous stream is still shutting down, please try again",
{ status: 429 }
);
}
}

const { messages }: { messages: UIMessage[] } = await req.json();

const { mcpEphemeralUrl, fs } = await freestyle.requestDevServer({
repoId: app.info.gitRepo,
});

const resumableStream = await sendMessageWithStreaming(
builderAgent,
appId,
mcpEphemeralUrl,
fs,
messages.at(-1)!
);

return resumableStream.response();
// Process chat request with all the durability features
return await ChatService.processChatRequest(req, appId);
}
42 changes: 42 additions & 0 deletions src/app/api/health/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { NextRequest } from "next/server";
import { Monitoring } from "@/lib/internal/monitoring";

export async function GET(req: NextRequest) {
try {
const healthSummary = Monitoring.getHealthSummary();
const metrics = Monitoring.getMetrics();

return new Response(
JSON.stringify({
status: healthSummary.status,
timestamp: new Date().toISOString(),
issues: healthSummary.issues,
summary: {
successRate: `${metrics.performance.successRate.toFixed(1)}%`,
totalRequests: metrics.performance.totalRequests,
averageResponseTime: `${metrics.performance.averageResponseTime.toFixed(0)}ms`,
circuitBreakers: Object.keys(metrics.circuitBreakers).length,
},
}),
{
status: healthSummary.status === "healthy" ? 200 : 503,
headers: {
"content-type": "application/json",
"cache-control": "no-cache",
},
}
);
} catch (error) {
return new Response(
JSON.stringify({
status: "error",
message: "Failed to get health status",
timestamp: new Date().toISOString(),
}),
{
status: 500,
headers: { "content-type": "application/json" },
}
);
}
}
Loading