Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/components/map-projects/ConfigurationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { SCORES_COLOR } from './constants'
import FilterTable from './FilterTable'
import MultiAlgoSelector from './MultiAlgoSelector'
import LookupConfig from './LookupConfig'
import RerankerConfig from './RerankerConfig'

const VisuallyHiddenInput = styled('input')({
clip: 'rect(0 0 0 0)',
Expand All @@ -46,7 +47,7 @@ const VisuallyHiddenInput = styled('input')({
});


const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, name, setName, description, setDescription, repo, onRepoChange, repoVersion, setRepoVersion, versions, mappedSources, targetSourcesFromRows, algosSelected, setAlgosSelected, sx, algos, validColumns, columns, isValidColumnValue, updateColumn, configure, setConfigure, columnVisibilityModel, setColumnVisibilityModel, onSave, isSaving, candidatesScore, onScoreChange, includeDefaultFilter, setIncludeDefaultFilter, filters, setFilters, locales, isLoadingLocales, setAIAssistantColumns, AIAssistantColumns, inAIAssistantGroup, lookupConfig, setLookupConfig, canBridge, canScispacy }) => {
const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, name, setName, description, setDescription, repo, onRepoChange, repoVersion, setRepoVersion, versions, mappedSources, targetSourcesFromRows, algosSelected, setAlgosSelected, sx, algos, validColumns, columns, isValidColumnValue, updateColumn, configure, setConfigure, columnVisibilityModel, setColumnVisibilityModel, onSave, isSaving, candidatesScore, onScoreChange, includeDefaultFilter, setIncludeDefaultFilter, filters, setFilters, locales, isLoadingLocales, setAIAssistantColumns, AIAssistantColumns, inAIAssistantGroup, lookupConfig, setLookupConfig, rerankerConfig, setRerankerConfig, isCoreUser, canBridge, canScispacy }) => {
const { t } = useTranslation();
const isLLMAlgoNotAllowed = !repoVersion?.match_algorithms?.includes('llm')
const appliedLocales = filters?.locale ? filters?.locale?.split(',') : []
Expand Down Expand Up @@ -222,6 +223,10 @@ const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, n
onChange={setAlgosSelected}
repo={repoVersion}
/>
{
isCoreUser &&
<RerankerConfig value={rerankerConfig} onChange={setRerankerConfig} />
}
<>
<Typography component="div" sx={{fontSize: '16px', fontWeight: 'bold', marginTop: '20px'}}>
{t('map_project.score_configuration')}
Expand Down
5 changes: 4 additions & 1 deletion src/components/map-projects/Discuss.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ const Discuss = ({ logs, onAdd }) => {
}
if(log.action === 'algo_finished')
return <>{t('map_project.finished_running')} <b>{log.extras.algo}</b></>
if(log.action === 'rerank_finished')
if(log.action === 'rerank_finished') {
if(log.description)
return log.description
return <>{t('map_project.finished_reranking')}</>
}
return log.description || startCase(log.action)
}

Expand Down
24 changes: 19 additions & 5 deletions src/components/map-projects/MapProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import ImportToCollection from './ImportToCollection'
import ProjectLogs from './ProjectLogs';
import { useAlgos } from './algorithms'
import AutoMatchDialog from './AutoMatchDialog'
import { DEFAULT_ENCODER_MODEL } from './rerankerModels'

