This example shows how to build a basic text analysis service using transformers and custom API endpoints. Perfect for getting started with custom Chutes.
What We'll Build
A simple text sentiment analysis service that:
📊 Analyzes sentiment using a pre-trained model
🔍 Validates input with Pydantic schemas
🚀 Provides REST API for easy integration
📦 Uses custom Docker image with optimized dependencies
Complete Example
sentiment_analyzer.py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from pydantic import BaseModel, Field
from fastapi import HTTPException
from chutes.chute import Chute, NodeSelector
from chutes.image import Image
# === INPUT/OUTPUT SCHEMAS ===
class TextInput(BaseModel):
text: str = Field(..., min_length=5, max_length=1000, description="Text to analyze")
class Config:
schema_extra = {
"example": {
"text": "I love using this new AI service!"
}
}
class SentimentResult(BaseModel):
text: str
sentiment: str # POSITIVE, NEGATIVE, NEUTRAL
confidence: float
processing_time: float
# === CUSTOM IMAGE ===
image = (
Image(username="myuser", name="sentiment-analyzer", tag="1.0")
.from_base("nvidia/cuda:12.4.1-runtime-ubuntu22.04")
.with_python("3.11")
.run_command("pip install torch>=2.4.0 transformers>=4.44.0 accelerate>=0.33.0")
.with_env("TRANSFORMERS_CACHE", "/app/models")
.run_command("mkdir -p /app/models")
)
# === CHUTE DEFINITION ===
chute = Chute(
username="myuser",
name="sentiment-analyzer",
image=image,
tagline="Simple sentiment analysis with transformers",
readme="""
# Sentiment Analyzer
A simple sentiment analysis service using DistilBERT.
## Usage
Send a POST request to `/analyze`:
```bash
curl -X POST https://myuser-sentiment-analyzer.chutes.ai/analyze \\
-H "Content-Type: application/json" \\
-d '{"text": "I love this product!"}'
Response
{
"text": "I love this product!",
"sentiment": "POSITIVE",
"confidence": 0.99,
"processing_time": 0.05
}
@chute.on_startup()
async def load_model(self):
"""Load the sentiment analysis model on startup."""
print("Loading sentiment analysis model...")
model_name = "distilbert-base-uncased-finetuned-sst-2-english"
# Load tokenizer and model
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForSequenceClassification.from_pretrained(model_name)
# Move to GPU if available
self.device = "cuda" if torch.cuda.is_available() else "cpu"
self.model.to(self.device)
self.model.eval() # Set to evaluation mode
print(f"Model loaded on device: {self.device}")
=== API ENDPOINTS ===
@chute.cord(
public_api_path="/analyze",
method="POST",
input_schema=TextInput,
output_content_type="application/json"
)
async def analyze_sentiment(self, data: TextInput) -> SentimentResult:
"""Analyze the sentiment of the input text."""
import time
async def test_locally():
# Simulate startup
await load_model(chute)
# Test analysis
test_input = TextInput(text="I love this new AI service!")
result = await analyze_sentiment(chute, test_input)
print(f"Result: {result}")
asyncio.run(test_locally())
import requests
texts = [
"I love this product!", # Should be POSITIVE
"This is terrible.", # Should be NEGATIVE
"It's okay, nothing special.", # Could be NEGATIVE or POSITIVE
"Amazing technology!", # Should be POSITIVE
"Poor quality." # Should be NEGATIVE
]
for text in texts:
response = requests.post(
"https://myuser-sentiment-analyzer.chutes.ai/analyze",
json={"text": text}
)
result = response.json()
print(f"'{text}' -> {result['sentiment']} ({result['confidence']:.2f})")