Spaces:
Running
Running
/** | |
* | |
* Copyright 2023-2025 InspectorRAGet Team | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
**/ | |
'use client'; | |
import { isEmpty } from 'lodash'; | |
import DOMPurify from 'dompurify'; | |
import parse from 'html-react-parser'; | |
import { useMemo, useState } from 'react'; | |
import { | |
Tabs, | |
TabList, | |
Tab, | |
TabPanels, | |
TabPanel, | |
ContainedList, | |
ContainedListItem, | |
} from '@carbon/react'; | |
import { Model, TaskEvaluation, Task, Metric, MessageStep } from '@/src/types'; | |
import { useDataStore } from '@/src/store'; | |
import { truncate } from '@/src/utilities/strings'; | |
import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable'; | |
import ChatLine from '@/src/components/chatline/ChatLine'; | |
import ChatTaskCopierModal from '@/src/components/task-copier/ChatTaskCopier'; | |
import classes from './ChatTask.module.scss'; | |
// =================================================================================== | |
// TYPES | |
// =================================================================================== | |
interface Props { | |
task: Task; | |
models: Map<string, Model>; | |
metrics: Metric[]; | |
taskCopierModalOpen: boolean; | |
setTaskCopierModalOpen: Function; | |
updateCommentProvenance: Function; | |
} | |
// =================================================================================== | |
// RENDER FUNCTIONS | |
// =================================================================================== | |
function Evaluation({ | |
evaluation, | |
hMetrics, | |
aMetrics, | |
}: { | |
evaluation: TaskEvaluation; | |
hMetrics: Map<string, Metric>; | |
aMetrics: Map<string, Metric>; | |
}) { | |
return ( | |
<div className={classes.evaluationContainer}> | |
{evaluation.annotations && hMetrics.size ? ( | |
<> | |
<h5>Human Evaluations:</h5> | |
<AnnotationsTable | |
annotations={evaluation.annotations} | |
metrics={[...hMetrics.values()]} | |
></AnnotationsTable> | |
</> | |
) : null} | |
{evaluation.annotations && aMetrics.size ? ( | |
<> | |
<h5>Algorithmic Evaluations:</h5> | |
<AnnotationsTable | |
annotations={evaluation.annotations} | |
metrics={[...aMetrics.values()]} | |
></AnnotationsTable> | |
</> | |
) : null} | |
</div> | |
); | |
} | |
function Steps({ steps }: { steps?: MessageStep[] }) { | |
return ( | |
<div className={classes.stepsContainer}> | |
{steps && !isEmpty(steps) ? ( | |
<></> | |
) : ( | |
<h4>No steps information is available.</h4> | |
)} | |
</div> | |
); | |
} | |
// =================================================================================== | |
// MAIN FUNCTION | |
// =================================================================================== | |
export default function ChatTask({ | |
task, | |
models, | |
metrics, | |
taskCopierModalOpen, | |
setTaskCopierModalOpen, | |
updateCommentProvenance, | |
}: Props) { | |
// Step 1: Initialize state and necessary variables | |
const [selectedEvaluationIndex, setSelectedEvaluationIndex] = | |
useState<number>(0); | |
// Step 2: Run effects | |
// Step 2.a: Fetch data from data store | |
const { item: data } = useDataStore(); | |
// Step 2.b: Fetch documents and evaluations | |
const evaluations = useMemo(() => { | |
// Step 2.b.i: Fetch evaluations | |
let taskEvaluations: TaskEvaluation[] | undefined = undefined; | |
if (data) { | |
taskEvaluations = data.evaluations.filter( | |
(evaluation) => evaluation.taskId === task.taskId, | |
); | |
} | |
return taskEvaluations; | |
}, [task.taskId, data]); | |
// Step 2.c: Build human & algorithmic metric maps | |
const [hMetrics, aMetrics] = useMemo(() => { | |
const humanMetrics = new Map( | |
metrics | |
?.filter((metric) => metric.author === 'human') | |
.map((metric) => [metric.name, metric]), | |
); | |
const algorithmicMetrics = new Map( | |
metrics | |
?.filter((metric) => metric.author === 'algorithm') | |
.map((metric) => [metric.name, metric]), | |
); | |
return [humanMetrics, algorithmicMetrics]; | |
}, [metrics]); | |
// Step 3: Render | |
return ( | |
<> | |
{models && metrics && task && evaluations && ( | |
<ChatTaskCopierModal | |
open={taskCopierModalOpen} | |
models={Array.from(models.values())} | |
metrics={metrics} | |
task={task} | |
evaluations={evaluations} | |
onClose={() => { | |
setTaskCopierModalOpen(false); | |
}} | |
></ChatTaskCopierModal> | |
)} | |
{task && models && evaluations && ( | |
<> | |
<div className={classes.inputContainer}> | |
{Array.isArray(task.input) | |
? task.input.map((message, messageIdx) => ( | |
<ChatLine | |
key={`data_point__message--${messageIdx}`} | |
messageId={`data_point__message--${messageIdx}`} | |
message={message} | |
onSelection={updateCommentProvenance} | |
focused={ | |
Array.isArray(task.input) && | |
messageIdx === task.input.length - 1 | |
? true | |
: false | |
} | |
latestResponse={ | |
Array.isArray(task.input) && | |
messageIdx === task.input.length - 1 | |
? true | |
: false | |
} | |
></ChatLine> | |
)) | |
: null} | |
</div> | |
<div className={classes.separator} /> | |
<div className={classes.modelsContainer}> | |
<Tabs | |
onChange={(e) => { | |
setSelectedEvaluationIndex(e.selectedIndex); | |
}} | |
> | |
<TabList aria-label="Models tab" contained> | |
{evaluations.map((evaluation) => ( | |
<Tab key={'model-' + evaluation.modelId}> | |
{truncate( | |
models.get(evaluation.modelId)?.name || | |
evaluation.modelId, | |
15, | |
)} | |
</Tab> | |
))} | |
</TabList> | |
<TabPanels> | |
{evaluations.map((evaluation) => ( | |
<TabPanel key={'model-' + evaluation.modelId + '-panel'}> | |
<div className={classes.tabContainer}> | |
<div className={classes.tabContentHeader}> | |
<h5>Model:</h5> | |
<span> | |
{models.get(evaluation.modelId)?.name || | |
evaluation.modelId} | |
</span> | |
</div> | |
<ContainedList | |
size="sm" | |
label="Response" | |
kind="disclosed" | |
> | |
<ContainedListItem> | |
<div | |
className={classes.responseContainer} | |
onMouseDown={() => { | |
updateCommentProvenance( | |
`${evaluation.modelId}::evaluation::response`, | |
); | |
}} | |
onMouseUp={() => | |
updateCommentProvenance( | |
`${evaluation.modelId}::evaluation::response`, | |
) | |
} | |
> | |
{parse( | |
DOMPurify.sanitize(evaluation.modelResponse), | |
)} | |
</div> | |
</ContainedListItem> | |
</ContainedList> | |
{task.targets && !isEmpty(task.targets) ? ( | |
<ContainedList | |
label="Targets" | |
kind="disclosed" | |
size="sm" | |
> | |
{task.targets.length > 1 ? ( | |
task.targets.map((target, targetIdx) => | |
target.text ? ( | |
<ContainedListItem key={`target--${targetIdx}`}> | |
<span> | |
Target {targetIdx + 1}: {target.text} | |
</span> | |
</ContainedListItem> | |
) : null, | |
) | |
) : ( | |
<ContainedListItem key={`target--0`}> | |
<span>{task.targets[0].text}</span> | |
</ContainedListItem> | |
)} | |
</ContainedList> | |
) : null} | |
<Tabs> | |
<TabList | |
aria-label="Model performance tab" | |
contained | |
fullWidth | |
> | |
<Tab | |
key={'model-' + evaluation.modelId + '-evaluations'} | |
> | |
Evaluations | |
</Tab> | |
<Tab key={'model-' + evaluation.modelId + '-steps'}> | |
Steps | |
</Tab> | |
</TabList> | |
<TabPanels> | |
<TabPanel | |
key={ | |
'model-' + | |
evaluation.modelId + | |
'-evaluations-panel' | |
} | |
> | |
<Evaluation | |
evaluation={evaluation} | |
hMetrics={hMetrics} | |
aMetrics={aMetrics} | |
/> | |
</TabPanel> | |
<TabPanel | |
key={'model-' + evaluation.modelId + '-steps-panel'} | |
> | |
<Steps steps={evaluation.steps} /> | |
</TabPanel> | |
</TabPanels> | |
</Tabs> | |
</div> | |
</TabPanel> | |
))} | |
</TabPanels> | |
</Tabs> | |
</div> | |
</> | |
)} | |
</> | |
); | |
} | |