mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 04:21:52 +00:00
fix(lighthouse): Display errors in Lighthouse & allow resending message (#8400)
Co-authored-by: Chandrapal Badshah <Chan9390@users.noreply.github.com> Co-authored-by: Pablo Lara <larabjj@gmail.com>
This commit is contained in:
@@ -2,6 +2,21 @@
|
||||
|
||||
All notable changes to the **Prowler UI** are documented in this file.
|
||||
|
||||
## [1.10.0] (Prowler v5.10.0 - UNRELEASED)
|
||||
|
||||
### Added
|
||||
|
||||
- Lighthouse banner [(#8259)](https://github.com/prowler-cloud/prowler/pull/8259)
|
||||
- Integration with Amazon S3, enabling storage and retrieval of scan data via S3 buckets [(#8056)](https://github.com/prowler-cloud/prowler/pull/8056)
|
||||
|
||||
___
|
||||
|
||||
## [v1.9.3] (Prowler v5.9.3)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Display error messages and allow editing last message in Lighthouse [(#8358)](https://github.com/prowler-cloud/prowler/pull/8358)
|
||||
|
||||
## [v1.9.0] (Prowler v5.9.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LangChainAdapter, Message } from "ai";
|
||||
|
||||
import { getLighthouseConfig } from "@/actions/lighthouse/lighthouse";
|
||||
import { getErrorMessage } from "@/lib/helper";
|
||||
import { getCurrentDataSection } from "@/lib/lighthouse/data";
|
||||
import {
|
||||
convertLangChainMessageToVercelMessage,
|
||||
@@ -73,22 +74,38 @@ export async function POST(req: Request) {
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
for await (const { event, data, tags } of agentStream) {
|
||||
if (event === "on_chat_model_stream") {
|
||||
if (data.chunk.content && !!tags && tags.includes("supervisor")) {
|
||||
const chunk = data.chunk;
|
||||
const aiMessage = convertLangChainMessageToVercelMessage(chunk);
|
||||
controller.enqueue(aiMessage);
|
||||
try {
|
||||
for await (const { event, data, tags } of agentStream) {
|
||||
if (event === "on_chat_model_stream") {
|
||||
if (data.chunk.content && !!tags && tags.includes("supervisor")) {
|
||||
const chunk = data.chunk;
|
||||
const aiMessage = convertLangChainMessageToVercelMessage(chunk);
|
||||
controller.enqueue(aiMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.close();
|
||||
} catch (error) {
|
||||
const errorName =
|
||||
error instanceof Error ? error.constructor.name : "UnknownError";
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
controller.enqueue({
|
||||
id: "error-" + Date.now(),
|
||||
role: "assistant",
|
||||
content: `[LIGHTHOUSE_ANALYST_ERROR]: ${errorName}: ${errorMessage}`,
|
||||
});
|
||||
controller.close();
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
return LangChainAdapter.toDataStreamResponse(stream);
|
||||
} catch (error) {
|
||||
console.error("Error in POST request:", error);
|
||||
return Response.json({ error: "An error occurred" }, { status: 500 });
|
||||
return Response.json(
|
||||
{ error: await getErrorMessage(error) },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useChat } from "@ai-sdk/react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { MemoizedMarkdown } from "@/components/lighthouse/memoized-markdown";
|
||||
@@ -25,20 +25,43 @@ interface ChatFormData {
|
||||
}
|
||||
|
||||
export const Chat = ({ hasConfig, isActive }: ChatProps) => {
|
||||
const { messages, handleSubmit, handleInputChange, append, status } = useChat(
|
||||
{
|
||||
api: "/api/lighthouse/analyst",
|
||||
credentials: "same-origin",
|
||||
experimental_throttle: 100,
|
||||
sendExtraMessageFields: true,
|
||||
onFinish: () => {
|
||||
// Handle chat completion
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Chat error:", error);
|
||||
},
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
const {
|
||||
messages,
|
||||
handleSubmit,
|
||||
handleInputChange,
|
||||
append,
|
||||
status,
|
||||
error,
|
||||
setMessages,
|
||||
} = useChat({
|
||||
api: "/api/lighthouse/analyst",
|
||||
credentials: "same-origin",
|
||||
experimental_throttle: 100,
|
||||
sendExtraMessageFields: true,
|
||||
onFinish: (message) => {
|
||||
// Detect error messages sent from backend using specific prefix
|
||||
if (message.content?.startsWith("[LIGHTHOUSE_ANALYST_ERROR]:")) {
|
||||
const errorText = message.content
|
||||
.replace("[LIGHTHOUSE_ANALYST_ERROR]:", "")
|
||||
.trim();
|
||||
setErrorMessage(errorText);
|
||||
// Remove error message from chat history
|
||||
setMessages((prev) =>
|
||||
prev.filter(
|
||||
(m) => !m.content?.startsWith("[LIGHTHOUSE_ANALYST_ERROR]:"),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
onError: (error) => {
|
||||
console.error("Chat error:", error);
|
||||
setErrorMessage(
|
||||
error?.message || "An error occurred. Please retry your message.",
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<ChatFormData>({
|
||||
defaultValues: {
|
||||
@@ -65,8 +88,29 @@ export const Chat = ({ hasConfig, isActive }: ChatProps) => {
|
||||
}
|
||||
}, [status, form]);
|
||||
|
||||
// Populate input with last user message when any error occurs
|
||||
useEffect(() => {
|
||||
if (errorMessage && messages.length > 0) {
|
||||
// Filter out the error message itself before finding the last user message
|
||||
const nonErrorMessages = messages.filter(
|
||||
(m) => !m.content?.startsWith("[LIGHTHOUSE_ANALYST_ERROR]:"),
|
||||
);
|
||||
if (nonErrorMessages.length > 0) {
|
||||
const lastUserMessage = nonErrorMessages
|
||||
.filter((m) => m.role === "user")
|
||||
.pop();
|
||||
if (lastUserMessage && !messageValue) {
|
||||
form.setValue("message", lastUserMessage.content);
|
||||
setMessages(nonErrorMessages.slice(0, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [errorMessage, messages, messageValue, form, setMessages]);
|
||||
|
||||
const onFormSubmit = form.handleSubmit((data) => {
|
||||
if (data.message.trim()) {
|
||||
// Clear error on new submission
|
||||
setErrorMessage(null);
|
||||
handleSubmit();
|
||||
}
|
||||
});
|
||||
@@ -148,7 +192,54 @@ export const Chat = ({ hasConfig, isActive }: ChatProps) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{messages.length === 0 ? (
|
||||
{/* Error Banner */}
|
||||
{(error || errorMessage) && (
|
||||
<div className="mx-4 mt-4 rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-800 dark:bg-red-900/20">
|
||||
<div className="flex items-start">
|
||||
<div className="flex-shrink-0">
|
||||
<svg
|
||||
className="h-5 w-5 text-red-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="text-sm font-medium text-red-800 dark:text-red-200">
|
||||
Error
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-red-700 dark:text-red-300">
|
||||
{errorMessage ||
|
||||
error?.message ||
|
||||
"An error occurred. Please retry your message."}
|
||||
</p>
|
||||
{/* Original error details for native errors */}
|
||||
{error && (error as any).status && (
|
||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400">
|
||||
Status: {(error as any).status}
|
||||
</p>
|
||||
)}
|
||||
{error && (error as any).body && (
|
||||
<details className="mt-2">
|
||||
<summary className="cursor-pointer text-xs text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300">
|
||||
Show details
|
||||
</summary>
|
||||
<pre className="mt-1 max-h-20 overflow-auto rounded bg-red-100 p-2 text-xs text-red-800 dark:bg-red-900/30 dark:text-red-200">
|
||||
{JSON.stringify((error as any).body, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{messages.length === 0 && !errorMessage && !error ? (
|
||||
<div className="flex flex-1 items-center justify-center p-4">
|
||||
<div className="w-full max-w-2xl">
|
||||
<h2 className="mb-4 text-center font-sans text-xl">Suggestions</h2>
|
||||
@@ -232,7 +323,11 @@ export const Chat = ({ hasConfig, isActive }: ChatProps) => {
|
||||
control={form.control}
|
||||
name="message"
|
||||
label=""
|
||||
placeholder="Type your message..."
|
||||
placeholder={
|
||||
error || errorMessage
|
||||
? "Edit your message and try again..."
|
||||
: "Type your message..."
|
||||
}
|
||||
variant="bordered"
|
||||
minRows={1}
|
||||
maxRows={6}
|
||||
|
||||
Reference in New Issue
Block a user