feat(metadata-editor): apply custom logic for multiple confidence scores and target locations#4502
Conversation
…res and target locations
WalkthroughEnhanced metadata field selection and fetching by adding bounding box validation to filter entries lacking required boxes, improving confidence score handling when provided as arrays by selecting the lowest score, and flattening reference arrays before use. Tests validate these new behaviors. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsx (1)
818-875: Add a regression test for empty confidence arrays.Please add a case where
confidence_score.field1is[]to ensure extraction does not throw and simply omitsaiSuggestionConfidenceScore.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsx` around lines 818 - 875, Add a regression test that ensures empty confidence arrays are handled gracefully: in the test file use mockAPI.extractStructured.mockResolvedValue to return answer: { field1: 'value1' } and confidence_score: { field1: [] } (and created_at/completion_reason as in other tests), call setupHook('123', true) and await result.current.status === STATUS.SUCCESS, then call result.current.extractSuggestions('templateKey', 'global') and assert that the returned suggestion for field1 contains aiSuggestion: 'value1' but does NOT include aiSuggestionConfidenceScore (or that aiSuggestionConfidenceScore is undefined), referencing the existing tests like "should pick the lowest confidence score when confidence_score is an array" and the extractSuggestions flow to place the new test alongside them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts`:
- Around line 287-293: The current computation of lowestConfidence in
useSidebarMetadataFetcher.ts uses Array.prototype.reduce without guarding
against an empty array, which throws when fieldConfidence === []; fix by
checking Array.isArray(fieldConfidence) && fieldConfidence.length > 0 before
reducing (or use reduce with an initial value such as fieldConfidence[0]) and
provide a safe fallback when the array is empty or fieldConfidence is
null/undefined so that result.aiSuggestionConfidenceScore is only assigned when
a valid lowestConfidence with .score and .level exists (or assign a safe
default/null). Update the logic around the symbols fieldConfidence,
lowestConfidence, and result.aiSuggestionConfidenceScore to handle empty arrays
and avoid TypeError.
---
Nitpick comments:
In `@src/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsx`:
- Around line 818-875: Add a regression test that ensures empty confidence
arrays are handled gracefully: in the test file use
mockAPI.extractStructured.mockResolvedValue to return answer: { field1: 'value1'
} and confidence_score: { field1: [] } (and created_at/completion_reason as in
other tests), call setupHook('123', true) and await result.current.status ===
STATUS.SUCCESS, then call result.current.extractSuggestions('templateKey',
'global') and assert that the returned suggestion for field1 contains
aiSuggestion: 'value1' but does NOT include aiSuggestionConfidenceScore (or that
aiSuggestionConfidenceScore is undefined), referencing the existing tests like
"should pick the lowest confidence score when confidence_score is an array" and
the extractSuggestions flow to place the new test alongside them.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5e18a21d-3f8e-4863-b9cd-700933d38bfe
📒 Files selected for processing (4)
src/elements/content-sidebar/__tests__/useMetadataFieldSelection.test.tssrc/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsxsrc/elements/content-sidebar/hooks/useMetadataFieldSelection.tssrc/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts
| const lowestConfidence = Array.isArray(fieldConfidence) | ||
| ? fieldConfidence.reduce((min, curr) => (curr.score < min.score ? curr : min)) | ||
| : fieldConfidence; | ||
| result.aiSuggestionConfidenceScore = { | ||
| value: fieldConfidence.score, | ||
| level: fieldConfidence.level, | ||
| value: lowestConfidence.score, | ||
| level: lowestConfidence.level, | ||
| isAccepted: false, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Read-only verification: inspect reducer usage and test coverage for empty confidence arrays
rg -n -C3 "fieldConfidence\\.reduce|confidence_score.*\\[\\s*\\]" src/elements/content-sidebarRepository: box/box-ui-elements
Length of output: 984
🏁 Script executed:
#!/bin/bash
# Get more context around the problematic code to understand data flow
sed -n '250,310p' src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.tsRepository: box/box-ui-elements
Length of output: 2608
🏁 Script executed:
#!/bin/bash
# Search for where confidenceScores is assigned/populated
rg -n "confidenceScores\s*=" src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts -B2 -A2Repository: box/box-ui-elements
Length of output: 246
🏁 Script executed:
#!/bin/bash
# Look for test files related to this hook
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx | grep -i "sidebarmeta\|confidence"Repository: box/box-ui-elements
Length of output: 136
🏁 Script executed:
#!/bin/bash
# Check for type definitions of confidenceScores
rg -n "type.*Confidence|interface.*Confidence" src/elements/content-sidebar/hooks/ -A3Repository: box/box-ui-elements
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check test file for empty confidence array coverage
cat -n src/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsx | head -150Repository: box/box-ui-elements
Length of output: 4767
🏁 Script executed:
#!/bin/bash
# Search for confidence_score mock responses in tests
rg -n "confidence_score|confidenceScores" src/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsx -B2 -A2Repository: box/box-ui-elements
Length of output: 3744
🏁 Script executed:
#!/bin/bash
# Look for any array length checks or guards on confidence data
rg -n "length\s*[><=]|\.length\s*\?" src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.tsRepository: box/box-ui-elements
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Search for extractStructured API documentation or type definitions
rg -n "extractStructured|confidence_score" src/api/ -lRepository: box/box-ui-elements
Length of output: 201
🏁 Script executed:
#!/bin/bash
# Check if there's any filtering on confidenceScores before field access
sed -n '260,290p' src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.tsRepository: box/box-ui-elements
Length of output: 1400
🏁 Script executed:
#!/bin/bash
# Look for type definitions of the API response
rg -n "confidence_score.*:" src/ -A2 --type ts --type tsx | head -40Repository: box/box-ui-elements
Length of output: 90
🏁 Script executed:
#!/bin/bash
# Check the API schema definitions
cat -n src/api/schemas/AiExtractResponse.jsRepository: box/box-ui-elements
Length of output: 1134
🏁 Script executed:
#!/bin/bash
# Also check the other schema file
cat -n src/api/schemas/AiExtractStructured.jsRepository: box/box-ui-elements
Length of output: 3773
🏁 Script executed:
#!/bin/bash
# Check if there are any constraints on confidence_score array values in API tests
rg -n "confidence_score.*\[\s*\]|confidence_score.*\[\]" src/ -A2 -B2Repository: box/box-ui-elements
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Look at the full test for array confidence scores to see all test cases
sed -n '818,875p' src/elements/content-sidebar/__tests__/useSidebarMetadataFetcher.test.tsxRepository: box/box-ui-elements
Length of output: 2491
🏁 Script executed:
#!/bin/bash
# Check if confidence_score is validated or processed before being used
rg -n "isEmpty|!.*confidence|validateConfidence" src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts -B2 -A2Repository: box/box-ui-elements
Length of output: 547
🏁 Script executed:
#!/bin/bash
# Check if there's a filter or validation on confidenceScores before the field mapping
sed -n '258,275p' src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.tsRepository: box/box-ui-elements
Length of output: 749
Guard against empty confidence arrays before reduce.
Line 288 will throw a TypeError when fieldConfidence is an empty array [], because if (fieldConfidence) does not prevent empty arrays (they are truthy in JavaScript) and Array.reduce() without an initial value crashes on empty arrays. While type definitions suggest confidence_score contains single objects, the code explicitly handles arrays and tests confirm arrays are a valid response format. This gap should be protected against.
Proposed fix
- const lowestConfidence = Array.isArray(fieldConfidence)
- ? fieldConfidence.reduce((min, curr) => (curr.score < min.score ? curr : min))
- : fieldConfidence;
- result.aiSuggestionConfidenceScore = {
- value: lowestConfidence.score,
- level: lowestConfidence.level,
- isAccepted: false,
- };
+ const confidenceEntries = Array.isArray(fieldConfidence) ? fieldConfidence : [fieldConfidence];
+ const validConfidenceEntries = confidenceEntries.filter(
+ entry => entry && typeof entry.score === 'number',
+ );
+
+ if (validConfidenceEntries.length > 0) {
+ const lowestConfidence = validConfidenceEntries.reduce((min, curr) =>
+ curr.score < min.score ? curr : min,
+ );
+ result.aiSuggestionConfidenceScore = {
+ value: lowestConfidence.score,
+ level: lowestConfidence.level,
+ isAccepted: false,
+ };
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const lowestConfidence = Array.isArray(fieldConfidence) | |
| ? fieldConfidence.reduce((min, curr) => (curr.score < min.score ? curr : min)) | |
| : fieldConfidence; | |
| result.aiSuggestionConfidenceScore = { | |
| value: fieldConfidence.score, | |
| level: fieldConfidence.level, | |
| value: lowestConfidence.score, | |
| level: lowestConfidence.level, | |
| isAccepted: false, | |
| const confidenceEntries = Array.isArray(fieldConfidence) ? fieldConfidence : [fieldConfidence]; | |
| const validConfidenceEntries = confidenceEntries.filter( | |
| entry => entry && typeof entry.score === 'number', | |
| ); | |
| if (validConfidenceEntries.length > 0) { | |
| const lowestConfidence = validConfidenceEntries.reduce((min, curr) => | |
| curr.score < min.score ? curr : min, | |
| ); | |
| result.aiSuggestionConfidenceScore = { | |
| value: lowestConfidence.score, | |
| level: lowestConfidence.level, | |
| isAccepted: false, | |
| }; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts` around lines
287 - 293, The current computation of lowestConfidence in
useSidebarMetadataFetcher.ts uses Array.prototype.reduce without guarding
against an empty array, which throws when fieldConfidence === []; fix by
checking Array.isArray(fieldConfidence) && fieldConfidence.length > 0 before
reducing (or use reduce with an initial value such as fieldConfidence[0]) and
provide a safe fallback when the array is empty or fieldConfidence is
null/undefined so that result.aiSuggestionConfidenceScore is only assigned when
a valid lowestConfidence with .score and .level exists (or assign a safe
default/null). Update the logic around the symbols fieldConfidence,
lowestConfidence, and result.aiSuggestionConfidenceScore to handle empty arrays
and avoid TypeError.
There was a problem hiding this comment.
Not a chance to get an empty array here
There was a problem hiding this comment.
@dlasecki-box, understood — if the API guarantees that confidence_score[field.key] is always a non-empty array when present, the reduce call is safe and the guard is unnecessary. Thanks for the clarification!
✏️ Learnings added
Learnt from: dlasecki-box
Repo: box/box-ui-elements PR: 4502
File: src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts:287-293
Timestamp: 2026-04-13T21:25:24.516Z
Learning: In `src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts`, the `confidence_score[field.key]` value from the Intelligence API is guaranteed to be non-empty when present (truthy). An empty array `[]` is not a possible response, so no guard before `Array.prototype.reduce` is needed for `fieldConfidence`.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
For multiSelect metadata fields, the Intelligence API returns per-value confidence scores, confidence levels, and bounding boxes - rather than a single set per field. This doesn't align with the current review/approval (HITL) process or meta-metadata schema, which expect one confidence score and one flat list of bounding boxes per field.
This PR applies the agreed-upon workaround:
Some per-value granularity is lost (one score/level stored per field instead of per option), but this is acceptable for the HITL process.
Summary by CodeRabbit
Release Notes
Bug Fixes
Tests