feat: member portal Sprint 9 — Ask AI (RAG over message archive)
- AskAIQuery model + migration (logs question/answer/citations per member) - AskAIService: RAG pipeline — retrieve via tenant-isolated SearchService (Meili), ground LLM in top-8 snippets with inline [n] citations, degrade gracefully when OPENROUTER_API_KEY absent or no context (snippet fallback) - Shared api/common/llm-client.ts (mirrors worker's OpenRouter client) - SearchModule now exports SearchService - Member endpoints: GET/POST /my/ask (history + ask) - /my/ask page + AskChat client component: question box, answer cards with sources - Ask AI nav item (top of member nav) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { getMemberToken, getApiBaseUrl } from '../../_lib/api';
|
||||
import { AskChat } from './AskChat';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Citation {
|
||||
messageId: string;
|
||||
snippet: string;
|
||||
senderName: string;
|
||||
sourceGroupName: string;
|
||||
approvedAt: number;
|
||||
}
|
||||
|
||||
interface QA {
|
||||
id: string;
|
||||
question: string;
|
||||
answer: string;
|
||||
citations: Citation[];
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
async function fetchHistory(token: string): Promise<QA[]> {
|
||||
const res = await fetch(`${getApiBaseUrl()}/my/ask`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
cache: 'no-store',
|
||||
}).catch(() => null);
|
||||
if (!res || !res.ok) return [];
|
||||
return (await res.json()) as QA[];
|
||||
}
|
||||
|
||||
export default async function AskPage() {
|
||||
const token = await getMemberToken();
|
||||
if (!token) return null;
|
||||
|
||||
const history = await fetchHistory(token);
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold text-gray-900">Ask AI</h1>
|
||||
<p className="text-sm text-gray-500 mt-0.5">Answers from your community's knowledge.</p>
|
||||
</div>
|
||||
<Link href="/my" className="text-sm text-indigo-600 hover:underline">← Dashboard</Link>
|
||||
</div>
|
||||
|
||||
<AskChat initialHistory={history} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user