Design CSV upload endpoint with GPT classification
Scale AISoftware Engineer · OnsiteLast updated
Implement a backend processing pipeline that parses an uploaded CSV file, enriches each text record via an external classification client, and persists structured results to a local JSON store.
Problem Statement
You are building a backend service for an AI data-labeling platform. Clients upload CSV files containing unlabeled text snippets, which must be validated, classified via an external GPT-based classification client, and saved locally as structured JSON for downstream evaluation.
Implement the CSVClassificationPipeline class with the following specifications:
- Parse & Validate CSV: The input is raw bytes representing a CSV file. The CSV must have headers
id(string) andtext(string). Skip rows whereidortextis missing or whitespace-only, incrementing a skipped counter. - Classify: For each valid row, call
classifier.classify(text). The classifier returns a dictionary{"label": str, "confidence": float}. If the classifier throws an exception, capture the error for that row ("error": str) without failing the entire file. - Persist: Write the final processed output to a local JSON file at
output_path. The JSON output should contain:summary: total rows, successful classifications, failed classifications, skipped rows.results: a list of records containingid,text, and eitherlabel+confidenceorerror.
- Return Value: Return the summary dictionary.
Interface
from typing import Any, Dict, Protocol
class LLMClassifier(Protocol):
def classify(self, text: str) -> Dict[str, Any]:
"""Returns: {'label': str, 'confidence': float}"""
...
class CSVClassificationPipeline:
def __init__(self, classifier: LLMClassifier):
self.classifier = classifier
def process_file(self, file_content: bytes, output_path: str) -> Dict[str, Any]:
"""
Processes the raw CSV bytes, persists results to output_path, and returns summary.
"""
pass
Constraints
- File content size is up to 10 MB (approximately 5,000 rows).
- Text snippets may contain UTF-8 characters, commas, and embedded newlines.
- If the CSV header is missing required columns (
id,text) or the file cannot be parsed, raise aValueError.
Example
csv_data = b"""id,text
1,"Great product! Highly recommend."
2,
3,"Broken on arrival."
"""
# classifier returns:
# classify("Great product! Highly recommend.") -> {"label": "positive", "confidence": 0.98}
# classify("Broken on arrival.") -> raises RuntimeError("API timeout")
pipeline = CSVClassificationPipeline(classifier)
summary = pipeline.process_file(csv_data, "results/batch_1.json")
Output Summary:
{
"total_rows": 3,
"successful": 1,
"failed": 1,
"skipped": 1
}
Persisted JSON (results/batch_1.json):
{
"summary": {"total_rows": 3, "successful": 1, "failed": 1, "skipped": 1},
"results": [
{"id": "1", "text": "Great product! Highly recommend.", "label": "positive", "confidence": 0.98},
{"id": "3", "text": "Broken on arrival.", "error": "API timeout"}
]
}
Hints
Hint 1
Decode the byte stream into UTF-8 text using `io.StringIO` and use Python's built-in `csv.DictReader` to handle escaped quotes and multiline fields correctly.
Hint 2
Validate header presence before processing rows by checking `reader.fieldnames` for required keys `id` and `text`.
Hint 3
Wrap individual classification calls in a `try...except` block so that external API failures on single rows do not abort the remaining dataset.
Follow-up questions
- How would you handle rate limits (HTTP 429) and concurrent network requests when processing 50,000 rows?
- How would you refactor this endpoint for an asynchronous architecture using background worker queues (e.g., Celery or SQS) and webhook notifications?
- How can you ensure atomic writes when writing the output JSON file so readers never read partially written files?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.