@@ -283,26 +283,27 @@ def _get_evaluation_html(eval_result_json: str) -> str:
283283 let traceHtml = `<div class="trace-event-row"><div class="name"><span class="icon">🏃</span>agent_run</div></div>`;
284284 eventsArray.forEach(event => {{
285285 if (event.content && event.content.parts && event.content.parts.length > 0) {{
286- const part = event.content.parts[0];
287- if (part.function_call) {{
288- traceHtml += `<div class="trace-details-wrapper"><details><summary><div class="trace-event-row"><div class="name trace-l1"><span class="icon">🛠️</span>function_call</div></div></summary>`;
289- traceHtml += `<div class="trace-details details-l1">function name: ${{part.function_call.name}}</div>`;
290- traceHtml += `<div class="trace-details details-l1">function args: ${{formatDictVals(part.function_call.args)}}</div></details></div>`;
291- }} else if (part.text && event.content.role === 'model') {{
292- traceHtml += `<div class="trace-details-wrapper"><details><summary><div class="trace-event-row"><div class="name trace-l1"><span class="icon">💬</span>call_llm</div></div></summary>`;
293- traceHtml += `<div class="trace-details details-l1">model response: ${{part.text}}</div></details></div>`;
294- }} else if (part.function_response) {{
295- traceHtml += `<div class="trace-details-wrapper"><details><summary><div class="trace-event-row"><div class="name trace-l1"><span class="icon">🛠️</span>function_response</div></div></summary>`;
296- traceHtml += `<div class="trace-details details-l1">function name: ${{part.function_response.name}}</div>`;
297- let response_val = part.function_response.response;
298- if(typeof response_val === 'object' && response_val !== null && response_val.result !== undefined) {{
299- response_val = response_val.result;
286+ event.content.parts.forEach(part => {{
287+ if (part.function_call) {{
288+ traceHtml += `<div class="trace-details-wrapper"><details><summary><div class="trace-event-row"><div class="name trace-l1"><span class="icon">🛠️</span>function_call</div></div></summary>`;
289+ traceHtml += `<div class="trace-details details-l1">function name: ${{part.function_call.name}}</div>`;
290+ traceHtml += `<div class="trace-details details-l1">function args: ${{formatDictVals(part.function_call.args)}}</div></details></div>`;
291+ }} else if (part.text && event.content.role === 'model') {{
292+ traceHtml += `<div class="trace-details-wrapper"><details><summary><div class="trace-event-row"><div class="name trace-l1"><span class="icon">💬</span>call_llm</div></div></summary>`;
293+ traceHtml += `<div class="trace-details details-l1">model response: ${{part.text}}</div></details></div>`;
294+ }} else if (part.function_response) {{
295+ traceHtml += `<div class="trace-details-wrapper"><details><summary><div class="trace-event-row"><div class="name trace-l1"><span class="icon">🛠️</span>function_response</div></div></summary>`;
296+ traceHtml += `<div class="trace-details details-l1">function name: ${{part.function_response.name}}</div>`;
297+ let response_val = part.function_response.response;
298+ if(typeof response_val === 'object' && response_val !== null && response_val.result !== undefined) {{
299+ response_val = response_val.result;
300+ }}
301+ traceHtml += `<div class="trace-details details-l1">function response: ${{formatDictVals(response_val)}}</div></details></div>`;
302+ }} else {{
303+ // Skipping user messages and other parts in trace view
304+ return;
300305 }}
301- traceHtml += `<div class="trace-details details-l1">function response: ${{formatDictVals(response_val)}}</div></details></div>`;
302- }} else {{
303- // Skipping user messages and other parts in trace view
304- return;
305- }}
306+ }});
306307 }}
307308 }});
308309 return traceHtml;
@@ -313,16 +314,17 @@ def _get_evaluation_html(eval_result_json: str) -> str:
313314 const role = event.content.role;
314315 let contentHtml = '';
315316 if (event.content && event.content.parts && event.content.parts.length > 0) {{
316- const part = event.content.parts[0];
317- if (part.text) {{
318- contentHtml = DOMPurify.sanitize(marked.parse(String(part.text)));
319- }} else if (part.function_call) {{
320- contentHtml = `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(part.function_call, null, 2))}}</pre>`;
321- }} else if (part.function_response) {{
322- contentHtml = `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(part.function_response, null, 2))}}</pre>`;
323- }} else {{
324- contentHtml = `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(event.content, null, 2))}}</pre>`;
325- }}
317+ event.content.parts.forEach(part => {{
318+ if (part.text) {{
319+ contentHtml += DOMPurify.sanitize(marked.parse(String(part.text)));
320+ }} else if (part.function_call) {{
321+ contentHtml += `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(part.function_call, null, 2))}}</pre>`;
322+ }} else if (part.function_response) {{
323+ contentHtml += `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(part.function_response, null, 2))}}</pre>`;
324+ }} else {{
325+ contentHtml += `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(part, null, 2))}}</pre>`;
326+ }}
327+ }});
326328 }} else {{
327329 contentHtml = `<pre class="raw-json-container">${{DOMPurify.sanitize(JSON.stringify(event.content, null, 2))}}</pre>`;
328330 }}
@@ -456,28 +458,43 @@ def _get_evaluation_html(eval_result_json: str) -> str:
456458
457459 if (name.startsWith('hallucination') && val.explanation) {{
458460 try {{
459- const explanationData = JSON.parse(val.explanation);
460- if (Array.isArray(explanationData) && explanationData.length > 0 && explanationData[0].sentence) {{
461- bubbles += '<div class="rubric-bubble-container" style="margin-top: 8px;">';
462- explanationData.forEach(item => {{
463- let sentence = item.sentence || 'N/A';
464- const label = item.label ? item.label.toLowerCase() : '';
465- const isPass = label === 'no_rad' || label === 'supported';
466- const verdictText = isPass ? '<span class="pass">Pass</span>' : '<span class="fail">Fail</span>';
467- if (isPass) {{
468- sentence = `"${{sentence}}" is grounded`;
469- }}
470- const rationale = item.rationale || 'N/A';
471- const itemJson = JSON.stringify(item, null, 2);
472- bubbles += `
473- <details class="rubric-details">
474- <summary class="rubric-bubble">${{verdictText}}: ${{DOMPurify.sanitize(sentence)}}</summary>
475- <div class="explanation" style="padding: 10px 0 0 20px;">${{DOMPurify.sanitize(rationale)}}</div>
476- <pre class="raw-json-container">${{DOMPurify.sanitize(itemJson)}}</pre>
477- </details>`;
478- }});
479- bubbles += '</div>';
480- explanationHandled = true;
461+ const explanationData = typeof val.explanation === 'string' ? JSON.parse(val.explanation) : val.explanation;
462+ if (Array.isArray(explanationData) && explanationData.length > 0) {{
463+ let sentenceGroups = [];
464+ if (explanationData[0].explanation && Array.isArray(explanationData[0].explanation)) {{
465+ explanationData.forEach(item => {{
466+ if(item.explanation && Array.isArray(item.explanation)) {{
467+ sentenceGroups.push(item.explanation);
468+ }}
469+ }});
470+ }} else if (explanationData[0].sentence) {{
471+ sentenceGroups.push(explanationData);
472+ }}
473+
474+ if(sentenceGroups.length > 0) {{
475+ sentenceGroups.forEach(sentenceList => {{
476+ bubbles += '<div class="rubric-bubble-container" style="margin-top: 8px;">';
477+ sentenceList.forEach(item => {{
478+ let sentence = item.sentence || 'N/A';
479+ const label = item.label ? item.label.toLowerCase() : '';
480+ const isPass = label === 'no_rad' || label === 'supported';
481+ const verdictText = isPass ? '<span class="pass">Pass</span>' : '<span class="fail">Fail</span>';
482+ if (isPass) {{
483+ sentence = `"${{sentence}}" is grounded`;
484+ }}
485+ const rationale = item.rationale || 'N/A';
486+ const itemJson = JSON.stringify(item, null, 2);
487+ bubbles += `
488+ <details class="rubric-details">
489+ <summary class="rubric-bubble">${{verdictText}}: ${{DOMPurify.sanitize(sentence)}}</summary>
490+ <div class="explanation" style="padding: 10px 0 0 20px;">${{DOMPurify.sanitize(rationale)}}</div>
491+ <pre class="raw-json-container">${{DOMPurify.sanitize(itemJson)}}</pre>
492+ </details>`;
493+ }});
494+ bubbles += '</div>';
495+ }});
496+ explanationHandled = true;
497+ }}
481498 }}
482499 }} catch (e) {{
483500 console.error("Failed to parse hallucination explanation:", e);
0 commit comments