nothingworry's picture
document deletion and improve tenant ID management
345b8ff
raw
history blame
5.32 kB
"use client";
import { useMemo, useState } from "react";
import { useTenant } from "@/contexts/TenantContext";
type Message = {
role: "user" | "assistant" | "system";
content: string;
meta?: string;
};
const API_BASE =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "") || "http://localhost:8000";
export function ChatPanel() {
const { tenantId } = useTenant();
const [message, setMessage] = useState("");
const [isSending, setIsSending] = useState(false);
const [history, setHistory] = useState<Message[]>([
{
role: "assistant",
content:
"Hi there! I’m the IntegraChat orchestrator. Ask anything about your tenant data and I will route the right MCP tools.",
meta: "Agent ready",
},
]);
const [lastDecision, setLastDecision] = useState<string | null>(null);
const conversationPayload = useMemo(
() =>
history
.filter((m) => m.role !== "system")
.map((m) => ({
role: m.role,
content: m.content,
})),
[history],
);
async function handleSend() {
if (!message.trim() || isSending) return;
const userMessage: Message = { role: "user", content: message.trim() };
const optimisticHistory = [...history, userMessage];
setHistory(optimisticHistory);
setMessage("");
setIsSending(true);
try {
const response = await fetch(`${API_BASE}/agent/message`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
tenant_id: tenantId,
message: userMessage.content,
conversation_history: conversationPayload,
temperature: 0,
}),
});
if (!response.ok) {
throw new Error(
`API error (${response.status}) – check backend/api/main.py`,
);
}
const data = await response.json();
const assistantText =
data?.text ??
"Agent responded but text field was empty. Inspect FastAPI logs for clues.";
setHistory((prev) => [
...prev,
{
role: "assistant",
content: assistantText,
meta: data?.decision?.reason ?? "response",
},
]);
setLastDecision(
data?.decision
? `${data.decision.action} · ${data.decision.tool ?? "llm"}`
: null,
);
} catch (err) {
console.error(err);
setHistory((prev) => [
...prev,
{
role: "assistant",
content:
err instanceof Error
? err.message
: "Failed to reach the FastAPI gateway.",
meta: "error",
},
]);
setLastDecision("error");
} finally {
setIsSending(false);
}
}
return (
<section
id="chat"
className="gradient-border relative rounded-[28px] p-1 text-white"
>
<div className="glass-panel relative rounded-[26px] p-6">
<div>
<p className="text-sm uppercase tracking-[0.5em] text-cyan-200/70">
Orchestrator Console
</p>
<h2 className="mt-2 text-3xl font-semibold">
Talk to your enterprise agent
</h2>
</div>
<div className="mt-6 h-[360px] space-y-3 overflow-y-auto rounded-2xl border border-white/10 bg-slate-950/40 p-4 scrollArea">
{history.map((msg, idx) => (
<div
key={`${msg.role}-${idx}`}
className={`flex gap-3 rounded-2xl px-4 py-3 ${
msg.role === "user"
? "bg-slate-900/70 text-slate-100"
: "bg-cyan-500/10 text-slate-100"
}`}
>
<span className="text-xs font-semibold uppercase tracking-widest text-cyan-200/80">
{msg.role}
</span>
<div className="space-y-1 text-sm">
<p>{msg.content}</p>
{msg.meta && (
<p className="text-xs text-slate-400">{msg.meta}</p>
)}
</div>
</div>
))}
</div>
<div className="mt-5 flex flex-col gap-3 md:flex-row">
<textarea
placeholder="Ask about policies, knowledge base hits, or route through RAG/Web/Admin..."
value={message}
onChange={(e) => setMessage(e.target.value)}
className="flex-1 rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white outline-none focus:border-cyan-200/80"
rows={3}
/>
<button
onClick={handleSend}
disabled={isSending}
className="min-w-[160px] rounded-2xl bg-gradient-to-r from-sky-400 to-cyan-500 px-6 py-3 font-semibold text-slate-950 shadow-lg shadow-cyan-500/30 transition hover:-translate-y-0.5 disabled:cursor-not-allowed disabled:opacity-60"
>
{isSending ? "Routing…" : "Send to MCP"}
</button>
</div>
<div className="mt-4 flex items-center gap-3 text-sm text-slate-300">
<span className="h-2 w-2 rounded-full bg-emerald-400 shadow-[0_0_12px_#34d399]" />
{lastDecision
? `Last decision: ${lastDecision}`
: "No tool invocation yet"}
</div>
</div>
</section>
);
}