mirror of
https://github.com/mblanke/Dashboard.git
synced 2026-03-01 12:10:20 -05:00
fix: add POST handler to /api/rag for semantic search + agent proxy
- Added POST handler that proxies retrieve/agent/tags actions to RAG API - Maps RAG API response shape (chunks[].rerank_score) to frontend expected format - Normalizes rerank_score (0-10 range) to 0-1 score for display - Disabled tags fetch on mount (blocks RAG API - scrolls all 49k Qdrant points)
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
const RAG_API = process.env.RAG_API_URL || "http://localhost:8099";
|
const RAG_API = process.env.RAG_API_URL || "http://localhost:8099";
|
||||||
|
|
||||||
|
/* ─── GET /api/rag → status + ingest-progress ─── */
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const [statusRes, progressRes] = await Promise.all([
|
const [statusRes, progressRes] = await Promise.all([
|
||||||
@@ -32,3 +33,95 @@ export async function GET() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── POST /api/rag → proxy retrieve / agent / tags ─── */
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
const body = await req.json();
|
||||||
|
const { action, ...params } = body;
|
||||||
|
|
||||||
|
/* --- tags --------------------------------------------------------- */
|
||||||
|
if (action === "tags") {
|
||||||
|
const res = await fetch(`${RAG_API}/tags`, {
|
||||||
|
next: { revalidate: 0 },
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
// normalise → always return { tags: string[] }
|
||||||
|
const tags: string[] = Array.isArray(data)
|
||||||
|
? data
|
||||||
|
: Array.isArray(data.tags)
|
||||||
|
? data.tags
|
||||||
|
: [];
|
||||||
|
return NextResponse.json({ tags });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- retrieve (semantic search) ----------------------------------- */
|
||||||
|
if (action === "retrieve") {
|
||||||
|
const res = await fetch(`${RAG_API}/retrieve`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
question: params.question,
|
||||||
|
top_k: params.top_k ?? 8,
|
||||||
|
tags: params.tags,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const err = await res.text();
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: err || "RAG retrieve failed" },
|
||||||
|
{ status: res.status }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
// RAG API returns { chunks: [...] } — map to frontend's expected shape
|
||||||
|
const chunks = data.chunks ?? data.results ?? (Array.isArray(data) ? data : []);
|
||||||
|
const results = chunks.map((c: Record<string, unknown>) => ({
|
||||||
|
title: c.title ?? "",
|
||||||
|
text: c.text ?? "",
|
||||||
|
source: c.source_path ?? c.source ?? "",
|
||||||
|
page: c.page ?? null,
|
||||||
|
score: c.rerank_score != null
|
||||||
|
? Math.min((c.rerank_score as number) / 10, 1) // normalise rerank_score ≈0-10 → 0-1
|
||||||
|
: c.score ?? null,
|
||||||
|
tags: c.tags ?? [],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return NextResponse.json(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- agent --------------------------------------------------------- */
|
||||||
|
if (action === "agent") {
|
||||||
|
const res = await fetch(`${RAG_API}/agent`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
question: params.question,
|
||||||
|
max_steps: params.max_steps ?? 5,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const err = await res.text();
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: err || "RAG agent failed" },
|
||||||
|
{ status: res.status }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return NextResponse.json(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: `Unknown action: ${action}` },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : "Unexpected error";
|
||||||
|
return NextResponse.json({ error: message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -232,20 +232,20 @@ export default function SemanticSearch() {
|
|||||||
const [agentResult, setAgentResult] = useState<AgentResponse | null>(null);
|
const [agentResult, setAgentResult] = useState<AgentResponse | null>(null);
|
||||||
|
|
||||||
// fetch tags on mount
|
// fetch tags on mount
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
ragFetch({ action: "tags" })
|
// ragFetch({ action: "tags" })
|
||||||
.then((data) => {
|
// .then((data) => {
|
||||||
const tags: string[] = Array.isArray(data)
|
// const tags: string[] = Array.isArray(data)
|
||||||
? data
|
// ? data
|
||||||
: Array.isArray(data.tags)
|
// : Array.isArray(data.tags)
|
||||||
? data.tags
|
// ? data.tags
|
||||||
: [];
|
// : [];
|
||||||
setAvailableTags(tags);
|
// setAvailableTags(tags);
|
||||||
})
|
// })
|
||||||
.catch(() => {
|
// .catch(() => {
|
||||||
/* silently ignore – tags are optional */
|
// /* silently ignore – tags are optional */
|
||||||
});
|
// });
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const toggleTag = useCallback((tag: string) => {
|
const toggleTag = useCallback((tag: string) => {
|
||||||
setSelectedTags((prev) =>
|
setSelectedTags((prev) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user