Partial success responses

A request that asks for five tasks should not return a 500 when one of them fails. The caller wanted sentiment, entities, keywords, summary, and emotion. Four of those came back clean. Surface the four successes, surface the one failure, let the caller decide what to do about it.

Per-task isolation in one picture

Each task runs in its own try block. Successes feed the response body, failures land in the errors map.

models.py
python
class AnalyzeResponse(BaseModel):
    text: str
    tasks: List[str]
    sentiment: Optional[SentimentResult] = None
    entities: Optional[List[Entity]] = None
    keywords: Optional[List[str]] = None
    summary: Optional[str] = None
    emotion: Optional[EmotionResult] = None
    errors: dict = Field(
        default_factory=dict,
        description="Map of task name to error message for any task that failed.",
    )

The errors dict is the key design choice. Task name to error message. Callers can look up errors["summary"] and render a "Summary unavailable" badge in the UI. No exceptions bubble up from the service. The response shape is always valid.

sample response
json
{
  "text": "Great product, terrible support.",
  "tasks": ["sentiment", "entities", "summary", "emotion"],
  "sentiment": { "label": "negative", "score": -0.4, "rationale": "Mixed review with net-negative tilt" },
  "entities": [],
  "keywords": null,
  "summary": "A customer praises the product but criticises support.",
  "emotion": null,
  "errors": {
    "emotion": "Rate limit hit, retry after 30s"
  }
}

Four tasks ran cleanly, emotion hit a rate limit. The caller gets everything that worked plus a clear signal about what did not. This is partial success done right.

A 500 throws away the work that succeeded. If four tasks cost you four LLM calls and one failed, a 500 means the caller has to retry all five and pay again. Partial success lets the caller keep the good data and only retry the one that failed. It also matches how the UI actually wants to behave: render the sentiment badge, show a small warning on the emotion pill, do not blank the whole page.