import './MapProject.scss'
import '../common/ResizablePanel.scss'
Expand Down Expand Up @@ -209,6 +210,7 @@ const MapProject = () => {
const [analysis, setAnalysis] = React.useState({})
const [AIModels, setAIModels] = React.useState([])
const [lookupConfig, setLookupConfig] = React.useState({})
const [encoderModel, setEncoderModel] = React.useState(DEFAULT_ENCODER_MODEL)

// import
const [openImportToCollection, setOpenImportToCollection] = React.useState(false)
Expand Down Expand Up @@ -422,6 +424,7 @@ const MapProject = () => {
setRetired(Boolean(response.data?.include_retired))
setCandidatesScore(response.data?.score_configuration)
setLookupConfig(response.data?.lookup_config)
setEncoderModel(response.data?.encoder_model || DEFAULT_ENCODER_MODEL)
setAnalysis(response.data?.analysis || {})
setProject(response.data)
setConfigure(false)
Expand Down Expand Up @@ -866,6 +869,7 @@ const MapProject = () => {
formData.append('algorithms', JSON.stringify(map(algosSelected, algo => omit(algo, ['__key']))))
formData.append('score_configuration', JSON.stringify(candidatesScore))
formData.append('lookup_config', JSON.stringify(lookupConfig))
formData.append('encoder_model', encoderModel)
formData.append('include_retired', retired)
formData.append('filters', JSON.stringify(getFilters()))
const isUpdate = Boolean(project?.id)
Expand Down Expand Up @@ -1098,7 +1102,8 @@ const MapProject = () => {
includeMappings: true,
mappingBrief: true,
mapTypes: 'SAME-AS,SAME AS,SAME_AS',
reranker: !isMultiAlgo
reranker: !isMultiAlgo,
...(encoderModel ? { encoder_model: encoderModel } : {})
}

forEach(rowBatch, __row => markAlgo(__row.__index, algo.id, 0))
Expand Down Expand Up @@ -2040,7 +2045,8 @@ const MapProject = () => {
limit: algoDef.limit || CANDIDATES_LIMIT,
offset: offset || 0,
semantic: ['ocl-semantic', 'custom'].includes(algoDef.type),
reranker: !isMultiAlgo && algoDef.provider === 'ocl'
reranker: !isMultiAlgo && algoDef.provider === 'ocl',
encoder_model: !isMultiAlgo && encoderModel ? encoderModel : undefined
}).then(response => callback(response, payload))
}

Expand Down Expand Up @@ -2201,7 +2207,11 @@ const MapProject = () => {
markAlgo(index, 'rerank', 0)
const service = APIService.concepts().appendToUrl('$rerank/')
try {
const response = await service.post({q: query, rows: candidates,});
const response = await service.post({
q: query,
rows: candidates,
...(encoderModel ? { encoder_model: encoderModel } : {})
});

setAllCandidates(prev => {
const newCandidates = {...prev}
Expand All @@ -2223,12 +2233,12 @@ const MapProject = () => {
return newCandidates
})
markAlgo(index, 'rerank', 1)
log({action: 'rerank_finished'}, index)
log({action: 'rerank_finished', description: `Reranked with ${encoderModel}`}, index)
if(isBulk)
setTimeout(() => setAutoMatched([index]), 1000)
return response
} catch (e) {
log({action: 'rerank_failed'}, index)
log({action: 'rerank_failed', description: `Rerank failed with ${encoderModel}`}, index)
markAlgo(index, 'rerank', -2); // optional: failed state
return null;
}
Expand Down Expand Up @@ -2510,6 +2520,7 @@ const MapProject = () => {
filters: filters,
fields_mapped: cols,
score_configuration: candidatesScore,
encoder_model: encoderModel,
target_repo: repo
}
}
Expand Down Expand Up @@ -2627,6 +2638,9 @@ const MapProject = () => {
inAIAssistantGroup={inAIAssistantGroup}
lookupConfig={lookupConfig}
setLookupConfig={setLookupConfig}
rerankerConfig={encoderModel}
setRerankerConfig={setEncoderModel}
isCoreUser={isCoreUser}
/>
)

Expand Down
97 changes: 97 additions & 0 deletions src/components/map-projects/RerankerConfig.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import Autocomplete from '@mui/material/Autocomplete'
import TextField from '@mui/material/TextField'
import Collapse from '@mui/material/Collapse'
import Button from '@mui/material/Button'
import FormHelperText from '@mui/material/FormHelperText'
import ListItemText from '@mui/material/ListItemText'
import UpIcon from '@mui/icons-material/ArrowDropUp';
import DownIcon from '@mui/icons-material/ArrowDropDown';
import { CUSTOM_ENCODER_MODEL_OPTION, DEFAULT_ENCODER_MODEL, ENCODER_MODEL_OPTIONS } from './rerankerModels'

const RerankerConfig = ({ value, onChange }) => {
const { t } = useTranslation()
const [open, setOpen] = React.useState(false)
const presetOptions = ENCODER_MODEL_OPTIONS.map(model => ({
id: model.id,
label: model.description,
isDefault: Boolean(model.default),
disabled: Boolean(model.disabled)
}))
const options = [
...presetOptions,
{ id: CUSTOM_ENCODER_MODEL_OPTION, label: t('map_project.reranker_configuration_custom_option') }
]
const isKnownOption = ENCODER_MODEL_OPTIONS.map(option => option.id).includes(value)
const selectedOption = isKnownOption ?
presetOptions.find(option => option.id === value) :
options.find(option => option.id === CUSTOM_ENCODER_MODEL_OPTION)
const isCustomSelected = selectedOption?.id === CUSTOM_ENCODER_MODEL_OPTION

return (
<div className='col-xs-12 padding-0' style={{ marginBottom: '16px', marginTop: '4px' }}>
<Button
size='small'
variant='text'
color={(value || open) ? 'primary' : 'secondary'}
endIcon={open ? <UpIcon fontSize='inherit' /> : <DownIcon fontSize='inherit' />}
onClick={() => setOpen(!open)}
sx={{ textTransform: 'none' }}
>
{t('map_project.reranker_configuration')}
</Button>
<Collapse in={open}>
<div className='col-xs-12 padding-0'>
<FormHelperText sx={{ marginBottom: '12px', marginTop: '-4px', paddingLeft: '4px' }}>
{t('map_project.reranker_configuration_description')}
</FormHelperText>
<Autocomplete
disableClearable
options={options}
value={selectedOption || null}
getOptionLabel={option => option?.id || ''}
isOptionEqualToValue={(option, current) => option.id === current.id}
getOptionDisabled={(option) => option?.disabled}
onChange={(event, option) => {
if(option?.id === CUSTOM_ENCODER_MODEL_OPTION) {
onChange(isKnownOption ? '' : value)
return
}
onChange(option?.id || DEFAULT_ENCODER_MODEL)
}}
renderInput={params => (
<TextField
{...params}
label={t('map_project.reranker_configuration_model')}
fullWidth
/>
)}
renderOption={(props, option) => (
<ListItemText
{...props}
key={option.id}
sx={{flexDirection: 'column', alignItems: 'flex-start !important'}}
primary={`${option.id}${option.isDefault ? ` (${t('common.default')})` : ''}`}
secondary={option.label}
/>
)}
/>
{
isCustomSelected &&
<TextField
sx={{ marginTop: '12px' }}
label={t('map_project.reranker_configuration_custom_model')}
fullWidth
value={value || ''}
placeholder={t('map_project.reranker_configuration_placeholder')}
onChange={event => onChange(event.target.value || '')}
/>
}
</div>
</Collapse>
</div>
)
}

export default RerankerConfig
32 changes: 32 additions & 0 deletions src/components/map-projects/rerankerModels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const DEFAULT_ENCODER_MODEL = 'BAAI/bge-reranker-v2-m3'

export const ENCODER_MODEL_OPTIONS = [
{
id: DEFAULT_ENCODER_MODEL,
description: 'Multilingual, general-purpose (0.6B)',
default: true
},
{
id: 'Qwen/Qwen3-Reranker-0.6B',
description: 'General purpose reranker (0.6B)',
},
{
id: 'Qwen/Qwen3-Reranker-4B',
description: 'General purpose reranker, best in quality (4B)',
disabled: true
},
{
id: 'cross-encoder/ms-marco-MiniLM-L-6-v2',
description: 'Fast and lightweight, English-only (23M)',
},
{
id: 'ncbi/MedCPT-Cross-Encoder',
description: 'Biomedical domain, trained on PubMed (110M)',
},
{
id: 'Alibaba-NLP/gte-reranker-modernbert-base',
description: 'Balanced quality, supports longer descriptions (149M)',
},
]

export const CUSTOM_ENCODER_MODEL_OPTION = '__custom_encoder_model__'
6 changes: 6 additions & 0 deletions src/i18n/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,12 @@
"lookup_configuration_url": "Repository URL",
"lookup_configuration_token": "Token",
"lookup_configuration_description": "Configure a Source/CodeSystem for lookup operation to fetch candidates definitions.",
"reranker_configuration": "Reranker Configuration",
"reranker_configuration_description": "Choose the reranker model used to calculate unified scores for this project. The default model is selected automatically, or you can enter a custom model name.",
"reranker_configuration_model": "Reranker model",
"reranker_configuration_custom_option": "Custom model name",
"reranker_configuration_custom_model": "Custom reranker model",
"reranker_configuration_placeholder": "e.g. BAAI/bge-reranker-v2-m3",
"refresh_candidates_tooltip": "Refresh Candidates",
"group_candidates": "Group By",
"sort_candidates": "Sort",
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/locales/es/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,12 @@
"lookup_configuration_url": "URL del repositorio",
"lookup_configuration_token": "Token",
"lookup_configuration_description": "Configure una Fuente/Sistema de códigos para la operación de búsqueda y recuperar definiciones de candidatos.",
"reranker_configuration": "Configuracion del reranker",
"reranker_configuration_description": "Elija el modelo de reranker utilizado para calcular las puntuaciones unificadas de este proyecto. El modelo predeterminado se selecciona automaticamente, o puede ingresar un nombre de modelo personalizado.",
"reranker_configuration_model": "Modelo de reranker",
"reranker_configuration_custom_option": "Nombre de modelo personalizado",
"reranker_configuration_custom_model": "Modelo de reranker personalizado",
"reranker_configuration_placeholder": "p. ej. BAAI/bge-reranker-v2-m3",
"refresh_candidates_tooltip": "Actualizar candidatos",
"group_candidates": "Agrupar por",
"sort_candidates": "Ordenar",
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/locales/zh/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,12 @@
"lookup_configuration_url": "仓库 URL",
"lookup_configuration_token": "令牌",
"lookup_configuration_description": "配置用于查找操作的源/代码系统,以获取候选项定义。",
"reranker_configuration": "重排序器配置",
"reranker_configuration_description": "为此项目选择用于计算统一分数的重排序器模型。默认模型会自动选中,您也可以输入自定义模型名称。",
"reranker_configuration_model": "重排序器模型",
"reranker_configuration_custom_option": "自定义模型名称",
"reranker_configuration_custom_model": "自定义重排序器模型",
"reranker_configuration_placeholder": "例如 BAAI/bge-reranker-v2-m3",
"refresh_candidates_tooltip": "刷新候选项",
"group_candidates": "分组依据",
"sort_candidates": "排序",
Expand Down