mixvideo-v2/apps/desktop/src/components/RagConfigManager.tsx

252 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* RAG配置管理组件
* 提供可视化的RAG检索参数配置界面
*/
import React, { useState, useEffect } from 'react';
import { RagGroundingConfig } from '../types/ragGrounding';
import { RagConfigOptimizer, RagConfigPresets } from '../utils/ragConfigOptimizer';
interface RagConfigManagerProps {
config: RagGroundingConfig;
onConfigChange: (config: RagGroundingConfig) => void;
onClose?: () => void;
}
export const RagConfigManager: React.FC<RagConfigManagerProps> = ({
config,
onConfigChange,
onClose
}) => {
const [currentConfig, setCurrentConfig] = useState<RagGroundingConfig>(config);
const [selectedScenario, setSelectedScenario] = useState<string>('BALANCED');
const [customPresets, setCustomPresets] = useState<Record<string, RagGroundingConfig>>({});
useEffect(() => {
setCustomPresets(RagConfigPresets.getPresets());
}, []);
const handleScenarioChange = (scenarioKey: string) => {
setSelectedScenario(scenarioKey);
const scenario = RagConfigOptimizer.SCENARIOS[scenarioKey];
if (scenario) {
const newConfig = RagConfigOptimizer.applyScenario(currentConfig, scenario);
setCurrentConfig(newConfig);
onConfigChange(newConfig);
}
};
const handleConfigUpdate = (updates: Partial<RagGroundingConfig>) => {
const newConfig = { ...currentConfig, ...updates };
setCurrentConfig(newConfig);
onConfigChange(newConfig);
};
const handleSavePreset = () => {
const name = prompt('请输入预设名称:');
if (name && name.trim()) {
RagConfigPresets.savePreset(name.trim(), currentConfig);
setCustomPresets(RagConfigPresets.getPresets());
}
};
const handleLoadPreset = (presetName: string) => {
const preset = RagConfigPresets.getPreset(presetName);
if (preset) {
setCurrentConfig(preset);
onConfigChange(preset);
}
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-bold text-gray-800">RAG检索配置管理</h2>
{onClose && (
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
>
</button>
)}
</div>
{/* 预设场景选择 */}
<div className="mb-6">
<h3 className="text-lg font-semibold mb-3"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{Object.entries(RagConfigOptimizer.SCENARIOS).map(([key, scenario]) => (
<button
key={key}
onClick={() => handleScenarioChange(key)}
className={`p-3 rounded-lg border text-left transition-colors ${
selectedScenario === key
? 'border-blue-500 bg-blue-50'
: 'border-gray-300 hover:border-gray-400'
}`}
>
<div className="font-medium text-gray-800">{scenario.name}</div>
<div className="text-sm text-gray-600 mt-1">{scenario.description}</div>
</button>
))}
</div>
</div>
{/* 详细配置 */}
<div className="mb-6">
<h3 className="text-lg font-semibold mb-3"></h3>
<div className="space-y-4">
{/* 最大检索结果数量 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
(5-50)
</label>
<input
type="number"
min="5"
max="50"
value={currentConfig.max_retrieval_results || 20}
onChange={(e) => handleConfigUpdate({
max_retrieval_results: parseInt(e.target.value)
})}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<p className="text-xs text-gray-500 mt-1">
</p>
</div>
{/* 相关性阈值 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
(0.1-0.9)
</label>
<input
type="number"
min="0.1"
max="0.9"
step="0.1"
value={currentConfig.relevance_threshold || 0.4}
onChange={(e) => handleConfigUpdate({
relevance_threshold: parseFloat(e.target.value)
})}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<p className="text-xs text-gray-500 mt-1">
</p>
</div>
{/* 搜索过滤器 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
()
</label>
<input
type="text"
value={currentConfig.search_filter || ''}
onChange={(e) => handleConfigUpdate({
search_filter: e.target.value || undefined
})}
placeholder="例如: category: ANY(&quot;服装&quot;, &quot;搭配&quot;)"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<p className="text-xs text-gray-500 mt-1">
使Vertex AI Search过滤器语法限制搜索范围
</p>
</div>
{/* 包含摘要 */}
<div>
<label className="flex items-center">
<input
type="checkbox"
checked={currentConfig.include_summary || false}
onChange={(e) => handleConfigUpdate({
include_summary: e.target.checked
})}
className="mr-2"
/>
<span className="text-sm font-medium text-gray-700"></span>
</label>
<p className="text-xs text-gray-500 mt-1">
AI理解上下文
</p>
</div>
</div>
</div>
{/* 配置说明 */}
<div className="mb-6 p-3 bg-blue-50 rounded-lg">
<h4 className="font-medium text-blue-800 mb-1"></h4>
<p className="text-sm text-blue-700">
{RagConfigOptimizer.getConfigExplanation(currentConfig)}
</p>
</div>
{/* 自定义预设管理 */}
<div className="mb-6">
<h3 className="text-lg font-semibold mb-3"></h3>
<div className="flex gap-2 mb-3">
<button
onClick={handleSavePreset}
className="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 transition-colors"
>
</button>
</div>
{Object.keys(customPresets).length > 0 && (
<div className="space-y-2">
{Object.keys(customPresets).map((presetName) => (
<div key={presetName} className="flex items-center justify-between p-2 bg-gray-50 rounded">
<span className="text-sm font-medium">{presetName}</span>
<div className="flex gap-2">
<button
onClick={() => handleLoadPreset(presetName)}
className="px-3 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600"
>
</button>
<button
onClick={() => {
RagConfigPresets.deletePreset(presetName);
setCustomPresets(RagConfigPresets.getPresets());
}}
className="px-3 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
>
</button>
</div>
</div>
))}
</div>
)}
</div>
{/* 操作按钮 */}
<div className="flex justify-end gap-3">
{onClose && (
<button
onClick={onClose}
className="px-4 py-2 text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
>
</button>
)}
<button
onClick={() => {
onConfigChange(currentConfig);
onClose?.();
}}
className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors"
>
</button>
</div>
</div>
</div>
);
};