Validate a Review Summary

Validate a Review Summary

A fictional online bookshop asks a language model to summarize each customer review as JSON. A moderation dashboard filters the summaries by sentiment and topic and averages the rating per book, so a reply that breaks the contract must never reach it. Model replies are untrusted text: most follow the contract and some do not.

Implement validateReviewSummary(raw), where raw is the model's reply as a string. Return:

  • { ok: true, value: { rating, sentiment, topics } } when the reply is accepted
  • { ok: false, errors: [...] } with error codes when it is not

Bad model output is an expected input, so the function returns codes and never throws.

The contract

FieldRule
ratingAn integer from 1 to 5. An integer is a JSON number with no fractional part, so 4.0 counts as 4. Booleans, strings, null, and fractions are not integers.
sentimentExactly "positive", "mixed", or "negative" (case-sensitive, no trimming).
topicsAn array of 0 to 3 items. Each item is exactly "price", "delivery", "condition", or "story", and no item repeats.
any other keyNot allowed.

Error codes, in check order

StepChecksCodes, in rule order
1whole replyreply:parse: raw is not valid JSON. Return at once with only this code.
2whole replyreply:type: the parsed value is not a plain object (an array, null, a string, a number, or a boolean). Return at once with only this code.
3ratingrating:missing (the key is absent), then rating:type (not an integer), then rating:range (below 1 or above 5)
4sentimentsentiment:missing, then sentiment:type (not a string), then sentiment:value (not one of the three values)
5topicstopics:missing, then topics:type (not an array), then topics:too-many (more than 3 items), then topics:value (any item that is not one of the four topics, non-strings included; one code even if several items fail), then topics:duplicate (an item appears more than once)
6other keysextra:<key> for each key the contract does not name, written exactly as in the reply
  • Steps 3, 4, and 5 each add at most one code: the first rule that fails for that field. All three fields are always checked.
  • A key set to null is present, not missing: "sentiment": null is sentiment:type.
  • Keys match exactly: "Rating" or " rating" is not rating, so it counts as a missing field and an extra key.
  • Field codes come first in contract order (rating, sentiment, topics), whatever order the reply uses. The extra: codes follow, sorted by plain string comparison (JavaScript sort(), Python sorted()), so uppercase letters come before lowercase ones. Sort only the extra codes.
  • With no codes, the reply is accepted. Build value as a new object with the keys rating, sentiment, topics in that order: rating is an integer (4.0 becomes 4), and topics keeps the reply's order.
  • The result has exactly two keys: ok and value, or ok and errors.
  • Do not repair keys or values: no trimming, no lowercasing, no converting "4" to 4.

Examples

Example 1

JavaScript
validateReviewSummary('{"topics": ["price", "price"], "summary": "Arrived late", "sentiment": "neutral", "rating": 3}');
// { ok: false, errors: ['sentiment:value', 'topics:duplicate', 'extra:summary'] }

Rating 3 passes. "neutral" is a string but not an allowed value. Both topics are allowed, but "price" repeats. summary is not in the contract. The reply lists topics first, yet the codes follow rating, sentiment, topics.

Example 2

JavaScript
validateReviewSummary('{"topics": ["delivery"], "sentiment": "mixed", "rating": 3.0}');
// { ok: true, value: { rating: 3, sentiment: 'mixed', topics: ['delivery'] } }

3.0 has no fractional part, so it is the integer 3 (Python returns the int 3). The value is rebuilt in contract order.

Example 3

JavaScript
validateReviewSummary('{"rating": 0, "sentiment": null}');
// { ok: false, errors: ['rating:range', 'sentiment:type', 'topics:missing'] }

0 is an integer below 1. sentiment is present with the value null, which is not a string. topics is absent.

In Python, implement validate_review_summary(raw) and return a dict with the same keys ("ok", "value", "errors"). errors and topics are lists, and rating is an int.

Constraints

  • raw is always a string. Whitespace around the JSON is allowed (both parsers accept it); values inside are never trimmed.
  • Key names in the tests are ASCII, so JavaScript sort() and Python sorted() agree.
  • NaN, Infinity, and repeated keys are outside the tests.
  • The first test checks eight replies, R1 to R8, from the AI Engineering question this problem belongs to. Predict their results there before you open or run the first test.

Expected Complexity

O(n + k log k) time for a reply of n characters with k extra keys, and O(n) extra space for the parsed value.

EASY
AI Engineering
Structured Output
Error Handling
Beginner

0 views

Solution

Hints

Hint 1
Hint 2
Premium
Hint 3
Premium
Hint 4
Premium