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:
2026-06-17 17:03:01 +05:30
parent 6d89d8a609
commit f61c070428
12 changed files with 378 additions and 1 deletions
+20
View File
@@ -0,0 +1,20 @@
import { jsonResponse, memberApiFetch } from '../../../_lib/api';
export const dynamic = 'force-dynamic';
export async function GET(): Promise<Response> {
const res = await memberApiFetch('/my/ask');
const body = await res.json();
return jsonResponse(body, res.status);
}
export async function POST(request: Request): Promise<Response> {
const body = await request.json();
const res = await memberApiFetch('/my/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const resBody = await res.json();
return jsonResponse(resBody, res.status);
}