| import { Database, FileSliders, Search, Shield } from 'lucide-react'; |
|
|
| import { Badge } from '@/components/ui/badge'; |
| import { |
| Card, |
| CardContent, |
| CardDescription, |
| CardHeader, |
| CardTitle, |
| } from '@/components/ui/card'; |
| import type { RagConfig } from '@/types/admin'; |
|
|
| function valueOrDash(value: unknown): string { |
| if (value === null || value === undefined || value === '') { |
| return '-'; |
| } |
|
|
| if (typeof value === 'boolean') { |
| return value ? 'Ya' : 'Tidak'; |
| } |
|
|
| return String(value); |
| } |
|
|
| function ConfigRows({ |
| rows, |
| }: { |
| rows: Array<[label: string, value: unknown]>; |
| }) { |
| return ( |
| <dl className="grid gap-3 text-sm"> |
| {rows.map(([label, value]) => ( |
| <div |
| className="flex items-start justify-between gap-4 rounded-lg border border-(--lecturer-border) px-3 py-2" |
| key={label} |
| > |
| <dt className="text-muted-foreground">{label}</dt> |
| <dd className="max-w-[60%] text-right font-medium break-words"> |
| {valueOrDash(value)} |
| </dd> |
| </div> |
| ))} |
| </dl> |
| ); |
| } |
|
|
| export function AdminRagSummary({ |
| ragConfig, |
| }: { |
| ragConfig?: RagConfig | null; |
| }) { |
| const llm = ragConfig?.llm_config ?? {}; |
| const vectorDb = ragConfig?.vector_db ?? {}; |
| const retrieval = ragConfig?.retrieval ?? {}; |
|
|
| return ( |
| <section className="grid gap-6 xl:grid-cols-2"> |
| <Card className="border-(--lecturer-border) bg-(--lecturer-surface) xl:col-span-2"> |
| <CardHeader className="gap-3"> |
| <div className="flex items-center justify-between gap-3"> |
| <span className="rounded-xl bg-[var(--lecturer-primary-soft)] p-3 text-[var(--lecturer-primary)]"> |
| <FileSliders className="size-5" /> |
| </span> |
| <Badge variant="outline">RAG</Badge> |
| </div> |
| <div> |
| <CardTitle>Konfigurasi RAG</CardTitle> |
| <CardDescription> |
| Path config: {ragConfig?.path ?? '-'} |
| </CardDescription> |
| </div> |
| </CardHeader> |
| </Card> |
| |
| <Card className="border-(--lecturer-border) bg-(--lecturer-surface)"> |
| <CardHeader> |
| <CardTitle className="flex items-center gap-2"> |
| <Shield className="size-5 text-[var(--lecturer-primary)]" /> |
| LLM |
| </CardTitle> |
| <CardDescription>Ringkasan model aktif.</CardDescription> |
| </CardHeader> |
| <CardContent> |
| <ConfigRows |
| rows={[ |
| ['Provider', llm.active_provider], |
| ['Model', llm.active_model], |
| ['Temperature', llm.temperature], |
| ['Max Tokens', llm.max_tokens], |
| ['Request Timeout', llm.request_timeout], |
| ]} |
| /> |
| </CardContent> |
| </Card> |
| |
| <Card className="border-(--lecturer-border) bg-(--lecturer-surface)"> |
| <CardHeader> |
| <CardTitle className="flex items-center gap-2"> |
| <Database className="size-5 text-[var(--lecturer-primary)]" /> |
| VectorDB |
| </CardTitle> |
| <CardDescription>Ringkasan embedding dan chunk.</CardDescription> |
| </CardHeader> |
| <CardContent> |
| <ConfigRows |
| rows={[ |
| ['Embedding Model', vectorDb.embedding_model], |
| ['Persist Path', vectorDb.persist_path], |
| ['Chunk Size', vectorDb.chunk_size], |
| ['Chunk Overlap', vectorDb.chunk_overlap], |
| ]} |
| /> |
| </CardContent> |
| </Card> |
| |
| <Card className="border-(--lecturer-border) bg-(--lecturer-surface) xl:col-span-2"> |
| <CardHeader> |
| <CardTitle className="flex items-center gap-2"> |
| <Search className="size-5 text-[var(--lecturer-primary)]" /> |
| Retrieval |
| </CardTitle> |
| <CardDescription> |
| Ringkasan bobot dan batas konteks retrieval. |
| </CardDescription> |
| </CardHeader> |
| <CardContent> |
| <ConfigRows |
| rows={[ |
| ['Top K', retrieval.top_k], |
| ['Max Context Chars', retrieval.max_context_chars], |
| ['BM25 Weight', retrieval.bm25_weight], |
| ['Dense Weight', retrieval.dense_weight], |
| ['Lexical Weight', retrieval.lexical_weight], |
| [ |
| 'Similarity Threshold', |
| retrieval.similarity_threshold, |
| ], |
| ['History Turns', retrieval.history_turns], |
| [ |
| 'Candidate Pool Size', |
| retrieval.candidate_pool_size, |
| ], |
| ['Neighbor Window', retrieval.neighbor_window], |
| ['Reranker Model', retrieval.reranker_model], |
| ['Enable Reranker', retrieval.enable_reranker], |
| ]} |
| /> |
| </CardContent> |
| </Card> |
| </section> |
| ); |
| } |
|
|
|
|
|
|
|
|