kpfadnis commited on
Commit
3939dda
·
1 Parent(s): d639f28

feat (chat task): Minor enhancements to chat task view.

Browse files

Signed-off-by: Kshitij Fadnis <[email protected]>

package.json CHANGED
@@ -43,7 +43,7 @@
43
  "@carbon/react": "1.75.0",
44
  "d3": "^7.8.4",
45
  "date-fns": "^2.30.0",
46
- "dompurify": "3.1.7",
47
  "html-react-parser": "^5.1.1",
48
  "lodash": "^4.17.21",
49
  "next": "^14.1.1",
 
43
  "@carbon/react": "1.75.0",
44
  "d3": "^7.8.4",
45
  "date-fns": "^2.30.0",
46
+ "dompurify": "3.2.4",
47
  "html-react-parser": "^5.1.1",
48
  "lodash": "^4.17.21",
49
  "next": "^14.1.1",
src/components/chatline/ChatLine.module.scss CHANGED
@@ -34,7 +34,7 @@
34
  }
35
 
36
  .latestResponse {
37
- margin-bottom: 3rem;
38
  }
39
 
40
  .baloon {
 
34
  }
35
 
36
  .latestResponse {
37
+ margin-bottom: $spacing-12;
38
  }
39
 
40
  .baloon {
src/components/chatline/ChatLine.tsx CHANGED
@@ -74,7 +74,7 @@ function ToolResponse({
74
  return (
75
  <div className={cx(classes.message, classes.toolResponse)}>
76
  <span>
77
- Tool ID: {message.tool_id}&nbsp;
78
  {message.name ? <span>({message.name})</span> : null}
79
  </span>
80
  {message.type === 'documents' && Array.isArray(message.content) ? (
 
74
  return (
75
  <div className={cx(classes.message, classes.toolResponse)}>
76
  <span>
77
+ Tool Call ID: {message.tool_call_id}&nbsp;
78
  {message.name ? <span>({message.name})</span> : null}
79
  </span>
80
  {message.type === 'documents' && Array.isArray(message.content) ? (
src/components/comments/AddCommentModal.tsx CHANGED
@@ -45,7 +45,10 @@ export default function AddCommentModal({
45
  const [author, setAuthor] = useState<string>('');
46
  const [tag, tagType] = useMemo(() => {
47
  if (provenance) {
48
- if (provenance.component.includes('input')) {
 
 
 
49
  return ['Input', 'purple'];
50
  } else if (provenance.component.includes('document_')) {
51
  return ['Contexts', 'cyan'];
 
45
  const [author, setAuthor] = useState<string>('');
46
  const [tag, tagType] = useMemo(() => {
47
  if (provenance) {
48
+ if (
49
+ provenance.component.includes('input') ||
50
+ provenance.component.includes('messages')
51
+ ) {
52
  return ['Input', 'purple'];
53
  } else if (provenance.component.includes('document_')) {
54
  return ['Contexts', 'cyan'];
src/components/comments/CommentsViewer.tsx CHANGED
@@ -56,7 +56,10 @@ function Comment({
56
 
57
  const [tag, tagType]: [string, string] = useMemo(() => {
58
  if (comment.provenance) {
59
- if (comment.provenance.component.includes('input')) {
 
 
 
60
  return ['Input', 'purple'];
61
  } else if (comment.provenance.component.includes('document_')) {
62
  return ['Contexts', 'cyan'];
 
56
 
57
  const [tag, tagType]: [string, string] = useMemo(() => {
58
  if (comment.provenance) {
59
+ if (
60
+ comment.provenance.component.includes('input') ||
61
+ comment.provenance.component.includes('messages')
62
+ ) {
63
  return ['Input', 'purple'];
64
  } else if (comment.provenance.component.includes('document_')) {
65
  return ['Contexts', 'cyan'];
src/components/task-copier/ChatTaskCopier.tsx ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ * Copyright 2023-2025 InspectorRAGet Team
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ **/
18
+
19
+ 'use client';
20
+
21
+ import { useState, useMemo } from 'react';
22
+ import { Modal, RadioTile, CodeSnippet } from '@carbon/react';
23
+
24
+ import { Metric, Model, Task, TaskEvaluation, Message } from '@/src/types';
25
+ import { WarningAlt } from '@carbon/icons-react';
26
+
27
+ import classes from './TaskCopier.module.scss';
28
+
29
+ interface Props {
30
+ models: Model[];
31
+ metrics: Metric[];
32
+ task: Task;
33
+ evaluations: TaskEvaluation[];
34
+ onClose: Function;
35
+ open: boolean;
36
+ }
37
+
38
+ function prepareTextForMessage(message: Message) {
39
+ // Step 1: Initialize necessary variable
40
+ let text = '';
41
+
42
+ // Step 2: Extract text from message object
43
+ // Step 2.a: If message with "tool" role
44
+ if (message.role === 'tool') {
45
+ text += JSON.stringify({
46
+ tool_call_id: message['tool_call_id'],
47
+ content: message.content,
48
+ });
49
+ }
50
+ // Step 2.b: If message with "assistant" role and with "tool_calls"
51
+ else if (
52
+ message.role === 'assistant' &&
53
+ message.hasOwnProperty('tool_calls')
54
+ ) {
55
+ text += JSON.stringify(message['tool_calls']);
56
+ }
57
+ // Step 2.c: Default extraction policy
58
+ else {
59
+ text +=
60
+ typeof message.content === 'string'
61
+ ? message.content
62
+ : JSON.stringify(message.content);
63
+ }
64
+
65
+ // Step 3: Return
66
+ return text;
67
+ }
68
+
69
+ function prepareText(
70
+ models: Model[],
71
+ metrics: Metric[],
72
+ task: Task,
73
+ evaluations: TaskEvaluation[],
74
+ ): string {
75
+ const separator = '=======================================================\n';
76
+ let input, responses;
77
+
78
+ // Step 1: Prepare input
79
+ input = `${separator}Input\n${separator}`;
80
+ if (Array.isArray(task.input)) {
81
+ task.input.map(
82
+ (message) =>
83
+ (input += `${message.role}: ${prepareTextForMessage(message)}\n`),
84
+ );
85
+ }
86
+
87
+ // Step 2: Prepare responses
88
+ if (evaluations && evaluations.length) {
89
+ responses = `${separator}Responses\n${separator}`;
90
+ const responseSeparator =
91
+ '\n-------------------------------------------------------\n';
92
+ evaluations.forEach((evaluation) => {
93
+ const model = models.find(
94
+ (entry) => entry.modelId === evaluation.modelId,
95
+ );
96
+ responses += `${model ? model.name.trim() : evaluation.modelId.trim()}${responseSeparator}${evaluation.modelResponse.trim()}\n${separator}`;
97
+ });
98
+ }
99
+
100
+ return `${input.trim()}\n${responses ? responses : ''}`;
101
+ }
102
+
103
+ function prepareLaTEXT(
104
+ models: Model[],
105
+ metrics: Metric[],
106
+ task: Task,
107
+ evaluations: TaskEvaluation[],
108
+ ): string {
109
+ let input, responses;
110
+
111
+ // Step 1: Prepare input
112
+ input =
113
+ '\\multicolumn{1}{|c|}{\\textbf{Conversation}} \\\\ \n\t\\toprule \n\t';
114
+ if (Array.isArray(task.input)) {
115
+ task.input.map(
116
+ (message) =>
117
+ (input += `\\textbf{${
118
+ message.role
119
+ }}: ${prepareTextForMessage(message)} \\\\ \n\t`),
120
+ );
121
+ }
122
+
123
+ // Step 2: Prepare responses
124
+ if (evaluations && evaluations.length) {
125
+ responses =
126
+ '\\toprule \n\t\\multicolumn{1}{|c|}{\\textbf{Responses}} \\\\ \n\t';
127
+ evaluations.forEach((evaluation) => {
128
+ const model = models.find(
129
+ (entry) => entry.modelId === evaluation.modelId,
130
+ );
131
+ responses += `\\toprule \n\t\\textbf{${model ? model.name.trim() : evaluation.modelId.trim()}} \\\\ \n\t\\midrule \n\t${evaluation.modelResponse.trim()} \\\\ \n\t`;
132
+ });
133
+ responses += '\\bottomrule \n\t';
134
+ }
135
+
136
+ return `\\begin{table*}\n\\small\n\t\\begin{tabular}{p{15.5cm}}\n\t\\toprule\n\t${input}${responses ? responses : ''}\\end{tabular}\n\\end{table*}`;
137
+ }
138
+
139
+ function prepareJSON(
140
+ models: Model[],
141
+ metrics: Metric[],
142
+ task: Task,
143
+ evaluations: TaskEvaluation[],
144
+ ): string {
145
+ return JSON.stringify(
146
+ {
147
+ input: task.input,
148
+ responses: evaluations.map((evaluation) => {
149
+ const model = models.find(
150
+ (entry) => entry.modelId === evaluation.modelId,
151
+ );
152
+ return {
153
+ model: model ? model.name : evaluation.modelId,
154
+ response: evaluation.modelResponse,
155
+ };
156
+ }),
157
+ },
158
+ null,
159
+ 2,
160
+ );
161
+ }
162
+
163
+ export default function ChatTaskCopierModal({
164
+ models,
165
+ metrics,
166
+ task,
167
+ evaluations,
168
+ onClose,
169
+ open = false,
170
+ }: Props) {
171
+ const [format, setFormat] = useState<'Text' | 'JSON' | 'LaTEX'>('Text');
172
+
173
+ const textToCopy = useMemo(() => {
174
+ let text;
175
+ if (format === 'Text') {
176
+ text = prepareText(models, metrics, task, evaluations);
177
+ } else if (format === 'LaTEX') {
178
+ text = prepareLaTEXT(models, metrics, task, evaluations);
179
+ } else {
180
+ text = prepareJSON(models, metrics, task, evaluations);
181
+ }
182
+
183
+ return text;
184
+ }, [models, metrics, task, evaluations, format]);
185
+
186
+ return (
187
+ <Modal
188
+ open={open}
189
+ modalLabel="Copy task details to clipboard"
190
+ primaryButtonText="Copy"
191
+ secondaryButtonText="Cancel"
192
+ onRequestSubmit={() => {
193
+ //Step 1: Copy to clipboard
194
+ navigator.clipboard.writeText(textToCopy);
195
+
196
+ // Step 2: Close model
197
+ onClose();
198
+ }}
199
+ onRequestClose={() => {
200
+ onClose();
201
+ }}
202
+ >
203
+ <div className={classes.container}>
204
+ <span className={classes.heading}>Select a format</span>
205
+ <div className={classes.copyFormatSelectors}>
206
+ <RadioTile
207
+ id={'formatSelector--text'}
208
+ value={'Text'}
209
+ checked={format === 'Text'}
210
+ onClick={() => {
211
+ setFormat('Text');
212
+ }}
213
+ >
214
+ Text
215
+ </RadioTile>
216
+ <RadioTile
217
+ id={'formatSelector--json'}
218
+ value={'JSON'}
219
+ checked={format === 'JSON'}
220
+ onClick={() => {
221
+ setFormat('JSON');
222
+ }}
223
+ >
224
+ JSON
225
+ </RadioTile>
226
+ <RadioTile
227
+ id={'formatSelector--latex'}
228
+ value={'LaTex'}
229
+ checked={format === 'LaTEX'}
230
+ onClick={() => {
231
+ setFormat('LaTEX');
232
+ }}
233
+ >
234
+ LaTex
235
+ </RadioTile>
236
+ </div>
237
+ <span className={classes.heading}>Preview</span>
238
+ <CodeSnippet
239
+ type="multi"
240
+ hideCopyButton={true}
241
+ wrapText={true}
242
+ className={classes.previewBox}
243
+ >
244
+ {textToCopy}
245
+ </CodeSnippet>
246
+ {format === 'LaTEX' && (
247
+ <div className={classes.warningContainer}>
248
+ <WarningAlt />
249
+ <span>&nbsp;Please make sure you add booktab package via</span>
250
+ <CodeSnippet type={'inline'}>
251
+ {'\\usepackage{booktabs}'}
252
+ </CodeSnippet>
253
+ <span> to your LaTEX project.</span>
254
+ </div>
255
+ )}
256
+ </div>
257
+ </Modal>
258
+ );
259
+ }
src/{views/task/TaskCopier.tsx → components/task-copier/RAGTaskCopier.tsx} RENAMED
@@ -72,6 +72,23 @@ function prepareText(
72
  );
73
  }
74
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
 
77
  // Step 2: Prepare context
@@ -191,7 +208,7 @@ function prepareJSON(
191
  );
192
  }
193
 
194
- export default function TaskCopierModal({
195
  models,
196
  metrics,
197
  task,
 
72
  );
73
  }
74
  }
75
+ } else if (task.taskType === 'chat') {
76
+ if (typeof task.input === 'string') {
77
+ input = `${separator}Question: ${task.input.trim()}`;
78
+ } else if (Array.isArray(task.input)) {
79
+ if (task.input.length == 1) {
80
+ input = `${separator}Question: ${task.input[0]['text'].trim()}`;
81
+ } else {
82
+ input = `${separator}Conversation\n${separator}`;
83
+ task.input.map(
84
+ (utterance) =>
85
+ (input += `${
86
+ utterance.speaker.charAt(0).toUpperCase() +
87
+ utterance.speaker.slice(1).toLowerCase()
88
+ }: ${utterance.text.trim()}\n`),
89
+ );
90
+ }
91
+ }
92
  }
93
 
94
  // Step 2: Prepare context
 
208
  );
209
  }
210
 
211
+ export default function RAGTaskCopierModal({
212
  models,
213
  metrics,
214
  task,
src/{views/task → components/task-copier}/TaskCopier.module.scss RENAMED
File without changes
src/components/task-copier/TaskCopier.tsx ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ * Copyright 2023-2025 InspectorRAGet Team
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ **/
18
+
19
+ 'use client';
20
+
21
+ import {
22
+ Metric,
23
+ Model,
24
+ Task,
25
+ RetrievedDocument,
26
+ TaskEvaluation,
27
+ } from '@/src/types';
28
+
29
+ import RAGTaskCopierModal from './RAGTaskCopier';
30
+ import ChatTaskCopierModal from './ChatTaskCopier';
31
+ import TextGenerationTaskCopierModal from './TextGenerationTaskCopier';
32
+
33
+ interface Props {
34
+ models: Model[];
35
+ metrics: Metric[];
36
+ task: Task;
37
+ evaluations: TaskEvaluation[];
38
+ onClose: Function;
39
+ open: boolean;
40
+ documents?: RetrievedDocument[];
41
+ }
42
+
43
+ export default function TaskCopierModal({
44
+ models,
45
+ metrics,
46
+ task,
47
+ evaluations,
48
+ onClose,
49
+ open = false,
50
+ documents,
51
+ }: Props) {
52
+ return (
53
+ <>
54
+ {task.taskType === 'rag' ? (
55
+ <RAGTaskCopierModal
56
+ models={models}
57
+ metrics={metrics}
58
+ task={task}
59
+ evaluations={evaluations}
60
+ onClose={onClose}
61
+ open={open}
62
+ documents={documents}
63
+ />
64
+ ) : task.taskType === 'chat' ? (
65
+ <ChatTaskCopierModal
66
+ models={models}
67
+ metrics={metrics}
68
+ task={task}
69
+ evaluations={evaluations}
70
+ onClose={onClose}
71
+ open={open}
72
+ />
73
+ ) : task.taskType === 'text_generation' ? (
74
+ <TextGenerationTaskCopierModal
75
+ models={models}
76
+ metrics={metrics}
77
+ task={task}
78
+ evaluations={evaluations}
79
+ onClose={onClose}
80
+ open={open}
81
+ />
82
+ ) : null}
83
+ </>
84
+ );
85
+ }
src/components/task-copier/TextGenerationTaskCopier.tsx ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ * Copyright 2023-2025 InspectorRAGet Team
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ **/
18
+
19
+ 'use client';
20
+
21
+ import { useState, useMemo } from 'react';
22
+ import { Modal, RadioTile, CodeSnippet } from '@carbon/react';
23
+
24
+ import {
25
+ Metric,
26
+ Model,
27
+ Task,
28
+ RetrievedDocument,
29
+ TaskEvaluation,
30
+ } from '@/src/types';
31
+ import { WarningAlt } from '@carbon/icons-react';
32
+
33
+ import classes from './TaskCopier.module.scss';
34
+
35
+ interface Props {
36
+ models: Model[];
37
+ metrics: Metric[];
38
+ task: Task;
39
+ evaluations: TaskEvaluation[];
40
+ onClose: Function;
41
+ open: boolean;
42
+ }
43
+
44
+ function prepareText(
45
+ models: Model[],
46
+ metrics: Metric[],
47
+ task: Task,
48
+ evaluations: TaskEvaluation[],
49
+ ): string {
50
+ const separator = '=======================================================\n';
51
+ let input, context, responses;
52
+
53
+ // Step 1: Prepare input
54
+ if (typeof task.input === 'string') {
55
+ input = `${separator}Input\n${separator}${task.input.trim()}`;
56
+ }
57
+
58
+ // Step 2: Prepare responses
59
+ if (evaluations && evaluations.length) {
60
+ responses = `${separator}Responses\n${separator}`;
61
+ const responseSeparator =
62
+ '\n-------------------------------------------------------\n';
63
+ evaluations.forEach((evaluation) => {
64
+ const model = models.find(
65
+ (entry) => entry.modelId === evaluation.modelId,
66
+ );
67
+ responses += `${model ? model.name.trim() : evaluation.modelId.trim()}${responseSeparator}${evaluation.modelResponse.trim()}\n${separator}`;
68
+ });
69
+ }
70
+
71
+ return `${input.trim()}\n${responses ? responses : ''}`;
72
+ }
73
+
74
+ function prepareLaTEXT(
75
+ models: Model[],
76
+ metrics: Metric[],
77
+ task: Task,
78
+ evaluations: TaskEvaluation[],
79
+ ): string {
80
+ let input, context, responses;
81
+
82
+ // Step 1: Prepare input
83
+ if (typeof task.input === 'string') {
84
+ input = `\\multicolumn{1}{|c|}{\\textbf{Input}} \\\\ \n\t\\toprule \n\t${task.input.trim()} \\\\ \n\t`;
85
+ }
86
+
87
+ // Step 2: Prepare responses
88
+ if (evaluations && evaluations.length) {
89
+ responses =
90
+ '\\toprule \n\t\\multicolumn{1}{|c|}{\\textbf{Responses}} \\\\ \n\t';
91
+ evaluations.forEach((evaluation) => {
92
+ const model = models.find(
93
+ (entry) => entry.modelId === evaluation.modelId,
94
+ );
95
+ responses += `\\toprule \n\t\\textbf{${model ? model.name.trim() : evaluation.modelId.trim()}} \\\\ \n\t\\midrule \n\t${evaluation.modelResponse.trim()} \\\\ \n\t`;
96
+ });
97
+ responses += '\\bottomrule \n\t';
98
+ }
99
+
100
+ return `\\begin{table*}\n\\small\n\t\\begin{tabular}{p{15.5cm}}\n\t\\toprule\n\t${input}${responses ? responses : ''}\\end{tabular}\n\\end{table*}`;
101
+ }
102
+
103
+ function prepareJSON(
104
+ models: Model[],
105
+ metrics: Metric[],
106
+ task: Task,
107
+ evaluations: TaskEvaluation[],
108
+ ): string {
109
+ return JSON.stringify(
110
+ {
111
+ input: task.input,
112
+ responses: evaluations.map((evaluation) => {
113
+ const model = models.find(
114
+ (entry) => entry.modelId === evaluation.modelId,
115
+ );
116
+ return {
117
+ model: model ? model.name : evaluation.modelId,
118
+ response: evaluation.modelResponse,
119
+ };
120
+ }),
121
+ },
122
+ null,
123
+ 2,
124
+ );
125
+ }
126
+
127
+ export default function TextGenerationTaskCopierModal({
128
+ models,
129
+ metrics,
130
+ task,
131
+ evaluations,
132
+ onClose,
133
+ open = false,
134
+ }: Props) {
135
+ const [format, setFormat] = useState<'Text' | 'JSON' | 'LaTEX'>('Text');
136
+
137
+ const textToCopy = useMemo(() => {
138
+ let text;
139
+ if (format === 'Text') {
140
+ text = prepareText(models, metrics, task, evaluations);
141
+ } else if (format === 'LaTEX') {
142
+ text = prepareLaTEXT(models, metrics, task, evaluations);
143
+ } else {
144
+ text = prepareJSON(models, metrics, task, evaluations);
145
+ }
146
+
147
+ return text;
148
+ }, [models, metrics, task, evaluations, format]);
149
+
150
+ return (
151
+ <Modal
152
+ open={open}
153
+ modalLabel="Copy task details to clipboard"
154
+ primaryButtonText="Copy"
155
+ secondaryButtonText="Cancel"
156
+ onRequestSubmit={() => {
157
+ //Step 1: Copy to clipboard
158
+ navigator.clipboard.writeText(textToCopy);
159
+
160
+ // Step 2: Close model
161
+ onClose();
162
+ }}
163
+ onRequestClose={() => {
164
+ onClose();
165
+ }}
166
+ >
167
+ <div className={classes.container}>
168
+ <span className={classes.heading}>Select a format</span>
169
+ <div className={classes.copyFormatSelectors}>
170
+ <RadioTile
171
+ id={'formatSelector--text'}
172
+ value={'Text'}
173
+ checked={format === 'Text'}
174
+ onClick={() => {
175
+ setFormat('Text');
176
+ }}
177
+ >
178
+ Text
179
+ </RadioTile>
180
+ <RadioTile
181
+ id={'formatSelector--json'}
182
+ value={'JSON'}
183
+ checked={format === 'JSON'}
184
+ onClick={() => {
185
+ setFormat('JSON');
186
+ }}
187
+ >
188
+ JSON
189
+ </RadioTile>
190
+ <RadioTile
191
+ id={'formatSelector--latex'}
192
+ value={'LaTex'}
193
+ checked={format === 'LaTEX'}
194
+ onClick={() => {
195
+ setFormat('LaTEX');
196
+ }}
197
+ >
198
+ LaTex
199
+ </RadioTile>
200
+ </div>
201
+ <span className={classes.heading}>Preview</span>
202
+ <CodeSnippet
203
+ type="multi"
204
+ hideCopyButton={true}
205
+ wrapText={true}
206
+ className={classes.previewBox}
207
+ >
208
+ {textToCopy}
209
+ </CodeSnippet>
210
+ {format === 'LaTEX' && (
211
+ <div className={classes.warningContainer}>
212
+ <WarningAlt />
213
+ <span>&nbsp;Please make sure you add booktab package via</span>
214
+ <CodeSnippet type={'inline'}>
215
+ {'\\usepackage{booktabs}'}
216
+ </CodeSnippet>
217
+ <span> to your LaTEX project.</span>
218
+ </div>
219
+ )}
220
+ </div>
221
+ </Modal>
222
+ );
223
+ }
src/types.ts CHANGED
@@ -167,7 +167,7 @@ export interface ToolCall {
167
  children?: string[];
168
  }
169
 
170
- interface Step {
171
  id: string;
172
  input: any;
173
  output: any;
@@ -203,7 +203,7 @@ export interface ToolMessageDocument {
203
  }
204
  export interface ToolMessage extends Message {
205
  role: 'tool';
206
- tool_id: string;
207
  type?: 'text' | 'documents' | 'json';
208
  content: string | object | ToolMessageDocument[];
209
  }
@@ -212,7 +212,7 @@ export interface AssistantMessage extends Message {
212
  role: 'assistant';
213
  refusal?: string;
214
  tool_calls?: ToolCall[];
215
- steps?: Step[];
216
  }
217
 
218
  // ===================================================================================
@@ -273,6 +273,7 @@ export interface TaskEvaluation {
273
  [key: string]: { [key: string]: Annotation };
274
  };
275
  readonly contexts?: RetrievedDocument[];
 
276
  [key: string]: any;
277
  }
278
 
 
167
  children?: string[];
168
  }
169
 
170
+ export interface MessageStep {
171
  id: string;
172
  input: any;
173
  output: any;
 
203
  }
204
  export interface ToolMessage extends Message {
205
  role: 'tool';
206
+ tool_call_id: string;
207
  type?: 'text' | 'documents' | 'json';
208
  content: string | object | ToolMessageDocument[];
209
  }
 
212
  role: 'assistant';
213
  refusal?: string;
214
  tool_calls?: ToolCall[];
215
+ steps?: MessageStep[];
216
  }
217
 
218
  // ===================================================================================
 
273
  [key: string]: { [key: string]: Annotation };
274
  };
275
  readonly contexts?: RetrievedDocument[];
276
+ readonly steps?: MessageStep[];
277
  [key: string]: any;
278
  }
279
 
src/views/data-characteristics/DataCharacteristics.tsx CHANGED
@@ -88,7 +88,10 @@ function computeWordCount(tasks: Task[], filters: string[]) {
88
  task.input.forEach((turn) => {
89
  if (turn.hasOwnProperty('text') && turn.text) {
90
  text += turn.text.trim();
91
- } else if (turn.hasOwnProperty('content') && turn.content) {
 
 
 
92
  text += turn.content.trim();
93
  }
94
  });
 
88
  task.input.forEach((turn) => {
89
  if (turn.hasOwnProperty('text') && turn.text) {
90
  text += turn.text.trim();
91
+ } else if (
92
+ turn.hasOwnProperty('content') &&
93
+ typeof turn.content === 'string'
94
+ ) {
95
  text += turn.content.trim();
96
  }
97
  });
src/views/task/ChatTask.module.scss CHANGED
@@ -60,3 +60,17 @@
60
  .responseContainer {
61
  line-height: 1.4em;
62
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  .responseContainer {
61
  line-height: 1.4em;
62
  }
63
+
64
+ .evaluationContainer {
65
+ display: flex;
66
+ flex-direction: column;
67
+ row-gap: $spacing-03;
68
+ overflow: auto;
69
+ }
70
+
71
+ .stepContainer {
72
+ display: flex;
73
+ flex-direction: column;
74
+ row-gap: $spacing-03;
75
+ overflow: auto;
76
+ }
src/views/task/ChatTask.tsx CHANGED
@@ -32,13 +32,13 @@ import {
32
  ContainedListItem,
33
  } from '@carbon/react';
34
 
35
- import { Model, TaskEvaluation, Task, Metric } from '@/src/types';
36
  import { useDataStore } from '@/src/store';
37
  import { truncate } from '@/src/utilities/strings';
38
 
39
  import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable';
40
  import ChatLine from '@/src/components/chatline/ChatLine';
41
- import TaskCopierModal from '@/src/views/task/TaskCopier';
42
 
43
  import classes from './ChatTask.module.scss';
44
 
@@ -54,6 +54,55 @@ interface Props {
54
  updateCommentProvenance: Function;
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  // ===================================================================================
58
  // MAIN FUNCTION
59
  // ===================================================================================
@@ -68,7 +117,6 @@ export default function ChatTask({
68
  // Step 1: Initialize state and necessary variables
69
  const [selectedEvaluationIndex, setSelectedEvaluationIndex] =
70
  useState<number>(0);
71
- const [activeDocumentIndex, setActiveDocumentIndex] = useState<number>(0);
72
 
73
  // Step 2: Run effects
74
  // Step 2.a: Fetch data from data store
@@ -103,10 +151,21 @@ export default function ChatTask({
103
  return [humanMetrics, algorithmicMetrics];
104
  }, [metrics]);
105
 
106
- console.log(task);
107
  // Step 3: Render
108
  return (
109
  <>
 
 
 
 
 
 
 
 
 
 
 
 
110
  {task && models && evaluations && (
111
  <>
112
  <div className={classes.inputContainer}>
@@ -117,7 +176,18 @@ export default function ChatTask({
117
  messageId={`data_point__message--${messageIdx}`}
118
  message={message}
119
  onSelection={updateCommentProvenance}
120
- focused={false}
 
 
 
 
 
 
 
 
 
 
 
121
  ></ChatLine>
122
  ))
123
  : null}
@@ -129,14 +199,9 @@ export default function ChatTask({
129
  <Tabs
130
  onChange={(e) => {
131
  setSelectedEvaluationIndex(e.selectedIndex);
132
- setActiveDocumentIndex(0);
133
  }}
134
  >
135
- <TabList
136
- className={classes.tabList}
137
- aria-label="Models tab"
138
- contained
139
- >
140
  {evaluations.map((evaluation) => (
141
  <Tab key={'model-' + evaluation.modelId}>
142
  {truncate(
@@ -207,24 +272,43 @@ export default function ChatTask({
207
  )}
208
  </ContainedList>
209
  ) : null}
210
- {evaluation.annotations && hMetrics.size ? (
211
- <>
212
- <h5>Human Evaluations:</h5>
213
- <AnnotationsTable
214
- annotations={evaluation.annotations}
215
- metrics={[...hMetrics.values()]}
216
- ></AnnotationsTable>
217
- </>
218
- ) : null}
219
- {evaluation.annotations && aMetrics.size ? (
220
- <>
221
- <h5>Algorithmic Evaluations:</h5>
222
- <AnnotationsTable
223
- annotations={evaluation.annotations}
224
- metrics={[...aMetrics.values()]}
225
- ></AnnotationsTable>
226
- </>
227
- ) : null}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  </div>
229
  </TabPanel>
230
  ))}
 
32
  ContainedListItem,
33
  } from '@carbon/react';
34
 
35
+ import { Model, TaskEvaluation, Task, Metric, MessageStep } from '@/src/types';
36
  import { useDataStore } from '@/src/store';
37
  import { truncate } from '@/src/utilities/strings';
38
 
39
  import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable';
40
  import ChatLine from '@/src/components/chatline/ChatLine';
41
+ import ChatTaskCopierModal from '@/src/components/task-copier/ChatTaskCopier';
42
 
43
  import classes from './ChatTask.module.scss';
44
 
 
54
  updateCommentProvenance: Function;
55
  }
56
 
57
+ // ===================================================================================
58
+ // RENDER FUNCTIONS
59
+ // ===================================================================================
60
+
61
+ function Evaluation({
62
+ evaluation,
63
+ hMetrics,
64
+ aMetrics,
65
+ }: {
66
+ evaluation: TaskEvaluation;
67
+ hMetrics: Map<string, Metric>;
68
+ aMetrics: Map<string, Metric>;
69
+ }) {
70
+ return (
71
+ <div className={classes.evaluationContainer}>
72
+ {evaluation.annotations && hMetrics.size ? (
73
+ <>
74
+ <h5>Human Evaluations:</h5>
75
+ <AnnotationsTable
76
+ annotations={evaluation.annotations}
77
+ metrics={[...hMetrics.values()]}
78
+ ></AnnotationsTable>
79
+ </>
80
+ ) : null}
81
+ {evaluation.annotations && aMetrics.size ? (
82
+ <>
83
+ <h5>Algorithmic Evaluations:</h5>
84
+ <AnnotationsTable
85
+ annotations={evaluation.annotations}
86
+ metrics={[...aMetrics.values()]}
87
+ ></AnnotationsTable>
88
+ </>
89
+ ) : null}
90
+ </div>
91
+ );
92
+ }
93
+
94
+ function Steps({ steps }: { steps?: MessageStep[] }) {
95
+ return (
96
+ <div className={classes.stepsContainer}>
97
+ {steps && !isEmpty(steps) ? (
98
+ <></>
99
+ ) : (
100
+ <h4>No steps information is available.</h4>
101
+ )}
102
+ </div>
103
+ );
104
+ }
105
+
106
  // ===================================================================================
107
  // MAIN FUNCTION
108
  // ===================================================================================
 
117
  // Step 1: Initialize state and necessary variables
118
  const [selectedEvaluationIndex, setSelectedEvaluationIndex] =
119
  useState<number>(0);
 
120
 
121
  // Step 2: Run effects
122
  // Step 2.a: Fetch data from data store
 
151
  return [humanMetrics, algorithmicMetrics];
152
  }, [metrics]);
153
 
 
154
  // Step 3: Render
155
  return (
156
  <>
157
+ {models && metrics && task && evaluations && (
158
+ <ChatTaskCopierModal
159
+ open={taskCopierModalOpen}
160
+ models={Array.from(models.values())}
161
+ metrics={metrics}
162
+ task={task}
163
+ evaluations={evaluations}
164
+ onClose={() => {
165
+ setTaskCopierModalOpen(false);
166
+ }}
167
+ ></ChatTaskCopierModal>
168
+ )}
169
  {task && models && evaluations && (
170
  <>
171
  <div className={classes.inputContainer}>
 
176
  messageId={`data_point__message--${messageIdx}`}
177
  message={message}
178
  onSelection={updateCommentProvenance}
179
+ focused={
180
+ Array.isArray(task.input) &&
181
+ messageIdx === task.input.length - 1
182
+ ? true
183
+ : false
184
+ }
185
+ latestResponse={
186
+ Array.isArray(task.input) &&
187
+ messageIdx === task.input.length - 1
188
+ ? true
189
+ : false
190
+ }
191
  ></ChatLine>
192
  ))
193
  : null}
 
199
  <Tabs
200
  onChange={(e) => {
201
  setSelectedEvaluationIndex(e.selectedIndex);
 
202
  }}
203
  >
204
+ <TabList aria-label="Models tab" contained>
 
 
 
 
205
  {evaluations.map((evaluation) => (
206
  <Tab key={'model-' + evaluation.modelId}>
207
  {truncate(
 
272
  )}
273
  </ContainedList>
274
  ) : null}
275
+
276
+ <Tabs>
277
+ <TabList
278
+ aria-label="Model performance tab"
279
+ contained
280
+ fullWidth
281
+ >
282
+ <Tab
283
+ key={'model-' + evaluation.modelId + '-evaluations'}
284
+ >
285
+ Evaluations
286
+ </Tab>
287
+ <Tab key={'model-' + evaluation.modelId + '-steps'}>
288
+ Steps
289
+ </Tab>
290
+ </TabList>
291
+ <TabPanels>
292
+ <TabPanel
293
+ key={
294
+ 'model-' +
295
+ evaluation.modelId +
296
+ '-evaluations-panel'
297
+ }
298
+ >
299
+ <Evaluation
300
+ evaluation={evaluation}
301
+ hMetrics={hMetrics}
302
+ aMetrics={aMetrics}
303
+ />
304
+ </TabPanel>
305
+ <TabPanel
306
+ key={'model-' + evaluation.modelId + '-steps-panel'}
307
+ >
308
+ <Steps steps={evaluation.steps} />
309
+ </TabPanel>
310
+ </TabPanels>
311
+ </Tabs>
312
  </div>
313
  </TabPanel>
314
  ))}
src/views/task/RAGTask.tsx CHANGED
@@ -49,7 +49,7 @@ import { mark } from '@/src/utilities/highlighter';
49
 
50
  import DocumentPanel from '@/src/views/document/DocumentPanel';
51
  import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable';
52
- import TaskCopierModal from '@/src/views/task/TaskCopier';
53
 
54
  import classes from './RAGTask.module.scss';
55
 
@@ -192,7 +192,7 @@ export default function RAGTask({
192
  return (
193
  <>
194
  {models && metrics && task && evaluations && (
195
- <TaskCopierModal
196
  open={taskCopierModalOpen}
197
  models={Array.from(models.values())}
198
  metrics={metrics}
@@ -202,7 +202,7 @@ export default function RAGTask({
202
  onClose={() => {
203
  setTaskCopierModalOpen(false);
204
  }}
205
- ></TaskCopierModal>
206
  )}
207
 
208
  {task && models && evaluations && (
 
49
 
50
  import DocumentPanel from '@/src/views/document/DocumentPanel';
51
  import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable';
52
+ import RAGTaskCopierModal from '@/src/components/task-copier/RAGTaskCopier';
53
 
54
  import classes from './RAGTask.module.scss';
55
 
 
192
  return (
193
  <>
194
  {models && metrics && task && evaluations && (
195
+ <RAGTaskCopierModal
196
  open={taskCopierModalOpen}
197
  models={Array.from(models.values())}
198
  metrics={metrics}
 
202
  onClose={() => {
203
  setTaskCopierModalOpen(false);
204
  }}
205
+ ></RAGTaskCopierModal>
206
  )}
207
 
208
  {task && models && evaluations && (
src/views/task/Task.module.scss CHANGED
@@ -36,7 +36,7 @@
36
  }
37
 
38
  .taskContainer {
39
- height: calc(100vh - 9rem);
40
  background-color: var(--cds-background);
41
  margin: $spacing-03 0;
42
  display: flex;
 
36
  }
37
 
38
  .taskContainer {
39
+ height: calc(100vh - 10rem);
40
  background-color: var(--cds-background);
41
  margin: $spacing-03 0;
42
  display: flex;
src/views/task/TextGenerationTask.tsx CHANGED
@@ -40,7 +40,7 @@ import { truncate, overlaps } from '@/src/utilities/strings';
40
  import { mark } from '@/src/utilities/highlighter';
41
 
42
  import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable';
43
- import TaskCopierModal from '@/src/views/task/TaskCopier';
44
 
45
  import classes from './TextGenerationTask.module.scss';
46
 
@@ -117,7 +117,7 @@ export default function TextGenerationTask({
117
  return (
118
  <>
119
  {models && metrics && task && evaluations && (
120
- <TaskCopierModal
121
  open={taskCopierModalOpen}
122
  models={Array.from(models.values())}
123
  metrics={metrics}
@@ -126,7 +126,7 @@ export default function TextGenerationTask({
126
  onClose={() => {
127
  setTaskCopierModalOpen(false);
128
  }}
129
- ></TaskCopierModal>
130
  )}
131
 
132
  {task && models && evaluations && (
 
40
  import { mark } from '@/src/utilities/highlighter';
41
 
42
  import AnnotationsTable from '@/src/views/annotations-table/AnnotationsTable';
43
+ import TextGenerationTaskCopierModal from '@/src/components/task-copier/TextGenerationTaskCopier';
44
 
45
  import classes from './TextGenerationTask.module.scss';
46
 
 
117
  return (
118
  <>
119
  {models && metrics && task && evaluations && (
120
+ <TextGenerationTaskCopierModal
121
  open={taskCopierModalOpen}
122
  models={Array.from(models.values())}
123
  metrics={metrics}
 
126
  onClose={() => {
127
  setTaskCopierModalOpen(false);
128
  }}
129
+ ></TextGenerationTaskCopierModal>
130
  )}
131
 
132
  {task && models && evaluations && (
yarn.lock CHANGED
@@ -1979,12 +1979,7 @@ [email protected], domhandler@^5.0.2, domhandler@^5.0.3:
1979
  dependencies:
1980
  domelementtype "^2.3.0"
1981
 
1982
- dompurify@3.1.7:
1983
- version "3.1.7"
1984
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.7.tgz#711a8c96479fb6ced93453732c160c3c72418a6a"
1985
- integrity sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==
1986
-
1987
- dompurify@^3.2.4:
1988
  version "3.2.4"
1989
  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.4.tgz#af5a5a11407524431456cf18836c55d13441cd8e"
1990
  integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==
 
1979
  dependencies:
1980
  domelementtype "^2.3.0"
1981
 
1982
+ dompurify@3.2.4, dompurify@^3.2.4:
 
 
 
 
 
1983
  version "3.2.4"
1984
  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.4.tgz#af5a5a11407524431456cf18836c55d13441cd8e"
1985
  integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==