Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/1600822305_aetherlink.json
T

1 line
25 KiB
JSON
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.
{"repo": "1600822305/aetherlink", "files": {"src/pages/Settings/AssistantModelSettings/VisionRecognitionTab.tsx": {"1b56c288492e5c5f51c6a29046220ff653ab983c": {"t": 1785595149.0954847, "n": 11590, "h": "e007c5f9362877b0", "c": "import React, { useState, useMemo } from 'react';\nimport {\n Box,\n Typography,\n TextField,\n Button,\n Alert,\n InputAdornment,\n IconButton,\n CircularProgress,\n FormControl,\n Select,\n MenuItem,\n} from '@mui/material';\nimport type { SelectChangeEvent } from '@mui/material';\nimport { Eye, EyeOff, CheckCircle2, XCircle, RefreshCw, RotateCcw } from 'lucide-react';\nimport { useSelector, useDispatch } from 'react-redux';\nimport { SettingGroup, Row, YStack } from '../../../components/settings/SettingComponents';\nimport CustomSwitch from '../../../components/CustomSwitch';\nimport type { RootState, AppDispatch } from '../../../shared/store';\nimport {\n setVisionEnabled,\n setVisionModelSource,\n setVisionPresetModelRef,\n updateVisionCustomConfig,\n setVisionPrompt,\n setVisionFailureStrategy,\n setVisionTimeoutMs,\n DEFAULT_VISION_PROMPT,\n type VisionModelSource,\n type VisionFailureStrategy,\n} from '../../../shared/store/slices/visionRecognitionSlice';\nimport { testVisionModelConnection, type VisionTestResult } from '../../../shared/services/vision/VisionRecognitionService';\nimport ModelPickerRow from './ModelPickerRow';\nimport { useAllModels, type ModelWithProvider } from './useAllModels';\n\n/** API 厂商预设(点击快捷填充 Base URL) */\nconst PROVIDER_PRESETS: Array<{ name: string; baseUrl: string }> = [\n { name: 'OpenAI', baseUrl: 'https://api.openai.com/v1' },\n { name: '小米 MiMo', baseUrl: 'https://api.xiaomimimo.com/v1' },\n { name: 'Kimi (Moonshot)', baseUrl: 'https://api.moonshot.cn/v1' },\n { name: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1' },\n { name: '硅基流动', baseUrl: 'https://api.siliconflow.cn/v1' },\n { name: '智谱 GLM', baseUrl: 'https://open.bigmodel.cn/api/paas/v4' },\n { name: '阿里云百炼', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1' },\n];\n\nconst VisionRecognitionTab: React.FC = () => {\n const dispatch = useDispatch<AppDispatch>();\n\n const visionState = useSelector((state: RootState) => state.visionRecognition);\n const allModels = useAllModels();\n\n const [showApiKey, setShowApiKey] = useState(false);\n const [isTesting, setIsTesting] = useState(false);\n const [testResult, setTestResult] = useState<VisionTestResult | null>(null);\n\n const selectedPresetModel = useMemo(() => {\n const ref = visionState.presetModelRef;\n if (!ref) return null;\n return allModels.find((model) => model.providerId === ref.providerId && model.id === ref.modelId) || null;\n }, [allModels, visionState.presetModelRef]);\n\n const handleModelSourceChange = (event: SelectChangeEvent) => {\n dispatch(setVisionModelSource(event.target.value as VisionModelSource));\n setTestResult(null);\n };\n\n const handlePresetModelSelect = (model: ModelWithProvider) => {\n dispatch(\n setVisionPresetModelRef({\n providerId: model.providerId || (model as any).provider,\n modelId: model.id,\n })\n );\n setTestResult(null);\n };\n\n const handleProviderPreset = (event: SelectChangeEvent) => {\n const preset = PROVIDER_PRESETS.find((p) => p.name === event.target.value);\n if (preset) {\n dispatch(updateVisionCustomConfig({ baseUrl: preset.baseUrl }));\n setTestResult(null);\n }\n };\n\n const handleCustomChange = (field: 'modelName' | 'baseUrl' | 'apiKey') =>\n (e: React.ChangeEvent<HTMLInputElement>) => {\n dispatch(updateVisionCustomConfig({ [field]: e.target.value }));\n setTestResult(null);\n };\n\n const handleTestConnection = async () => {\n setIsTesting(true);\n setTestResult(null);\n try {\n const result = await testVisionModelConnection(visionState);\n setTestResult(result);\n } finally {\n setIsTesting(false);\n }\n };\n\n const handleTimeoutChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n if (value === '') return;\n const seconds = parseInt(value, 10);\n if (!isNaN(seconds) && seconds >= 5 && seconds <= 600) {\n dispatch(setVisionTimeoutMs(seconds * 1000));\n }\n };\n\n const isCustom = visionState.modelSource === 'custom';\n const canTest = isCustom\n ? !!(visionState.custom.modelName && visionState.custom.baseUrl && visionState.custom.apiKey)\n : !!visionState.presetModelRef;\n\n return (\n <YStack sx={{ gap: 3 }}>\n\n {/* ==================== 总开关 ==================== */}\n <SettingGroup title=\"视觉识别\">\n <Row>\n <Box sx={{ flex: 1 }}>\n <Typography>启用视觉识别</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n 发送图片给不支持视觉的模型时,自动用视觉模型分析图片内容并提供给当前模型\n </Typography>\n </Box>\n <CustomSwitch\n checked={visionState.enabled}\n onChange={() => dispatch(setVisionEnabled(!visionState.enabled))}\n />\n </Row>\n </SettingGroup>\n\n {/* ==================== 模型来源 ==================== */}\n <SettingGroup title=\"视觉模型\">\n <Row>\n <Typography sx={{ flex: 1 }}>模型来源</Typography>\n <FormControl size=\"small\" sx={{ minWidth: 180 }}>\n <Select value={visionState.modelSource} onChange={handleModelSourceChange}>\n <MenuItem value=\"preset\">使用已配置的模型</MenuItem>\n <MenuItem value=\"custom\">自定义API配置</MenuItem>\n </Select>\n </FormControl>\n </Row>\n\n {!isCustom && (\n <ModelPickerRow\n label=\"视觉模型\"\n selectedModel={selectedPresetModel}\n availableModels={allModels}\n onSelect={handlePresetModelSelect}\n />\n )}\n\n {isCustom && (\n <>\n <Row>\n <Typography sx={{ minWidth: 80 }}>厂商预设</Typography>\n <FormControl size=\"small\" sx={{ flex: 1 }}>\n <Select value=\"\" onChange={handleProviderPreset} displayEmpty>\n <MenuItem value=\"\">\n <em>选择厂商快捷填充 Base URL</em>\n </MenuItem>\n {PROVIDER_PRESETS.map((preset) => (\n <MenuItem key={preset.name} value={preset.name}>\n {preset.name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Row>\n\n <Row>\n <Typography sx={{ minWidth: 80 }}>模型名称</Typography>\n <TextField\n size=\"small\"\n placeholder=\"如 gpt-4o-mini、moonshot-v1-8k-vision-preview\"\n value={visionState.custom.modelName}\n onChange={handleCustomChange('modelName')}\n sx={{ flex: 1 }}\n />\n </Row>\n\n <Row>\n <Typography sx={{ minWidth: 80 }}>Base URL</Typography>\n <TextField\n size=\"small\"\n placeholder=\"https://api.openai.com/v1\"\n value={visionState.custom.baseUrl}\n onChange={handleCustomChange('baseUrl')}\n sx={{ flex: 1 }}\n />\n </Row>\n\n <Row>\n <Typography sx={{ minWidth: 80 }}>API 密钥</Typography>\n <TextField\n size=\"small\"\n type={showApiKey ? 'text' : 'password'}\n placeholder=\"sk-...\"\n value={visionState.custom.apiKey}\n onChange={handleCustomChange('apiKey')}\n sx={{ flex: 1 }}\n InputProps={{\n endAdornment: (\n <InputAdornment position=\"end\">\n <IconButton size=\"small\" onClick={() => setShowApiKey(!showApiKey)} edge=\"end\">\n {showApiKey ? <EyeOff size={16} /> : <Eye size={16} />}\n </IconButton>\n </InputAdornment>\n ),\n }}\n />\n </Row>\n </>\n )}\n\n <Box sx={{ px: 2, pb: 2 }}>\n <Button\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n onClick={handleTestConnection}\n disabled={isTesting || !canTest}\n startIcon={isTesting ? <CircularProgress size={16} /> : <RefreshCw size={16} />}\n sx={{ textTransform: 'none' }}\n >\n {isTesting ? '测试中...' : '测试 API 连接'}\n </Button>\n\n {testResult && (\n <Alert\n severity={testResult.success ? 'success' : 'error'}\n icon={testResult.success ? <CheckCircle2 size={18} /> : <XCircle size={18} />}\n sx={{ mt: 1.5, borderRadius: 2 }}\n >\n <Typography variant=\"body2\">{testResult.message}</Typography>\n {testResult.success && testResult.latencyMs !== undefined && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n 响应时间: {testResult.latencyMs}ms\n </Typography>\n )}\n </Alert>\n )}\n </Box>\n </SettingGroup>\n\n {/* ==================== 高级设置 ==================== */}\n <SettingGroup title=\"高级设置\">\n <Row sx={{ alignItems: 'flex-start', flexDirection: 'column', gap: 1 }}>\n <Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>\n <Box sx={{ flex: 1 }}>\n <Typography>分析提示词</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n 指导视觉模型如何描述图片\n </Typography>\n </Box>\n <IconButton\n size=\"small\"\n onClick={() => dispatch(setVisionPrompt(DEFAULT_VISION_PROMPT))}\n title=\"恢复默认\"\n >\n <RotateCcw size={16} />\n </IconButton>\n </Box>\n <TextField\n fullWidth\n multiline\n minRows={3}\n maxRows={8}\n size=\"small\"\n value={visionState.prompt}\n onChange={(e) => dispatch(setVisionPrompt(e.target.value))}\n />\n </Row>\n\n <Row>\n <Box sx={{ flex: 1 }}>\n <Typography>分析失败时</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n 视觉模型分析失败后的处理方式\n </Typography>\n </Box>\n <FormControl size=\"small\" sx={{ minWidth: 160 }}>\n <Select\n value={visionState.onFailure}\n onChange={(e) => dispatch(setVisionFailureStrategy(e.target.value as VisionFailureStrategy))}\n >\n <MenuItem value=\"abort\">中止并报错</MenuItem>\n <MenuItem value=\"continueWithoutImage\">移除图片继续发送</MenuItem>\n </Select>\n </FormControl>\n </Row>\n\n <Row>\n <Typography sx={{ flex: 1 }}>分析超时(秒)</Typography>\n <TextField\n size=\"small\"\n value={Math.round(visionState.timeoutMs / 1000)}\n onChange={handleTimeoutChange}\n inputProps={{ inputMode: 'numeric', pattern: '[0-9]*' }}\n sx={{ width: 100 }}\n />\n </Row>\n </SettingGroup>\n\n {/* ==================== 配置说明 ==================== */}\n <Box sx={{ px: 1.5 }}>\n <YStack sx={{ gap: 0.75 }}>\n {[\n { label: '使用已配置的模型', desc: '从现有供应商中选择一个支持视觉的模型,复用其密钥与地址' },\n { label: '自定义API配置', desc: '独立填写模型名、Base URL 与密钥(OpenAI 兼容协议),与主API互不影响' },\n { label: '工作方式', desc: '仅当当前对话模型不支持图片时触发;分析结果只注入本次请求,聊天记录仍保留原图' },\n ].map((item) => (\n <Typography key={item.label} variant=\"caption\" color=\"text.secondary\">\n <strong>{item.label}</strong> — {item.desc}\n </Typography>\n ))}\n </YStack>\n </Box>\n\n </YStack>\n );\n};\n\nexport default VisionRecognitionTab;\n"}, "2fc9a7e75431a6871ebb5e881412987a1520ec7a": {"t": 1785764114.289419, "n": 11590, "h": "e007c5f9362877b0", "c": "import React, { useState, useMemo } from 'react';\nimport {\n Box,\n Typography,\n TextField,\n Button,\n Alert,\n InputAdornment,\n IconButton,\n CircularProgress,\n FormControl,\n Select,\n MenuItem,\n} from '@mui/material';\nimport type { SelectChangeEvent } from '@mui/material';\nimport { Eye, EyeOff, CheckCircle2, XCircle, RefreshCw, RotateCcw } from 'lucide-react';\nimport { useSelector, useDispatch } from 'react-redux';\nimport { SettingGroup, Row, YStack } from '../../../components/settings/SettingComponents';\nimport CustomSwitch from '../../../components/CustomSwitch';\nimport type { RootState, AppDispatch } from '../../../shared/store';\nimport {\n setVisionEnabled,\n setVisionModelSource,\n setVisionPresetModelRef,\n updateVisionCustomConfig,\n setVisionPrompt,\n setVisionFailureStrategy,\n setVisionTimeoutMs,\n DEFAULT_VISION_PROMPT,\n type VisionModelSource,\n type VisionFailureStrategy,\n} from '../../../shared/store/slices/visionRecognitionSlice';\nimport { testVisionModelConnection, type VisionTestResult } from '../../../shared/services/vision/VisionRecognitionService';\nimport ModelPickerRow from './ModelPickerRow';\nimport { useAllModels, type ModelWithProvider } from './useAllModels';\n\n/** API 厂商预设(点击快捷填充 Base URL) */\nconst PROVIDER_PRESETS: Array<{ name: string; baseUrl: string }> = [\n { name: 'OpenAI', baseUrl: 'https://api.openai.com/v1' },\n { name: '小米 MiMo', baseUrl: 'https://api.xiaomimimo.com/v1' },\n { name: 'Kimi (Moonshot)', baseUrl: 'https://api.moonshot.cn/v1' },\n { name: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1' },\n { name: '硅基流动', baseUrl: 'https://api.siliconflow.cn/v1' },\n { name: '智谱 GLM', baseUrl: 'https://open.bigmodel.cn/api/paas/v4' },\n { name: '阿里云百炼', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1' },\n];\n\nconst VisionRecognitionTab: React.FC = () => {\n const dispatch = useDispatch<AppDispatch>();\n\n const visionState = useSelector((state: RootState) => state.visionRecognition);\n const allModels = useAllModels();\n\n const [showApiKey, setShowApiKey] = useState(false);\n const [isTesting, setIsTesting] = useState(false);\n const [testResult, setTestResult] = useState<VisionTestResult | null>(null);\n\n const selectedPresetModel = useMemo(() => {\n const ref = visionState.presetModelRef;\n if (!ref) return null;\n return allModels.find((model) => model.providerId === ref.providerId && model.id === ref.modelId) || null;\n }, [allModels, visionState.presetModelRef]);\n\n const handleModelSourceChange = (event: SelectChangeEvent) => {\n dispatch(setVisionModelSource(event.target.value as VisionModelSource));\n setTestResult(null);\n };\n\n const handlePresetModelSelect = (model: ModelWithProvider) => {\n dispatch(\n setVisionPresetModelRef({\n providerId: model.providerId || (model as any).provider,\n modelId: model.id,\n })\n );\n setTestResult(null);\n };\n\n const handleProviderPreset = (event: SelectChangeEvent) => {\n const preset = PROVIDER_PRESETS.find((p) => p.name === event.target.value);\n if (preset) {\n dispatch(updateVisionCustomConfig({ baseUrl: preset.baseUrl }));\n setTestResult(null);\n }\n };\n\n const handleCustomChange = (field: 'modelName' | 'baseUrl' | 'apiKey') =>\n (e: React.ChangeEvent<HTMLInputElement>) => {\n dispatch(updateVisionCustomConfig({ [field]: e.target.value }));\n setTestResult(null);\n };\n\n const handleTestConnection = async () => {\n setIsTesting(true);\n setTestResult(null);\n try {\n const result = await testVisionModelConnection(visionState);\n setTestResult(result);\n } finally {\n setIsTesting(false);\n }\n };\n\n const handleTimeoutChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n if (value === '') return;\n const seconds = parseInt(value, 10);\n if (!isNaN(seconds) && seconds >= 5 && seconds <= 600) {\n dispatch(setVisionTimeoutMs(seconds * 1000));\n }\n };\n\n const isCustom = visionState.modelSource === 'custom';\n const canTest = isCustom\n ? !!(visionState.custom.modelName && visionState.custom.baseUrl && visionState.custom.apiKey)\n : !!visionState.presetModelRef;\n\n return (\n <YStack sx={{ gap: 3 }}>\n\n {/* ==================== 总开关 ==================== */}\n <SettingGroup title=\"视觉识别\">\n <Row>\n <Box sx={{ flex: 1 }}>\n <Typography>启用视觉识别</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n 发送图片给不支持视觉的模型时,自动用视觉模型分析图片内容并提供给当前模型\n </Typography>\n </Box>\n <CustomSwitch\n checked={visionState.enabled}\n onChange={() => dispatch(setVisionEnabled(!visionState.enabled))}\n />\n </Row>\n </SettingGroup>\n\n {/* ==================== 模型来源 ==================== */}\n <SettingGroup title=\"视觉模型\">\n <Row>\n <Typography sx={{ flex: 1 }}>模型来源</Typography>\n <FormControl size=\"small\" sx={{ minWidth: 180 }}>\n <Select value={visionState.modelSource} onChange={handleModelSourceChange}>\n <MenuItem value=\"preset\">使用已配置的模型</MenuItem>\n <MenuItem value=\"custom\">自定义API配置</MenuItem>\n </Select>\n </FormControl>\n </Row>\n\n {!isCustom && (\n <ModelPickerRow\n label=\"视觉模型\"\n selectedModel={selectedPresetModel}\n availableModels={allModels}\n onSelect={handlePresetModelSelect}\n />\n )}\n\n {isCustom && (\n <>\n <Row>\n <Typography sx={{ minWidth: 80 }}>厂商预设</Typography>\n <FormControl size=\"small\" sx={{ flex: 1 }}>\n <Select value=\"\" onChange={handleProviderPreset} displayEmpty>\n <MenuItem value=\"\">\n <em>选择厂商快捷填充 Base URL</em>\n </MenuItem>\n {PROVIDER_PRESETS.map((preset) => (\n <MenuItem key={preset.name} value={preset.name}>\n {preset.name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Row>\n\n <Row>\n <Typography sx={{ minWidth: 80 }}>模型名称</Typography>\n <TextField\n size=\"small\"\n placeholder=\"如 gpt-4o-mini、moonshot-v1-8k-vision-preview\"\n value={visionState.custom.modelName}\n onChange={handleCustomChange('modelName')}\n sx={{ flex: 1 }}\n />\n </Row>\n\n <Row>\n <Typography sx={{ minWidth: 80 }}>Base URL</Typography>\n <TextField\n size=\"small\"\n placeholder=\"https://api.openai.com/v1\"\n value={visionState.custom.baseUrl}\n onChange={handleCustomChange('baseUrl')}\n sx={{ flex: 1 }}\n />\n </Row>\n\n <Row>\n <Typography sx={{ minWidth: 80 }}>API 密钥</Typography>\n <TextField\n size=\"small\"\n type={showApiKey ? 'text' : 'password'}\n placeholder=\"sk-...\"\n value={visionState.custom.apiKey}\n onChange={handleCustomChange('apiKey')}\n sx={{ flex: 1 }}\n InputProps={{\n endAdornment: (\n <InputAdornment position=\"end\">\n <IconButton size=\"small\" onClick={() => setShowApiKey(!showApiKey)} edge=\"end\">\n {showApiKey ? <EyeOff size={16} /> : <Eye size={16} />}\n </IconButton>\n </InputAdornment>\n ),\n }}\n />\n </Row>\n </>\n )}\n\n <Box sx={{ px: 2, pb: 2 }}>\n <Button\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n onClick={handleTestConnection}\n disabled={isTesting || !canTest}\n startIcon={isTesting ? <CircularProgress size={16} /> : <RefreshCw size={16} />}\n sx={{ textTransform: 'none' }}\n >\n {isTesting ? '测试中...' : '测试 API 连接'}\n </Button>\n\n {testResult && (\n <Alert\n severity={testResult.success ? 'success' : 'error'}\n icon={testResult.success ? <CheckCircle2 size={18} /> : <XCircle size={18} />}\n sx={{ mt: 1.5, borderRadius: 2 }}\n >\n <Typography variant=\"body2\">{testResult.message}</Typography>\n {testResult.success && testResult.latencyMs !== undefined && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n 响应时间: {testResult.latencyMs}ms\n </Typography>\n )}\n </Alert>\n )}\n </Box>\n </SettingGroup>\n\n {/* ==================== 高级设置 ==================== */}\n <SettingGroup title=\"高级设置\">\n <Row sx={{ alignItems: 'flex-start', flexDirection: 'column', gap: 1 }}>\n <Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>\n <Box sx={{ flex: 1 }}>\n <Typography>分析提示词</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n 指导视觉模型如何描述图片\n </Typography>\n </Box>\n <IconButton\n size=\"small\"\n onClick={() => dispatch(setVisionPrompt(DEFAULT_VISION_PROMPT))}\n title=\"恢复默认\"\n >\n <RotateCcw size={16} />\n </IconButton>\n </Box>\n <TextField\n fullWidth\n multiline\n minRows={3}\n maxRows={8}\n size=\"small\"\n value={visionState.prompt}\n onChange={(e) => dispatch(setVisionPrompt(e.target.value))}\n />\n </Row>\n\n <Row>\n <Box sx={{ flex: 1 }}>\n <Typography>分析失败时</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n 视觉模型分析失败后的处理方式\n </Typography>\n </Box>\n <FormControl size=\"small\" sx={{ minWidth: 160 }}>\n <Select\n value={visionState.onFailure}\n onChange={(e) => dispatch(setVisionFailureStrategy(e.target.value as VisionFailureStrategy))}\n >\n <MenuItem value=\"abort\">中止并报错</MenuItem>\n <MenuItem value=\"continueWithoutImage\">移除图片继续发送</MenuItem>\n </Select>\n </FormControl>\n </Row>\n\n <Row>\n <Typography sx={{ flex: 1 }}>分析超时(秒)</Typography>\n <TextField\n size=\"small\"\n value={Math.round(visionState.timeoutMs / 1000)}\n onChange={handleTimeoutChange}\n inputProps={{ inputMode: 'numeric', pattern: '[0-9]*' }}\n sx={{ width: 100 }}\n />\n </Row>\n </SettingGroup>\n\n {/* ==================== 配置说明 ==================== */}\n <Box sx={{ px: 1.5 }}>\n <YStack sx={{ gap: 0.75 }}>\n {[\n { label: '使用已配置的模型', desc: '从现有供应商中选择一个支持视觉的模型,复用其密钥与地址' },\n { label: '自定义API配置', desc: '独立填写模型名、Base URL 与密钥(OpenAI 兼容协议),与主API互不影响' },\n { label: '工作方式', desc: '仅当当前对话模型不支持图片时触发;分析结果只注入本次请求,聊天记录仍保留原图' },\n ].map((item) => (\n <Typography key={item.label} variant=\"caption\" color=\"text.secondary\">\n <strong>{item.label}</strong> — {item.desc}\n </Typography>\n ))}\n </YStack>\n </Box>\n\n </YStack>\n );\n};\n\nexport default VisionRecognitionTab;\n"}}}}