Wrappuccino provides a clean, modular way to deploy full machine learning pipelines as REST APIs using FastAPI and Gunicorn. Each pipeline can consist of a preprocessing script, a vectorizer, and a modelβall packaged into a single folder for clarity and reusability.
- Pipeline-based organization: Each ML pipeline lives in its own subfolder under
pipelines/
- Optional preprocessing: Supports modular text transformations before vectorization
- Automatic API generation: REST API with comprehensive endpoint documentation
- Scalable: FastAPI with Gunicorn for production deployment
- Easy to extend: Add new pipelines by simply creating folders with model files
wrappuccino/
βββ pipelines/
β βββ sentiment_classification/
β β βββ preprocessing.py # Custom text preprocessing
β β βββ vectorizer.pkl # TF-IDF vectorizer
β β βββ model.pkl # Random Forest classifier
β βββ iris_classifier/
β β βββ model.pkl # Iris dataset classifier
βββ app.py # Main FastAPI application
βββ main.py # Application entry point
βββ model_loader.py # ML model loading utilities
βββ pipeline.py # Pipeline discovery and validation
βββ README.md # This file
The project uses uv
for dependency management. Dependencies are automatically installed:
- FastAPI
- uvicorn
- scikit-learn
- numpy
- pydantic
- requests
uvicorn app:app --host 0.0.0.0 --port 5000 --reload
Or alternatively:
python app.py
The server will start on http://localhost:5000
with the following endpoints:
- API Documentation:
http://localhost:5000/docs
(Interactive Swagger UI) - ReDoc Documentation:
http://localhost:5000/redoc
(Alternative documentation) - Health Check:
http://localhost:5000/health
- List Pipelines:
http://localhost:5000/pipelines
- Make Predictions:
http://localhost:5000/predict
Returns a list of available pipeline folders.
Example Response:
{
"available_pipelines": ["iris_classifier", "sentiment_classification"]
}
Use this endpoint to run predictions via ML pipelines.
{
"pipeline_name": "sentiment_classification",
"text": "I love this product! It works perfectly."
}
{
"pipeline_name": "iris_classifier",
"features": [5.1, 3.5, 1.4, 0.2]
}
{
"pipeline_name": "sentiment_classification",
"prediction": 1,
"confidence": 0.93,
"preprocessing_applied": true,
"vectorizer_applied": true
}
Health check endpoint for monitoring.
Example Response:
{
"status": "healthy",
"pipelines_loaded": 2
}
To add a new ML pipeline, create a folder under pipelines/
with the following structure:
pipelines/your_pipeline_name/
βββ model.pkl # Required: Your trained ML model
βββ vectorizer.pkl # Optional: For text processing
βββ preprocessing.py # Optional: Custom preprocessing functions
pipelines/your_pytorch_pipeline/
βββ model.pth # Required: PyTorch model (.pth or .pt)
βββ model_architecture.py # Required if using state dict
βββ label_encoder.pkl # Optional: Label mapping
βββ vectorizer.pkl # Optional: For text processing
βββ preprocessing.py # Optional: Custom preprocessing functions
pipelines/your_onnx_pipeline/
βββ model.onnx # Required: ONNX model file
βββ label_encoder.pkl # Optional: Label mapping
βββ vectorizer.pkl # Optional: For text processing
βββ preprocessing.py # Optional: Custom preprocessing functions
model.pkl
- Scikit-learn model saved with pickle/joblibmodel.pth
ormodel.pt
- PyTorch model (full model or state dict)model.onnx
- ONNX optimized model for cross-platform deployment
-
model_architecture.py
(PyTorch only)- Required if saving PyTorch state dict instead of full model
- Must define
create_model()
function that returns model instance
-
label_encoder.pkl
(PyTorch/ONNX)- Maps numeric model outputs to meaningful labels
- Used for classification tasks with string labels
-
vectorizer.pkl
(All model types)- Text vectorizer (TF-IDF, CountVectorizer, etc.)
- Converts text to numerical features for the model
-
preprocessing.py
(All model types)- Must define a
custom_preprocess(text: str) -> str
function - Applied before vectorization for domain-specific text cleaning
- Must define a
# Create and save a model
from sklearn.ensemble import RandomForestClassifier
import pickle
model = RandomForestClassifier()
model.fit(X_train, y_train)
with open('pipelines/my_pipeline/model.pkl', 'wb') as f:
pickle.dump(model, f)
The pipeline will be automatically discovered and available via the API.
# Test root endpoint
curl http://localhost:5000/
# List available pipelines
curl http://localhost:5000/pipelines
# Make a prediction with numeric features
curl -X POST http://localhost:5000/predict \
-H "Content-Type: application/json" \
-d '{"pipeline_name": "iris_classifier", "features": [5.1, 3.5, 1.4, 0.2]}'
# Make a prediction with text
curl -X POST http://localhost:5000/predict \
-H "Content-Type: application/json" \
-d '{"pipeline_name": "sentiment_classification", "text": "I love this product!"}'
import requests
# Test numeric prediction
iris_data = {
"pipeline_name": "iris_classifier",
"features": [5.1, 3.5, 1.4, 0.2]
}
response = requests.post("http://localhost:5000/predict", json=iris_data)
print(response.json())
# Test text prediction
text_data = {
"pipeline_name": "sentiment_classification",
"text": "Today's working was incredible. I couldn't be happier!"
}
response = requests.post("http://localhost:5000/predict", json=text_data)
print(response.json())
- Type: Numeric features
- Input: 4 numeric features (sepal length, sepal width, petal length, petal width)
- Output: Species classification (0=setosa, 1=versicolor, 2=virginica)
- Model: Random Forest Classifier
- Type: Text processing
- Input: Text expressing opinions or sentiments
- Output: Binary classification (0=negative sentiment, 1=positive sentiment)
- Features: Custom preprocessing + TF-IDF vectorization + Random Forest
- Preprocessing: Text cleaning, lowercasing, special character removal
For production deployment, use Gunicorn with proper configuration:
gunicorn --bind 0.0.0.0:5000 --worker-class sync --workers 4 main:app
SESSION_SECRET
: Flask session secret key (defaults to "wrappuccino-secret-key")
-
Pipeline not found
- Ensure
model.pkl
exists in the pipeline folder - Check that the folder name matches the
pipeline_name
in requests
- Ensure
-
Preprocessing errors
- Verify
preprocessing.py
definescustom_preprocess(text: str) -> str
- Check for import errors in the preprocessing module
- Verify
-
Vectorizer issues
- Ensure vectorizer is trained and saved properly
- Verify it implements the
.transform()
method
-
Model prediction errors
- Check that input features match the model's expected format
- Ensure the model was trained and saved correctly
Server logs provide detailed information about:
- Pipeline discovery and validation
- Model loading success/failure
- Prediction request processing
- Error details and stack traces
All prediction responses follow this format:
{
"pipeline_name": "string", // Name of the pipeline used
"prediction": "any", // Model prediction result
"confidence": 0.95, // Confidence score (0-1, optional)
"preprocessing_applied": true, // Whether preprocessing was used
"vectorizer_applied": true // Whether vectorization was applied
}
Error responses:
{
"error": "string" // Error description
}
This project is open source and available under the MIT License.
To contribute to Wrappuccino:
- Fork the repository
- Create your feature branch
- Add your ML pipeline following the structure guidelines
- Test your pipeline with the API
- Submit a pull request
Happy ML serving with Wrappuccino! βπ€