mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
perf(rag): ui and related apis
This commit is contained in:
@@ -20,7 +20,7 @@ class KnowledgeBaseRouterGroup(group.RouterGroup):
|
||||
|
||||
@self.route(
|
||||
'/<knowledge_base_uuid>',
|
||||
methods=['GET', 'DELETE'],
|
||||
methods=['GET', 'DELETE', 'PUT'],
|
||||
)
|
||||
async def handle_specific_knowledge_base(knowledge_base_uuid: str) -> quart.Response:
|
||||
if quart.request.method == 'GET':
|
||||
@@ -34,6 +34,12 @@ class KnowledgeBaseRouterGroup(group.RouterGroup):
|
||||
'base': knowledge_base,
|
||||
}
|
||||
)
|
||||
|
||||
elif quart.request.method == 'PUT':
|
||||
json_data = await quart.request.json
|
||||
await self.ap.knowledge_service.update_knowledge_base(knowledge_base_uuid, json_data)
|
||||
return self.success({})
|
||||
|
||||
elif quart.request.method == 'DELETE':
|
||||
await self.ap.knowledge_service.delete_knowledge_base(knowledge_base_uuid)
|
||||
return self.success({})
|
||||
|
||||
@@ -47,12 +47,18 @@ class KnowledgeService:
|
||||
|
||||
async def update_knowledge_base(self, kb_uuid: str, kb_data: dict) -> None:
|
||||
"""更新知识库"""
|
||||
if 'uuid' in kb_data:
|
||||
del kb_data['uuid']
|
||||
|
||||
if 'embedding_model_uuid' in kb_data:
|
||||
del kb_data['embedding_model_uuid']
|
||||
|
||||
await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.update(persistence_rag.KnowledgeBase)
|
||||
.values(kb_data)
|
||||
.where(persistence_rag.KnowledgeBase.uuid == kb_uuid)
|
||||
)
|
||||
await self.ap.rag_mgr.remove_knowledge_base(kb_uuid)
|
||||
await self.ap.rag_mgr.remove_knowledge_base_from_runtime(kb_uuid)
|
||||
|
||||
kb = await self.get_knowledge_base(kb_uuid)
|
||||
|
||||
@@ -91,7 +97,7 @@ class KnowledgeService:
|
||||
|
||||
async def delete_knowledge_base(self, kb_uuid: str) -> None:
|
||||
"""删除知识库"""
|
||||
await self.ap.rag_mgr.remove_knowledge_base(kb_uuid)
|
||||
await self.ap.rag_mgr.delete_knowledge_base(kb_uuid)
|
||||
|
||||
await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.delete(persistence_rag.KnowledgeBase).where(persistence_rag.KnowledgeBase.uuid == kb_uuid)
|
||||
|
||||
@@ -33,6 +33,9 @@ class LocalAgentRunner(runner.RequestRunner):
|
||||
|
||||
kb_uuid = query.pipeline_config['ai']['local-agent']['knowledge-base']
|
||||
|
||||
if kb_uuid == '__none__':
|
||||
kb_uuid = None
|
||||
|
||||
user_message = copy.deepcopy(query.user_message)
|
||||
|
||||
user_message_text = ''
|
||||
|
||||
@@ -198,7 +198,13 @@ class RAGManager:
|
||||
return kb
|
||||
return None
|
||||
|
||||
async def remove_knowledge_base(self, kb_uuid: str):
|
||||
async def remove_knowledge_base_from_runtime(self, kb_uuid: str):
|
||||
for kb in self.knowledge_bases:
|
||||
if kb.knowledge_base_entity.uuid == kb_uuid:
|
||||
self.knowledge_bases.remove(kb)
|
||||
return
|
||||
|
||||
async def delete_knowledge_base(self, kb_uuid: str):
|
||||
for kb in self.knowledge_bases:
|
||||
if kb.knowledge_base_entity.uuid == kb_uuid:
|
||||
await kb.dispose()
|
||||
|
||||
@@ -5,6 +5,7 @@ from chromadb import PersistentClient
|
||||
from pkg.vector.vdb import VectorDatabase
|
||||
from pkg.core import app
|
||||
import chromadb
|
||||
import chromadb.errors
|
||||
|
||||
|
||||
class ChromaVectorDatabase(VectorDatabase):
|
||||
@@ -51,5 +52,10 @@ class ChromaVectorDatabase(VectorDatabase):
|
||||
async def delete_collection(self, collection: str):
|
||||
if collection in self._collections:
|
||||
del self._collections[collection]
|
||||
await asyncio.to_thread(self.client.delete_collection, name=collection)
|
||||
|
||||
try:
|
||||
await asyncio.to_thread(self.client.delete_collection, name=collection)
|
||||
except chromadb.errors.NotFoundError:
|
||||
self.ap.logger.warning(f"Chroma collection '{collection}' not found.")
|
||||
return
|
||||
self.ap.logger.info(f"Chroma collection '{collection}' deleted.")
|
||||
|
||||
@@ -72,6 +72,9 @@ stages:
|
||||
label:
|
||||
en_US: Knowledge Base
|
||||
zh_Hans: 知识库
|
||||
description:
|
||||
en_US: Configure the knowledge base to use for the agent, if not selected, the agent will directly use the LLM to reply
|
||||
zh_Hans: 配置用于提升回复质量的知识库,若不选择,则直接使用大模型回复
|
||||
type: knowledge-base-selector
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function DynamicFormItemComponent({
|
||||
});
|
||||
}
|
||||
}, [config.type]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (config.type === DynamicFormItemType.KNOWLEDGE_BASE_SELECTOR) {
|
||||
httpClient
|
||||
@@ -272,28 +272,11 @@ export default function DynamicFormItemComponent({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{/* <SelectItem value="">{t('knowledge.empty')}</SelectItem> */}
|
||||
<SelectItem value="__none__">{t('knowledge.empty')}</SelectItem>
|
||||
{knowledgeBases.map((base) => (
|
||||
<HoverCard key={base.uuid} openDelay={0} closeDelay={0}>
|
||||
<HoverCardTrigger asChild>
|
||||
<SelectItem value={base.uuid ?? ''}>{base.name}</SelectItem>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent
|
||||
className="w-80 data-[state=open]:animate-none data-[state=closed]:animate-none"
|
||||
align="end"
|
||||
side="right"
|
||||
sideOffset={10}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="font-medium">{base.name}</h4>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{base.description}
|
||||
</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
<SelectItem key={base.uuid} value={base.uuid ?? ''}>
|
||||
{base.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
|
||||
@@ -36,6 +36,7 @@ interface KBDetailDialogProps {
|
||||
onFormCancel: () => void;
|
||||
onKbDeleted: () => void;
|
||||
onNewKbCreated: (kbId: string) => void;
|
||||
onKbUpdated: (kbId: string) => void;
|
||||
}
|
||||
|
||||
export default function KBDetailDialog({
|
||||
@@ -46,6 +47,7 @@ export default function KBDetailDialog({
|
||||
onFormCancel,
|
||||
onKbDeleted,
|
||||
onNewKbCreated,
|
||||
onKbUpdated,
|
||||
}: KBDetailDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [kbId, setKbId] = useState<string | undefined>(propKbId);
|
||||
@@ -111,6 +113,7 @@ export default function KBDetailDialog({
|
||||
onFormCancel={onFormCancel}
|
||||
onKbDeleted={onKbDeleted}
|
||||
onNewKbCreated={onNewKbCreated}
|
||||
onKbUpdated={onKbUpdated}
|
||||
/>
|
||||
)}
|
||||
{activeMenu === 'documents' && <div>documents</div>}
|
||||
@@ -185,6 +188,7 @@ export default function KBDetailDialog({
|
||||
onFormCancel={onFormCancel}
|
||||
onKbDeleted={onKbDeleted}
|
||||
onNewKbCreated={onNewKbCreated}
|
||||
onKbUpdated={onKbUpdated}
|
||||
/>
|
||||
)}
|
||||
{activeMenu === 'documents' && <KBDoc kbId={kbId} />}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function KBDoc({ kbId }: { kbId: string }) {
|
||||
setDocumentsList(
|
||||
resp.files.map((file: KnowledgeBaseFile) => {
|
||||
return {
|
||||
id: file.id,
|
||||
uuid: file.uuid,
|
||||
name: file.file_name,
|
||||
status: file.status,
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type DocumentFile = {
|
||||
id: string;
|
||||
uuid: string;
|
||||
name: string;
|
||||
status: string;
|
||||
};
|
||||
@@ -52,7 +52,7 @@ export const columns = (
|
||||
{t('knowledge.documentsTab.actions')}
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuItem onClick={() => onDelete(document.id)}>
|
||||
<DropdownMenuItem onClick={() => onDelete(document.uuid)}>
|
||||
{t('knowledge.documentsTab.delete')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { KnowledgeBase } from '@/app/infra/entities/api';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const getFormSchema = (t: (key: string) => string) =>
|
||||
z.object({
|
||||
@@ -42,6 +43,7 @@ export default function KBForm({
|
||||
onFormCancel,
|
||||
onKbDeleted,
|
||||
onNewKbCreated,
|
||||
onKbUpdated,
|
||||
}: {
|
||||
initKbId?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -49,6 +51,7 @@ export default function KBForm({
|
||||
onFormCancel: () => void;
|
||||
onKbDeleted: () => void;
|
||||
onNewKbCreated: (kbId: string) => void;
|
||||
onKbUpdated: (kbId: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const formSchema = getFormSchema(t);
|
||||
@@ -114,6 +117,17 @@ export default function KBForm({
|
||||
description: data.description,
|
||||
embedding_model_uuid: data.embeddingModelUUID,
|
||||
};
|
||||
httpClient
|
||||
.updateKnowledgeBase(initKbId, updateKb)
|
||||
.then((res) => {
|
||||
console.log('update knowledge base success', res);
|
||||
onKbUpdated(res.uuid);
|
||||
toast.success(t('knowledge.updateKnowledgeBaseSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('update knowledge base failed', err);
|
||||
toast.error(t('knowledge.updateKnowledgeBaseFailed'));
|
||||
});
|
||||
} else {
|
||||
// create knowledge base
|
||||
const newKb: KnowledgeBase = {
|
||||
@@ -186,6 +200,7 @@ export default function KBForm({
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Select
|
||||
disabled={!!initKbId}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
console.log('value', value);
|
||||
@@ -210,7 +225,9 @@ export default function KBForm({
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t('knowledge.embeddingModelDescription')}
|
||||
{initKbId
|
||||
? t('knowledge.cannotChangeEmbeddingModel')
|
||||
: t('knowledge.embeddingModelDescription')}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -81,6 +81,10 @@ export default function KnowledgePage() {
|
||||
setDetailDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleKbUpdated = (kbId: string) => {
|
||||
getKnowledgeBaseList();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<KBDetailDialog
|
||||
@@ -91,6 +95,7 @@ export default function KnowledgePage() {
|
||||
onFormCancel={handleFormCancel}
|
||||
onKbDeleted={handleKbDeleted}
|
||||
onNewKbCreated={handleNewKbCreated}
|
||||
onKbUpdated={handleKbUpdated}
|
||||
/>
|
||||
|
||||
<div className={styles.knowledgeListContainer}>
|
||||
|
||||
@@ -164,7 +164,7 @@ export interface ApiRespKnowledgeBaseFiles {
|
||||
}
|
||||
|
||||
export interface KnowledgeBaseFile {
|
||||
id: string;
|
||||
uuid: string;
|
||||
file_name: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@@ -459,6 +459,13 @@ class HttpClient {
|
||||
return this.post('/api/v1/knowledge/bases', base);
|
||||
}
|
||||
|
||||
public updateKnowledgeBase(
|
||||
uuid: string,
|
||||
base: KnowledgeBase,
|
||||
): Promise<{ uuid: string }> {
|
||||
return this.put(`/api/v1/knowledge/bases/${uuid}`, base);
|
||||
}
|
||||
|
||||
public uploadKnowledgeBaseFile(
|
||||
uuid: string,
|
||||
file_id: string,
|
||||
|
||||
@@ -253,6 +253,10 @@ const enUS = {
|
||||
embeddingModelDescription:
|
||||
'Used to vectorize the text, you can configure it in the Models page',
|
||||
updateTime: 'Updated ',
|
||||
cannotChangeEmbeddingModel:
|
||||
'Knowledge base created cannot be modified embedding model',
|
||||
updateKnowledgeBaseSuccess: 'Knowledge base updated successfully',
|
||||
updateKnowledgeBaseFailed: 'Knowledge base update failed',
|
||||
documentsTab: {
|
||||
name: 'Name',
|
||||
status: 'Status',
|
||||
|
||||
@@ -255,6 +255,10 @@ const jaJP = {
|
||||
embeddingModelDescription:
|
||||
'テキストのベクトル化に使用する埋め込みモデルを管理します',
|
||||
updateTime: '更新日時',
|
||||
cannotChangeEmbeddingModel:
|
||||
'知識ベース作成後は埋め込みモデルを変更できません',
|
||||
updateKnowledgeBaseSuccess: '知識ベースの更新に成功しました',
|
||||
updateKnowledgeBaseFailed: '知識ベースの更新に失敗しました',
|
||||
documentsTab: {
|
||||
name: '名前',
|
||||
status: 'ステータス',
|
||||
|
||||
@@ -247,6 +247,9 @@ const zhHans = {
|
||||
selectEmbeddingModel: '选择嵌入模型',
|
||||
embeddingModelDescription: '用于向量化文本,可在模型配置页面配置',
|
||||
updateTime: '更新于',
|
||||
cannotChangeEmbeddingModel: '知识库创建后不可修改嵌入模型',
|
||||
updateKnowledgeBaseSuccess: '知识库更新成功',
|
||||
updateKnowledgeBaseFailed: '知识库更新失败',
|
||||
documentsTab: {
|
||||
name: '名称',
|
||||
status: '状态',
|
||||
|
||||
Reference in New Issue
Block a user