mirror of
https://github.com/dataelement/bisheng.git
synced 2026-09-21 12:43:36 +08:00
Merge branch 'feat/2.0.0' of github.com:dataelement/bisheng into feat/2.0.0
This commit is contained in:
@@ -229,17 +229,22 @@ class SOPManageService:
|
||||
name = sheet.cell(row=i, column=1).value
|
||||
description = sheet.cell(row=i, column=2).value
|
||||
content = sheet.cell(row=i, column=3).value
|
||||
error_msg = []
|
||||
if not name:
|
||||
error_rows.append(f"• 第{i}行: 缺少名称")
|
||||
elif not content:
|
||||
error_rows.append(f"• 第{i}行: 缺少详细内容内容")
|
||||
elif len(str(name)) >= 500:
|
||||
error_rows.append(f"• 第{i}行: 名称长度超过500字符")
|
||||
elif len(str(content)) >= 50000:
|
||||
error_rows.append(f"• 第{i}行: 详细内容长度超过50000字符")
|
||||
elif description and len(str(description)) >= 1000:
|
||||
error_rows.append(f"• 第{i}行: 描述长度超过1000字符")
|
||||
elif name and content:
|
||||
error_msg.append("缺少名称")
|
||||
if not content:
|
||||
error_msg.append("缺少详细内容")
|
||||
if len(str(name)) >= 500:
|
||||
error_msg.append("名称长度超过500字符")
|
||||
if len(str(content)) >= 50000:
|
||||
error_msg.append("详细内容长度超过50000字符")
|
||||
if description and len(str(description)) >= 1000:
|
||||
error_msg.append("描述长度超过1000字符")
|
||||
|
||||
if error_msg:
|
||||
error_msg = "、".join(error_msg)
|
||||
error_rows.extend(f"• 第{i}行: {error_msg}")
|
||||
else:
|
||||
success_rows.append({
|
||||
"name": str(name),
|
||||
"description": str(description) if description is not None else "",
|
||||
|
||||
@@ -46,7 +46,7 @@ class LinsightAgent(BaseModel):
|
||||
knowledge_list_str = ""
|
||||
if knowledge_list:
|
||||
knowledge_list_str = "\n".join(knowledge_list)
|
||||
knowledge_list_str = f"<知识库列表>\n{knowledge_list_str}\n</<知识库列表>>"
|
||||
knowledge_list_str = f"<知识库列表>\n{knowledge_list_str}\n</知识库列表>"
|
||||
return knowledge_list_str
|
||||
|
||||
async def _parse_sop_content(self, sop_prompt: str) -> AsyncIterator[ChatGenerationChunk]:
|
||||
|
||||
@@ -129,14 +129,14 @@ const SopFormDrawer = ({
|
||||
<div className="flex flex-col ">
|
||||
<div className="flex items-center justify-between px-4 pt-5 border-gray-200">
|
||||
<SheetTitle className="text-lg font-medium text-gray-900">
|
||||
{isEditing ? '编辑SOP' : '新建SOP'}
|
||||
{isEditing ? '编辑指导手册' : '新建指导手册'}
|
||||
</SheetTitle>
|
||||
</div>
|
||||
<div className="flex-1 px-4 pb-4 pt-3">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="sop-name" className="block text-sm font-medium pb-1 text-gray-700">
|
||||
SOP名称<span className="text-red-500">*</span>
|
||||
指导手册名称<span className="text-red-500">*</span>
|
||||
</label>
|
||||
< Input
|
||||
type="text"
|
||||
@@ -147,7 +147,7 @@ const SopFormDrawer = ({
|
||||
value={sopForm.name}
|
||||
onChange={(e) => handleInputChange('name', e.target.value)}
|
||||
className={`mt-1 block w-full border ${errors.name ? 'border-red-500' : 'border-gray-300'} rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-[16px]`}
|
||||
placeholder="请输入SOP名称"
|
||||
placeholder="请输入指导手册名称"
|
||||
/>
|
||||
<div className="flex justify-between">
|
||||
{errors.name && (
|
||||
@@ -167,7 +167,7 @@ const SopFormDrawer = ({
|
||||
value={sopForm.description}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-[16px]"
|
||||
placeholder="请输入SOP描述"
|
||||
placeholder="请输入指导手册描述"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -247,14 +247,14 @@ const fetchRecords = async (isSearch = false) => {
|
||||
{/* 左侧记录列表 */}
|
||||
<div className="p-6 w-[50%] min-w-160">
|
||||
<SheetHeader>
|
||||
<SheetTitle>从运行记录中导入SOP</SheetTitle>
|
||||
<SheetTitle>从运行记录中导入指导手册</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="relative mt-6 mb-6 w-[80%]">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索SOP"
|
||||
placeholder="搜索指导手册"
|
||||
className="w-full pl-10 pr-4 py-2 rounded-md border focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
@@ -466,7 +466,7 @@ const fetchRecords = async (isSearch = false) => {
|
||||
}}
|
||||
disabled={!currentRecord || loading}
|
||||
>
|
||||
{loading ? '导入中...' : '导入当前SOP'}
|
||||
{loading ? '导入中...' : '导入当前指导手册'}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// src/features/chat-config/ChatConfig.tsx
|
||||
import { Button } from "@/components/bs-ui/button";
|
||||
import { Card, CardContent } from "@/components/bs-ui/card";
|
||||
import { toast, useToast } from "@/components/bs-ui/toast/use-toast";
|
||||
import { message, toast, useToast } from "@/components/bs-ui/toast/use-toast";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import SopFormDrawer from "@/components/LinSight/SopFormDrawer";
|
||||
import ImportFromRecordsDialog from "@/components/LinSight/SopFromRecord";
|
||||
@@ -27,6 +27,8 @@ import { locationContext } from "@/contexts/locationContext";
|
||||
import { UploadIcon } from "@/components/bs-icons";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/bs-ui/dialog";
|
||||
import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
|
||||
import sampleData from "@/pages/ModelPage/components/sampleData";
|
||||
import { downloadJson } from "@/util/utils";
|
||||
|
||||
|
||||
export interface FormErrors {
|
||||
@@ -709,14 +711,17 @@ const [importFormData, setImportFormData] = useState<FormData | null>(null);
|
||||
});
|
||||
}, []);
|
||||
const { getRootProps: getLocalFileRootProps, getInputProps: getLocalFileInputProps } = useDropzone({
|
||||
accept: {
|
||||
'application/*': appConfig.enableEtl4lm ?
|
||||
['.PDF', '.TXT', '.DOCX', '.PPT', '.PPTX', '.MD', '.HTML', '.XLS', '.XLSX', '.CSV', '.DOC', '.PNG', '.JPG', '.JPEG', '.BMP']
|
||||
: ['.PDF', '.TXT', '.DOCX', '.DOC', '.PPT', '.PPTX', '.MD', '.HTML', '.XLS', '.XLSX', '.CSV']
|
||||
},
|
||||
multiple: true, // 允许选择多个文件
|
||||
multiple: false,
|
||||
onDrop: (acceptedFiles) => {
|
||||
setImportFiles(prev => [...prev, ...acceptedFiles]); // 追加新文件
|
||||
if (acceptedFiles.length === 0) return;
|
||||
|
||||
const file = acceptedFiles[0];
|
||||
const ext = file.name.split('.').pop().toLowerCase();
|
||||
if (ext !== 'xlsx') {
|
||||
message({ variant: 'warning', description: '请上传xlsx格式的文件' });
|
||||
return;
|
||||
}
|
||||
setImportFiles(acceptedFiles);
|
||||
}
|
||||
});
|
||||
const handleLocalFileImport = async () => {
|
||||
@@ -749,7 +754,13 @@ const handleLocalFileImport = async () => {
|
||||
setDuplicateDialogOpen(true);
|
||||
setImportFormData(formData);
|
||||
}else{
|
||||
sopApi.getSopList({
|
||||
page_size: pageSize,
|
||||
page: 1,
|
||||
keywords: keywords,
|
||||
})
|
||||
toast({ variant: 'success', description: '提交成功' });
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,6 +790,11 @@ const handleValidationDialogConfirm = async () => {
|
||||
setImportFormData(formData);
|
||||
return
|
||||
}else{
|
||||
sopApi.getSopList({
|
||||
page_size: pageSize,
|
||||
page: 1,
|
||||
keywords: keywords,
|
||||
})
|
||||
toast({ variant: 'success', description: '提交成功' });
|
||||
}
|
||||
};
|
||||
@@ -827,13 +843,13 @@ const handleValidationDialogConfirm = async () => {
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<p className="text-lg font-bold mb-2">灵思SOP库</p>
|
||||
<p className="text-lg font-bold mb-2">灵思指导手册库</p>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="relative flex-1 max-w-xs">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索SOP"
|
||||
placeholder="搜索指导手册"
|
||||
className="w-full pl-10 pr-3 py-1.5 text-sm border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={keywords}
|
||||
onChange={(e) => {
|
||||
@@ -883,7 +899,7 @@ const handleValidationDialogConfirm = async () => {
|
||||
setIsDrawerOpen(true);
|
||||
}}
|
||||
>
|
||||
新建SOP
|
||||
新建指导手册
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -976,7 +992,7 @@ const handleValidationDialogConfirm = async () => {
|
||||
<Dialog open={localFileDialogOpen} onOpenChange={setLocalFileDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[1200px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>导入SOP</DialogTitle>
|
||||
<DialogTitle>导入指导手册</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="grid gap-4 py-4">
|
||||
@@ -985,23 +1001,23 @@ const handleValidationDialogConfirm = async () => {
|
||||
<span>请上传文件</span>
|
||||
<button
|
||||
className="text-blue-600 hover:underline ml-auto"
|
||||
|
||||
onClick={() => downloadJson(sampleData)}
|
||||
>
|
||||
<span className="text-black">示例文件:</span>
|
||||
SOP导入格式示例.xlsx
|
||||
用户指导手册格式示例.xlsx
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
{...getLocalFileRootProps()}
|
||||
className="group h-48 border border-dashed rounded-md flex flex-col justify-center items-center cursor-pointer gap-3 hover:border-primary"
|
||||
className="group h-40 border border-dashed rounded-md flex flex-col justify-center items-center cursor-pointer gap-3 hover:border-primary"
|
||||
>
|
||||
<input {...getLocalFileInputProps()} />
|
||||
<UploadIcon className="group-hover:text-primary size-5" />
|
||||
<p className="text-sm">{t('code.clickOrDragHere')}</p>
|
||||
{importFiles.length > 0 && (
|
||||
<div className="w-full max-h-32 overflow-y-auto">
|
||||
{importFiles.slice(0, 1).map((file, index) => ( // 只显示一个文件
|
||||
{importFiles.slice(0, 1).map((file, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-1 bg-gray-200 mt-14">
|
||||
<span className="text-sm text-gray-600 truncate max-w-xs">
|
||||
{file.name}
|
||||
@@ -1009,7 +1025,7 @@ const handleValidationDialogConfirm = async () => {
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setImportFiles([]); // 清空文件列表
|
||||
setImportFiles([]);
|
||||
}}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
@@ -1035,6 +1051,7 @@ const handleValidationDialogConfirm = async () => {
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await handleLocalFileImport(); // 等待导入完成
|
||||
setImportFiles([])
|
||||
setLocalFileDialogOpen(false); // 关闭弹窗
|
||||
}}
|
||||
disabled={isImporting || importFiles.length === 0}
|
||||
|
||||
@@ -20,6 +20,9 @@ export const WebSearchConfig = ({
|
||||
onChange: (field: string, value: any) => void;
|
||||
errors?: Record<string, any>;
|
||||
}) => {
|
||||
|
||||
console.log(config,88);
|
||||
|
||||
// Store parameters for all tools separately
|
||||
const [toolsParams, setToolsParams] = useState<Record<string, any>>({
|
||||
bing: { api_key: '', base_url: '' },
|
||||
@@ -31,6 +34,7 @@ export const WebSearchConfig = ({
|
||||
|
||||
// Initialize toolsParams with existing config
|
||||
useEffect(() => {
|
||||
console.log(config,333);
|
||||
if (config.tool && config.params) {
|
||||
setToolsParams(prev => ({
|
||||
...prev,
|
||||
@@ -54,7 +58,7 @@ export const WebSearchConfig = ({
|
||||
<Label className="bisheng-label">联网搜索提示词</Label>
|
||||
<div className="mt-3">
|
||||
<Textarea
|
||||
value={config.prompt}
|
||||
value={config.prompt||config}
|
||||
className="min-h-48"
|
||||
onChange={(e) => onChange('prompt', e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -370,8 +370,9 @@ const handleWebSearchChange = useCallback((field: string, value: any) => {
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{console.log(1111,webSearchData, formData.webSearch.prompt)}
|
||||
<WebSearchConfig
|
||||
config={webSearchData || formData.webSearch}
|
||||
config={ formData.webSearch.prompt}
|
||||
onChange={handleWebSearchChange}
|
||||
/>
|
||||
</ToggleSection>
|
||||
@@ -426,6 +427,8 @@ const handleWebSearchChange = useCallback((field: string, value: any) => {
|
||||
<DialogTitle>联网搜索配置</DialogTitle>
|
||||
</DialogHeader>
|
||||
<WebSearchForm
|
||||
prompt={ formData.webSearch.prompt}
|
||||
enabled={formData.webSearch.enabled}
|
||||
formData={formData.webSearch}
|
||||
onSubmit={handleWebSearchSave}
|
||||
/>
|
||||
|
||||
@@ -29,7 +29,7 @@ const defaultToolParams = {
|
||||
},
|
||||
};
|
||||
|
||||
const WebSearchForm = ({ formData, onSubmit, errors = {} }) => {
|
||||
const WebSearchForm = ({ formData, onSubmit, errors = {},enabled,prompt }) => {
|
||||
const { t } = useTranslation();
|
||||
const { config: webSearchData, setConfig } = useWebSearchStore();
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -134,9 +134,10 @@ const WebSearchForm = ({ formData, onSubmit, errors = {} }) => {
|
||||
}
|
||||
|
||||
const newConfig = {
|
||||
enabled: true,
|
||||
enabled,
|
||||
type: selectedTool,
|
||||
config: allToolsConfig
|
||||
config: allToolsConfig,
|
||||
prompt
|
||||
};
|
||||
try {
|
||||
setConfig(newConfig);
|
||||
|
||||
Reference in New Issue
Block a user