218 lines
7.1 KiB
TypeScript
218 lines
7.1 KiB
TypeScript
import { type NextRequest, NextResponse } from "next/server"
|
|
import type { ChatRequest, ChatResponse } from "@/lib/types"
|
|
import { getFlags } from "@/lib/flags"
|
|
|
|
/**
|
|
* Get webhook URL for a specific agent from environment variables
|
|
* Format: AGENT_{agentIndex}_URL
|
|
*/
|
|
function getAgentWebhookUrl(agentId: string): string | null {
|
|
// Extract agent index from agentId (format: "agent-1", "agent-2", etc.)
|
|
const match = agentId.match(/agent-(\d+)/)
|
|
if (!match) {
|
|
console.error("[chat] Invalid agentId format:", agentId)
|
|
return null
|
|
}
|
|
|
|
const agentIndex = match[1]
|
|
const urlKey = `AGENT_${agentIndex}_URL`
|
|
const webhookUrl = process.env[urlKey]
|
|
|
|
if (!webhookUrl) {
|
|
console.error(`[chat] No webhook URL configured for ${urlKey}`)
|
|
return null
|
|
}
|
|
|
|
return webhookUrl
|
|
}
|
|
|
|
// Helper function to convert diff tool call to markdown format
|
|
function convertToDiffTool(args: any, diffToolEnabled: boolean): string {
|
|
try {
|
|
const { oldCode, newCode, title, language } = args
|
|
|
|
if (!oldCode || !newCode) {
|
|
return "Error: Missing oldCode or newCode in diff tool call"
|
|
}
|
|
|
|
// If diff tool is disabled, return as plain code blocks
|
|
if (!diffToolEnabled) {
|
|
const titleText = title || "Code Changes"
|
|
return `### ${titleText}\n\n**Before:**\n\`\`\`${language || 'text'}\n${oldCode}\n\`\`\`\n\n**After:**\n\`\`\`${language || 'text'}\n${newCode}\n\`\`\``
|
|
}
|
|
|
|
const diffToolCall = {
|
|
oldCode: String(oldCode).replace(/\n/g, '\\n'),
|
|
newCode: String(newCode).replace(/\n/g, '\\n'),
|
|
title: title || "Code Changes",
|
|
language: language || "text"
|
|
}
|
|
|
|
return `\`\`\`diff-tool\n${JSON.stringify(diffToolCall, null, 2)}\n\`\`\``
|
|
} catch (error) {
|
|
console.error("[v0] Error converting diff tool:", error)
|
|
return "Error: Failed to process diff tool call"
|
|
}
|
|
}
|
|
|
|
export async function POST(request: NextRequest): Promise<NextResponse<ChatResponse>> {
|
|
try {
|
|
const body = await request.json()
|
|
if (typeof body !== "object" || body === null) {
|
|
return NextResponse.json({ error: "Invalid request body" }, { status: 400 })
|
|
}
|
|
|
|
const { message, timestamp, sessionId, agentId, images } = body as ChatRequest
|
|
|
|
// Get feature flags
|
|
const flags = getFlags()
|
|
|
|
// Validate required fields
|
|
if (!message || typeof message !== "string") {
|
|
return NextResponse.json({ error: "Message is required" }, { status: 400 })
|
|
}
|
|
|
|
if (!agentId || typeof agentId !== "string") {
|
|
return NextResponse.json({ error: "Agent ID is required" }, { status: 400 })
|
|
}
|
|
|
|
// Check if image uploads are enabled
|
|
if (images && images.length > 0 && !flags.IMAGE_UPLOADS_ENABLED) {
|
|
return NextResponse.json(
|
|
{
|
|
error: "Image uploads are currently disabled",
|
|
hint: "Contact your administrator to enable the IMAGE_UPLOADS_ENABLED flag"
|
|
},
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
|
|
// Get webhook URL for the selected agent
|
|
const webhookUrl = getAgentWebhookUrl(agentId)
|
|
if (!webhookUrl) {
|
|
return NextResponse.json(
|
|
{ error: `Agent ${agentId} is not properly configured` },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
|
|
console.log("[chat] Sending to webhook:", { agentId, message, timestamp, sessionId })
|
|
|
|
const response = await fetch(webhookUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
message,
|
|
timestamp,
|
|
sessionId,
|
|
agentId,
|
|
images: images && images.length > 0 ? images : undefined,
|
|
}),
|
|
})
|
|
|
|
console.log("[v0] Webhook response status:", response.status)
|
|
|
|
const responseText = await response.text()
|
|
console.log("[v0] Webhook response body (first 200 chars):", responseText.substring(0, 200))
|
|
|
|
if (!response.ok) {
|
|
// Try to parse as JSON if possible, otherwise use text
|
|
let errorData
|
|
try {
|
|
errorData = responseText ? JSON.parse(responseText) : {}
|
|
} catch {
|
|
errorData = { message: responseText || "Unknown error" }
|
|
}
|
|
|
|
console.error("[v0] Webhook error:", errorData)
|
|
|
|
return NextResponse.json(
|
|
{
|
|
error: errorData.message || "Failed to communicate with webhook",
|
|
hint: errorData.hint,
|
|
code: errorData.code,
|
|
},
|
|
{ status: response.status },
|
|
)
|
|
}
|
|
|
|
if (!responseText) {
|
|
console.log("[v0] Empty response from webhook")
|
|
return NextResponse.json({
|
|
response:
|
|
"The webhook received your message but didn't return a response. Please ensure your n8n workflow includes a 'Respond to Webhook' node that returns data.",
|
|
hint: "Add a 'Respond to Webhook' node in your n8n workflow to send responses back to the chat.",
|
|
})
|
|
}
|
|
|
|
try {
|
|
// Split response by newlines to get individual JSON objects
|
|
const lines = responseText.trim().split("\n")
|
|
const chunks: string[] = []
|
|
|
|
for (const line of lines) {
|
|
if (!line.trim()) continue
|
|
|
|
try {
|
|
const chunk = JSON.parse(line)
|
|
|
|
// Extract content from "item" type chunks
|
|
if (chunk.type === "item" && chunk.content) {
|
|
chunks.push(chunk.content)
|
|
}
|
|
|
|
// Handle diff tool calls
|
|
if (chunk.type === "tool_call" && chunk.name === "show_diff") {
|
|
const diffTool = convertToDiffTool(chunk.args, flags.DIFF_TOOL_ENABLED)
|
|
chunks.push(diffTool)
|
|
}
|
|
} catch {
|
|
console.log("[v0] Failed to parse line:", line)
|
|
}
|
|
}
|
|
|
|
// Combine all chunks into a single message
|
|
if (chunks.length > 0) {
|
|
const fullMessage = chunks.join("")
|
|
console.log("[v0] Combined message from", chunks.length, "chunks")
|
|
return NextResponse.json({ response: fullMessage })
|
|
}
|
|
|
|
// If no chunks found, try parsing as regular JSON
|
|
const data = JSON.parse(responseText)
|
|
console.log("[v0] Parsed webhook data:", data)
|
|
|
|
// Check if this is a diff tool call
|
|
if (data.type === "tool_call" && data.name === "show_diff") {
|
|
const diffTool = convertToDiffTool(data.args, flags.DIFF_TOOL_ENABLED)
|
|
return NextResponse.json({ response: diffTool })
|
|
}
|
|
|
|
// Extract the response from various possible fields
|
|
let responseMessage = data.response || data.message || data.output || data.text
|
|
|
|
// If the response is an object, try to extract from nested fields
|
|
if (typeof responseMessage === "object") {
|
|
responseMessage =
|
|
responseMessage.response || responseMessage.message || responseMessage.output || responseMessage.text
|
|
}
|
|
|
|
// If still no message found, stringify the entire response
|
|
if (!responseMessage) {
|
|
responseMessage = JSON.stringify(data)
|
|
}
|
|
|
|
return NextResponse.json({ response: responseMessage })
|
|
} catch {
|
|
console.log("[v0] Response is not JSON, returning as text")
|
|
// If not JSON, return the text as the response
|
|
return NextResponse.json({ response: responseText })
|
|
}
|
|
} catch (error) {
|
|
console.error("[v0] API route error:", error)
|
|
return NextResponse.json({ error: "Internal server error" }, { status: 500 })
|
|
}
|
|
}
|