From 67f9099af92226fac603c4cbec944e66c4fc737f Mon Sep 17 00:00:00 2001 From: A Vertex SDK engineer Date: Tue, 28 Oct 2025 09:51:18 -0700 Subject: [PATCH] fix: GenAI Client(evals) - fix visualization PiperOrigin-RevId: 825084384 --- vertexai/_genai/_evals_visualization.py | 119 ++++++++++++++---------- 1 file changed, 68 insertions(+), 51 deletions(-) diff --git a/vertexai/_genai/_evals_visualization.py b/vertexai/_genai/_evals_visualization.py index 2cfbc6a052..0436d893ae 100644 --- a/vertexai/_genai/_evals_visualization.py +++ b/vertexai/_genai/_evals_visualization.py @@ -283,26 +283,27 @@ def _get_evaluation_html(eval_result_json: str) -> str: let traceHtml = `
🏃agent_run
`; eventsArray.forEach(event => {{ if (event.content && event.content.parts && event.content.parts.length > 0) {{ - const part = event.content.parts[0]; - if (part.function_call) {{ - traceHtml += `
🛠️function_call
`; - traceHtml += `
function name: ${{part.function_call.name}}
`; - traceHtml += `
function args: ${{formatDictVals(part.function_call.args)}}
`; - }} else if (part.text && event.content.role === 'model') {{ - traceHtml += `
💬call_llm
`; - traceHtml += `
model response: ${{part.text}}
`; - }} else if (part.function_response) {{ - traceHtml += `
🛠️function_response
`; - traceHtml += `
function name: ${{part.function_response.name}}
`; - let response_val = part.function_response.response; - if(typeof response_val === 'object' && response_val !== null && response_val.result !== undefined) {{ - response_val = response_val.result; + event.content.parts.forEach(part => {{ + if (part.function_call) {{ + traceHtml += `
🛠️function_call
`; + traceHtml += `
function name: ${{part.function_call.name}}
`; + traceHtml += `
function args: ${{formatDictVals(part.function_call.args)}}
`; + }} else if (part.text && event.content.role === 'model') {{ + traceHtml += `
💬call_llm
`; + traceHtml += `
model response: ${{part.text}}
`; + }} else if (part.function_response) {{ + traceHtml += `
🛠️function_response
`; + traceHtml += `
function name: ${{part.function_response.name}}
`; + let response_val = part.function_response.response; + if(typeof response_val === 'object' && response_val !== null && response_val.result !== undefined) {{ + response_val = response_val.result; + }} + traceHtml += `
function response: ${{formatDictVals(response_val)}}
`; + }} else {{ + // Skipping user messages and other parts in trace view + return; }} - traceHtml += `
function response: ${{formatDictVals(response_val)}}
`; - }} else {{ - // Skipping user messages and other parts in trace view - return; - }} + }}); }} }}); return traceHtml; @@ -313,16 +314,17 @@ def _get_evaluation_html(eval_result_json: str) -> str: const role = event.content.role; let contentHtml = ''; if (event.content && event.content.parts && event.content.parts.length > 0) {{ - const part = event.content.parts[0]; - if (part.text) {{ - contentHtml = DOMPurify.sanitize(marked.parse(String(part.text))); - }} else if (part.function_call) {{ - contentHtml = `
${{DOMPurify.sanitize(JSON.stringify(part.function_call, null, 2))}}
`; - }} else if (part.function_response) {{ - contentHtml = `
${{DOMPurify.sanitize(JSON.stringify(part.function_response, null, 2))}}
`; - }} else {{ - contentHtml = `
${{DOMPurify.sanitize(JSON.stringify(event.content, null, 2))}}
`; - }} + event.content.parts.forEach(part => {{ + if (part.text) {{ + contentHtml += DOMPurify.sanitize(marked.parse(String(part.text))); + }} else if (part.function_call) {{ + contentHtml += `
${{DOMPurify.sanitize(JSON.stringify(part.function_call, null, 2))}}
`; + }} else if (part.function_response) {{ + contentHtml += `
${{DOMPurify.sanitize(JSON.stringify(part.function_response, null, 2))}}
`; + }} else {{ + contentHtml += `
${{DOMPurify.sanitize(JSON.stringify(part, null, 2))}}
`; + }} + }}); }} else {{ contentHtml = `
${{DOMPurify.sanitize(JSON.stringify(event.content, null, 2))}}
`; }} @@ -456,28 +458,43 @@ def _get_evaluation_html(eval_result_json: str) -> str: if (name.startsWith('hallucination') && val.explanation) {{ try {{ - const explanationData = JSON.parse(val.explanation); - if (Array.isArray(explanationData) && explanationData.length > 0 && explanationData[0].sentence) {{ - bubbles += '
'; - explanationData.forEach(item => {{ - let sentence = item.sentence || 'N/A'; - const label = item.label ? item.label.toLowerCase() : ''; - const isPass = label === 'no_rad' || label === 'supported'; - const verdictText = isPass ? 'Pass' : 'Fail'; - if (isPass) {{ - sentence = `"${{sentence}}" is grounded`; - }} - const rationale = item.rationale || 'N/A'; - const itemJson = JSON.stringify(item, null, 2); - bubbles += ` -
- ${{verdictText}}: ${{DOMPurify.sanitize(sentence)}} -
${{DOMPurify.sanitize(rationale)}}
-
${{DOMPurify.sanitize(itemJson)}}
-
`; - }}); - bubbles += '
'; - explanationHandled = true; + const explanationData = typeof val.explanation === 'string' ? JSON.parse(val.explanation) : val.explanation; + if (Array.isArray(explanationData) && explanationData.length > 0) {{ + let sentenceGroups = []; + if (explanationData[0].explanation && Array.isArray(explanationData[0].explanation)) {{ + explanationData.forEach(item => {{ + if(item.explanation && Array.isArray(item.explanation)) {{ + sentenceGroups.push(item.explanation); + }} + }}); + }} else if (explanationData[0].sentence) {{ + sentenceGroups.push(explanationData); + }} + + if(sentenceGroups.length > 0) {{ + sentenceGroups.forEach(sentenceList => {{ + bubbles += '
'; + sentenceList.forEach(item => {{ + let sentence = item.sentence || 'N/A'; + const label = item.label ? item.label.toLowerCase() : ''; + const isPass = label === 'no_rad' || label === 'supported'; + const verdictText = isPass ? 'Pass' : 'Fail'; + if (isPass) {{ + sentence = `"${{sentence}}" is grounded`; + }} + const rationale = item.rationale || 'N/A'; + const itemJson = JSON.stringify(item, null, 2); + bubbles += ` +
+ ${{verdictText}}: ${{DOMPurify.sanitize(sentence)}} +
${{DOMPurify.sanitize(rationale)}}
+
${{DOMPurify.sanitize(itemJson)}}
+
`; + }}); + bubbles += '
'; + }}); + explanationHandled = true; + }} }} }} catch (e) {{ console.error("Failed to parse hallucination explanation:", e);