feat(admin): build the AI Policy console (§6) — approved, spec now implemented
New admin tab "AI" (app/(main)/admin/_tabs/ai-policy.tsx): provider-class toggles, server model allow-list, BYOK provider allow-list, seats/usage (front-end for the already-real lib/ai/entitlement.ts), retrieval on/off, consent text + version bump. Real backend, not cosmetic: AiConsoleConfig persisted via config-manager (lib/ai/types.ts, ai-policy.json in the CONFIG dir). New GET/PUT /api/admin/ai/policy. Enforcement wired at every real chokepoint, not just the picker: /api/ai/server/chat checks classesEnabled.server and the model allow-list, /api/ai/retrieve checks retrievalEnabled, /api/ai/server/models filters by allow-list. GET /api/ai/policy folds classesEnabled into the classes list clients see. Resolved the spec's 3 open questions as recommended: BYOK allow-list stays client-side/advisory (wired into ai-assistant-settings.tsx's addProfile), tier picker stays cosmetic, master aiAssistantEnabled toggle stays in the existing Policy tab (this tab links to it instead of duplicating it). Defaults preserve today's behavior exactly (classesEnabled/allowlists all start empty/null) — turning this on changes nothing until an admin touches it.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getStalwartCredentials } from '@/lib/stalwart/credentials';
|
||||
import { serverSearchMail, hydrateMailRefs } from '@/lib/ai/retrieval/mail-embeddings';
|
||||
import { configManager } from '@/lib/admin/config-manager';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
@@ -31,6 +32,14 @@ export async function POST(request: NextRequest) {
|
||||
return new NextResponse(null, { status: 404 });
|
||||
}
|
||||
|
||||
// Real enforcement, not cosmetic client hiding (docs/ADMIN-AI-POLICY-CONSOLE-SPEC.md
|
||||
// §6): an admin can disable mail-content-to-embeddings augmentation
|
||||
// independent of disabling the `server` chat class outright.
|
||||
await configManager.ensureLoaded();
|
||||
if (!configManager.getAiConsoleConfig().retrievalEnabled) {
|
||||
return NextResponse.json({ error: 'retrieval is disabled by admin policy' }, { status: 403 });
|
||||
}
|
||||
|
||||
let body: { query?: unknown; limit?: unknown };
|
||||
try {
|
||||
body = await request.json();
|
||||
|
||||
Reference in New Issue
Block a user