mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-26 15:34:26 +00:00
2a74a8d6ae
* feat(rag): add knowledge base migration from v4.9.0 to plugin architecture Rewrite dbm020 to backup old knowledge_bases data and preserve external_knowledge_bases table. Add migration API endpoints and frontend dialog so users can opt-in to auto-install LangRAG plugin and restore their knowledge bases with original UUIDs preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): query marketplace for actual plugin version instead of 'latest' The marketplace API does not support 'latest' as a version string. Fetch the plugin info first to get latest_version, then use that concrete version for installation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(rag): add data-only migration option and fix dialog width Add option to migrate knowledge base data without auto-installing the LangRAG plugin (for offline/intranet environments). Also narrow the migration dialog to match other confirmation dialogs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: to red and no more * fix lint * fix ruff lint * feat: add external migration * fix: show * feat: add external plugin auto download * feat: update migration messages for knowledge base in multiple languages --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com>
151 lines
4.3 KiB
TypeScript
151 lines
4.3 KiB
TypeScript
'use client';
|
|
|
|
import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
|
|
import styles from './knowledgeBase.module.css';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useEffect, useState } from 'react';
|
|
import { KnowledgeBaseVO } from '@/app/home/knowledge/components/kb-card/KBCardVO';
|
|
import KBCard from '@/app/home/knowledge/components/kb-card/KBCard';
|
|
import KBDetailDialog from '@/app/home/knowledge/KBDetailDialog';
|
|
import KBMigrationDialog from '@/app/home/knowledge/components/kb-migration-dialog/KBMigrationDialog';
|
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
|
import { KnowledgeBase } from '@/app/infra/entities/api';
|
|
|
|
export default function KnowledgePage() {
|
|
const { t } = useTranslation();
|
|
const [knowledgeBaseList, setKnowledgeBaseList] = useState<KnowledgeBaseVO[]>(
|
|
[],
|
|
);
|
|
const [selectedKbId, setSelectedKbId] = useState<string>('');
|
|
const [detailDialogOpen, setDetailDialogOpen] = useState(false);
|
|
|
|
// Migration dialog state
|
|
const [migrationDialogOpen, setMigrationDialogOpen] = useState(false);
|
|
const [migrationInternalCount, setMigrationInternalCount] = useState(0);
|
|
const [migrationExternalCount, setMigrationExternalCount] = useState(0);
|
|
|
|
useEffect(() => {
|
|
getKnowledgeBaseList();
|
|
checkMigrationStatus();
|
|
}, []);
|
|
|
|
async function checkMigrationStatus() {
|
|
try {
|
|
const resp = await httpClient.getRagMigrationStatus();
|
|
if (resp.needed) {
|
|
setMigrationInternalCount(resp.internal_kb_count);
|
|
setMigrationExternalCount(resp.external_kb_count);
|
|
setMigrationDialogOpen(true);
|
|
}
|
|
} catch {
|
|
// Silently ignore - migration check is non-critical
|
|
}
|
|
}
|
|
|
|
async function getKnowledgeBaseList() {
|
|
const resp = await httpClient.getKnowledgeBases();
|
|
|
|
const currentTime = new Date();
|
|
|
|
const kbs = resp.bases.map((kb: KnowledgeBase) => {
|
|
const lastUpdatedTimeAgo = Math.floor(
|
|
(currentTime.getTime() -
|
|
new Date(kb.updated_at ?? currentTime.getTime()).getTime()) /
|
|
1000 /
|
|
60 /
|
|
60 /
|
|
24,
|
|
);
|
|
|
|
const lastUpdatedTimeAgoText =
|
|
lastUpdatedTimeAgo > 0
|
|
? ` ${lastUpdatedTimeAgo} ${t('knowledge.daysAgo')}`
|
|
: t('knowledge.today');
|
|
|
|
return new KnowledgeBaseVO({
|
|
id: kb.uuid || '',
|
|
name: kb.name,
|
|
description: kb.description,
|
|
emoji: kb.emoji,
|
|
lastUpdatedTimeAgo: lastUpdatedTimeAgoText,
|
|
ragEngine: kb.knowledge_engine,
|
|
ragEnginePluginId: kb.knowledge_engine_plugin_id,
|
|
});
|
|
});
|
|
|
|
setKnowledgeBaseList(kbs);
|
|
}
|
|
|
|
const handleKBCardClick = (kbId: string) => {
|
|
setSelectedKbId(kbId);
|
|
setDetailDialogOpen(true);
|
|
};
|
|
|
|
const handleCreateKBClick = () => {
|
|
setSelectedKbId('');
|
|
setDetailDialogOpen(true);
|
|
};
|
|
|
|
const handleFormCancel = () => {
|
|
setDetailDialogOpen(false);
|
|
};
|
|
|
|
const handleKbDeleted = () => {
|
|
getKnowledgeBaseList();
|
|
setDetailDialogOpen(false);
|
|
};
|
|
|
|
const handleNewKbCreated = (newKbId: string) => {
|
|
getKnowledgeBaseList();
|
|
setSelectedKbId(newKbId);
|
|
setDetailDialogOpen(true);
|
|
};
|
|
|
|
const handleKbUpdated = () => {
|
|
getKnowledgeBaseList();
|
|
};
|
|
|
|
const handleMigrationComplete = () => {
|
|
getKnowledgeBaseList();
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<KBMigrationDialog
|
|
open={migrationDialogOpen}
|
|
onOpenChange={setMigrationDialogOpen}
|
|
internalKbCount={migrationInternalCount}
|
|
externalKbCount={migrationExternalCount}
|
|
onMigrationComplete={handleMigrationComplete}
|
|
/>
|
|
|
|
<KBDetailDialog
|
|
open={detailDialogOpen}
|
|
onOpenChange={setDetailDialogOpen}
|
|
kbId={selectedKbId || undefined}
|
|
onFormCancel={handleFormCancel}
|
|
onKbDeleted={handleKbDeleted}
|
|
onNewKbCreated={handleNewKbCreated}
|
|
onKbUpdated={handleKbUpdated}
|
|
/>
|
|
|
|
<div className={styles.knowledgeListContainer}>
|
|
<CreateCardComponent
|
|
width={'100%'}
|
|
height={'10rem'}
|
|
plusSize={'90px'}
|
|
onClick={handleCreateKBClick}
|
|
/>
|
|
|
|
{knowledgeBaseList.map((kb) => {
|
|
return (
|
|
<div key={kb.id} onClick={() => handleKBCardClick(kb.id)}>
|
|
<KBCard kbCardVO={kb} />
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|