walidhadri's picture
Initial HF Space app
84f224f

A newer version of the Gradio SDK is available: 6.13.0

Upgrade

Now I have a comprehensive understanding of the ecosystem. Let me create a detailed implementation plan for your time series forecasting Hugging Face Space.

Time Series Forecasting HF Space - Implementation Plan Overview Build a Gradio-based Hugging Face Space that allows users to upload time series data, run predictions using multiple foundation models (starting with Toto), compare model outputs visually, and optionally evaluate against ground truth.

Phase 1: Core Infrastructure & Toto Model Integration Step 1.1: Project Setup Create the basic HF Space structure: timeseries-forecasting-space/ โ”œโ”€โ”€ app.py # Main Gradio application โ”œโ”€โ”€ requirements.txt # Dependencies โ”œโ”€โ”€ models/ โ”‚ โ”œโ”€โ”€ init.py โ”‚ โ”œโ”€โ”€ base_model.py # Abstract base class for all models โ”‚ โ””โ”€โ”€ toto_model.py # Toto model wrapper โ”œโ”€โ”€ utils/ โ”‚ โ”œโ”€โ”€ init.py โ”‚ โ”œโ”€โ”€ data_processing.py # Data loading, validation, preprocessing โ”‚ โ”œโ”€โ”€ visualization.py # Plotting functions โ”‚ โ””โ”€โ”€ metrics.py # Evaluation metrics (MAE, RMSE, MASE, sMAPE, etc.) โ”œโ”€โ”€ README.md โ””โ”€โ”€ examples/ # Sample CSV files for users to try โ””โ”€โ”€ sample_univariate.csv โ””โ”€โ”€ sample_multivariate.csv Step 1.2: Data Processing Module (utils/data_processing.py) Implement functions to:

load_timeseries_from_file(file)

Accept CSV/Excel uploads Auto-detect timestamp column (look for datetime-like columns) Auto-detect target column(s) (numeric columns) Handle both univariate and multivariate data Return pandas DataFrame with standardized format

validate_timeseries(df)

Check for missing values (offer interpolation options) Validate timestamp continuity Check minimum length requirements for models Return validation report

preprocess_for_model(df, model_name, prediction_length)

Convert to model-specific input format Handle frequency inference Split into context (history) and optional holdout

infer_frequency(df, timestamp_col)

Auto-detect time series frequency (hourly, daily, etc.)

Step 1.3: Base Model Interface (models/base_model.py) Create abstract base class: pythonfrom abc import ABC, abstractmethod

class BaseForecastModel(ABC): @abstractmethod def load_model(self): """Load/initialize the model""" pass

@abstractmethod
def predict(self, context_data, prediction_length, **kwargs):
    """
    Generate forecasts
    Returns: dict with keys:
      - 'mean': point forecast (array)
      - 'quantiles': dict of quantile forecasts (optional)
      - 'samples': raw samples if available (optional)
    """
    pass

@abstractmethod
def get_model_info(self):
    """Return model metadata (name, description, supports_multivariate, etc.)"""
    pass

Step 1.4: Toto Model Wrapper (models/toto_model.py) Implement Toto integration: pythonclass TotoModel(BaseForecastModel): def init(self, device='cuda' if torch.cuda.is_available() else 'cpu'): self.device = device self.model = None self.forecaster = None

def load_model(self):
    from toto.model.toto import Toto
    from toto.inference.forecaster import TotoForecaster
    
    self.model = Toto.from_pretrained('Datadog/Toto-Open-Base-1.0').to(self.device)
    self.forecaster = TotoForecaster(self.model.model)

def predict(self, context_data, prediction_length, num_samples=256, **kwargs):
    # Convert pandas DataFrame to MaskedTimeseries format
    # context_data: DataFrame with timestamp index, numeric columns as variates
    
    from toto.data.util.dataset import MaskedTimeseries
    
    # Prepare input tensors
    series = torch.tensor(context_data.values.T, dtype=torch.float32).to(self.device)
    # ... handle timestamp_seconds, time_interval_seconds
    
    inputs = MaskedTimeseries(
        series=series,
        padding_mask=torch.full_like(series, True, dtype=torch.bool),
        id_mask=torch.zeros_like(series),
        timestamp_seconds=timestamp_seconds,
        time_interval_seconds=time_interval_seconds,
    )
    
    forecast = self.forecaster.forecast(
        inputs,
        prediction_length=prediction_length,
        num_samples=num_samples,
        samples_per_batch=min(num_samples, 256),
    )
    
    return {
        'mean': forecast.median.cpu().numpy(),  # or use median
        'quantiles': {
            0.1: forecast.quantile(0.1).cpu().numpy(),
            0.5: forecast.quantile(0.5).cpu().numpy(),
            0.9: forecast.quantile(0.9).cpu().numpy(),
        },
        'samples': forecast.samples.cpu().numpy() if hasattr(forecast, 'samples') else None
    }

def get_model_info(self):
    return {
        'name': 'Toto',
        'full_name': 'Toto-Open-Base-1.0',
        'description': 'Time Series Optimized Transformer for Observability by Datadog',
        'supports_multivariate': True,
        'supports_covariates': False,
        'max_context_length': 4096,
        'parameters': '151M',
    }

Step 1.5: Visualization Module (utils/visualization.py) Implement plotting functions using Plotly (for interactive plots):

plot_forecast(history_df, forecast_dict, ground_truth=None, model_name='Model')

Plot historical data Plot point forecast with confidence intervals (quantiles) Optionally overlay ground truth Support multivariate (subplot per variable or selectable)

plot_model_comparison(history_df, forecasts_dict, ground_truth=None)

forecasts_dict: {model_name: forecast_dict} Overlay multiple model predictions Color-code by model

plot_residuals(ground_truth, predictions, model_name)

Error distribution plot Residuals over time

Step 1.6: Metrics Module (utils/metrics.py) Implement evaluation metrics: pythondef calculate_metrics(y_true, y_pred, seasonality=1): """ Returns dict of metrics: - MAE, RMSE, MAPE, sMAPE, MASE, WAPE """ pass

def create_metrics_table(ground_truth, predictions_dict): """ predictions_dict: {model_name: predictions_array} Returns pandas DataFrame comparison table """ pass

Phase 2: Gradio Interface (app.py) Step 2.1: Main Layout Structure Use gr.Blocks() for complex layout: pythonimport gradio as gr

with gr.Blocks(title="Time Series Forecasting Playground") as demo: gr.Markdown("# ๐Ÿ”ฎ Time Series Forecasting Playground") gr.Markdown("Upload your time series data and compare predictions from state-of-the-art models")

with gr.Row():
    # Left column: Data Upload & Configuration
    with gr.Column(scale=1):
        # Data upload section
        # Model selection section
        # Parameters section
    
    # Right column: Visualization & Results
    with gr.Column(scale=2):
        # Plots
        # Metrics tables

Step 2.2: Data Upload Section pythonwith gr.Group(): gr.Markdown("### ๐Ÿ“ Data Upload")

file_upload = gr.File(
    label="Upload Time Series (CSV/Excel)",
    file_types=[".csv", ".xlsx", ".xls"]
)

# After upload, show data preview
data_preview = gr.DataFrame(
    label="Data Preview (first 10 rows)",
    interactive=False
)

# Column configuration (auto-detected but editable)
with gr.Row():
    timestamp_col = gr.Dropdown(label="Timestamp Column", choices=[])
    target_cols = gr.Dropdown(
        label="Target Column(s)", 
        choices=[], 
        multiselect=True
    )

# Optional: Upload ground truth for evaluation
with gr.Accordion("๐Ÿ“Š Upload Ground Truth (Optional)", open=False):
    ground_truth_file = gr.File(
        label="Ground Truth for Evaluation",
        file_types=[".csv", ".xlsx"]
    )

Step 2.3: Model Selection Section pythonwith gr.Group(): gr.Markdown("### ๐Ÿค– Model Selection")

model_checkboxes = gr.CheckboxGroup(
    choices=["Toto"],  # Will expand: ["Toto", "Chronos-2", "Chronos-Bolt", ...]
    value=["Toto"],
    label="Select Models to Run"
)

# Model info accordion
with gr.Accordion("Model Information", open=False):
    model_info_display = gr.Markdown()

Step 2.4: Forecast Parameters Section pythonwith gr.Group(): gr.Markdown("### โš™๏ธ Forecast Parameters")

prediction_length = gr.Slider(
    minimum=1,
    maximum=720,
    value=24,
    step=1,
    label="Prediction Horizon (steps)"
)

num_samples = gr.Slider(
    minimum=50,
    maximum=500,
    value=256,
    step=50,
    label="Number of Samples (for probabilistic forecast)"
)

with gr.Row():
    show_confidence = gr.Checkbox(
        value=True, 
        label="Show Confidence Intervals"
    )
    confidence_level = gr.Dropdown(
        choices=["80%", "90%", "95%"],
        value="90%",
        label="Confidence Level"
    )

run_button = gr.Button("๐Ÿš€ Generate Forecasts", variant="primary")

Step 2.5: Results Display Section pythonwith gr.Group(): gr.Markdown("### ๐Ÿ“ˆ Results")

# Main forecast plot
forecast_plot = gr.Plot(label="Forecast Visualization")

# Variable selector for multivariate
variable_selector = gr.Dropdown(
    label="Select Variable to Display",
    choices=[],
    visible=False  # Show only for multivariate
)

# Tabs for different views
with gr.Tabs():
    with gr.Tab("Comparison Plot"):
        comparison_plot = gr.Plot(label="Model Comparison")
    
    with gr.Tab("Metrics"):
        metrics_table = gr.DataFrame(
            label="Evaluation Metrics",
            interactive=False
        )
    
    with gr.Tab("Residuals"):
        residuals_plot = gr.Plot(label="Residual Analysis")

# Download results
with gr.Row():
    download_predictions = gr.File(label="Download Predictions (CSV)")
    download_metrics = gr.File(label="Download Metrics (CSV)")

Step 2.6: Event Handlers python# On file upload: parse and preview data file_upload.change( fn=handle_file_upload, inputs=[file_upload], outputs=[data_preview, timestamp_col, target_cols, ...] )

On run button click

run_button.click( fn=run_forecasts, inputs=[ file_upload, timestamp_col, target_cols, model_checkboxes, prediction_length, num_samples, ground_truth_file, show_confidence, confidence_level ], outputs=[ forecast_plot, comparison_plot, metrics_table, residuals_plot, download_predictions, download_metrics, variable_selector ] )

Variable selector for multivariate update

variable_selector.change( fn=update_plot_for_variable, inputs=[variable_selector, ...], outputs=[forecast_plot] ) Step 2.7: Core Function Implementations pythondef handle_file_upload(file): """ Parse uploaded file, detect columns, return preview """ df = load_timeseries_from_file(file) validation = validate_timeseries(df)

# Auto-detect timestamp and target columns
timestamp_col = detect_timestamp_column(df)
target_cols = detect_numeric_columns(df)

return (
    df.head(10),           # preview
    timestamp_col,         # dropdown selection
    target_cols,           # dropdown choices
    validation['report']   # any warnings
)

def run_forecasts(file, ts_col, target_cols, models, pred_len, n_samples, gt_file, show_ci, ci_level): """ Main orchestration function """ # 1. Load and preprocess data df = load_timeseries_from_file(file)

# 2. Load ground truth if provided
ground_truth = None
if gt_file:
    ground_truth = load_timeseries_from_file(gt_file)

# 3. Run each selected model
results = {}
for model_name in models:
    model = MODEL_REGISTRY[model_name]()
    model.load_model()
    
    context_data = preprocess_for_model(df, model_name, pred_len)
    forecast = model.predict(context_data, pred_len, num_samples=n_samples)
    results[model_name] = forecast

# 4. Generate visualizations
forecast_fig = plot_forecast(df, results[models[0]], ground_truth, models[0])
comparison_fig = plot_model_comparison(df, results, ground_truth)

# 5. Calculate metrics if ground truth provided
metrics_df = None
residuals_fig = None
if ground_truth is not None:
    metrics_df = create_metrics_table(ground_truth, results)
    residuals_fig = plot_residuals(ground_truth, results)

# 6. Prepare downloadable files
predictions_csv = create_predictions_csv(results)
metrics_csv = metrics_df.to_csv() if metrics_df is not None else None

return (
    forecast_fig, comparison_fig, metrics_df,
    residuals_fig, predictions_csv, metrics_csv,
    target_cols  # for variable selector
)

Phase 3: Add More Models (Incremental) Step 3.1: Chronos-2 Model (models/chronos_model.py) pythonclass Chronos2Model(BaseForecastModel): def load_model(self): from chronos import Chronos2Pipeline self.pipeline = Chronos2Pipeline.from_pretrained( "amazon/chronos-2", device_map="cuda" if torch.cuda.is_available() else "cpu" )

def predict(self, context_df, prediction_length, **kwargs):
    # Chronos-2 uses DataFrame format directly
    pred_df = self.pipeline.predict_df(
        context_df,
        prediction_length=prediction_length,
        quantile_levels=[0.1, 0.5, 0.9],
        id_column='id',
        timestamp_column='timestamp',
        target='target'
    )
    # Convert to standard format
    return self._format_output(pred_df)

def get_model_info(self):
    return {
        'name': 'Chronos-2',
        'description': 'Universal forecasting model by Amazon',
        'supports_multivariate': True,
        'supports_covariates': True,
    }

Step 3.2: Chronos-Bolt Model (models/chronos_bolt_model.py) Similar wrapper for faster inference. Step 3.3: Statistical Baselines (models/baselines.py) Add simple baselines for comparison:

Naive (last value repeat) Seasonal Naive Moving Average AutoARIMA (via statsforecast)

Step 3.4: Update Model Registry pythonMODEL_REGISTRY = { 'Toto': TotoModel, 'Chronos-2': Chronos2Model, 'Chronos-Bolt': ChronosBoltModel, 'Naive': NaiveModel, 'AutoARIMA': AutoARIMAModel, } Step 3.5: Update UI pythonmodel_checkboxes = gr.CheckboxGroup( choices=list(MODEL_REGISTRY.keys()), value=["Toto"], label="Select Models to Run" )

Phase 4: Enhanced Features Step 4.1: Covariate Support For models that support covariates (Chronos-2): pythonwith gr.Accordion("๐Ÿ“Š Covariates (Optional)", open=False): covariate_cols = gr.Dropdown( label="Known Future Covariates", choices=[], multiselect=True ) past_covariate_cols = gr.Dropdown( label="Past-only Covariates", choices=[], multiselect=True ) Step 4.2: Batch Processing For multiple time series in one file: pythonwith gr.Accordion("๐Ÿ”„ Batch Mode", open=False): id_column = gr.Dropdown( label="Time Series ID Column (for batch)", choices=[] ) # Process each series independently Step 4.3: Export & Sharing pythonwith gr.Row(): export_config = gr.Button("๐Ÿ“‹ Export Configuration") share_link = gr.Button("๐Ÿ”— Generate Shareable Link") Step 4.4: Example Datasets pythongr.Examples( examples=[ ["examples/sample_univariate.csv", "timestamp", ["value"], ["Toto"], 24], ["examples/sample_multivariate.csv", "date", ["temp", "sales"], ["Toto", "Chronos-2"], 48], ], inputs=[file_upload, timestamp_col, target_cols, model_checkboxes, prediction_length] )


---

## **Phase 5: Deployment & Requirements**

### **Step 5.1: requirements.txt**

gradio>=4.0.0 pandas>=2.0.0 numpy>=1.24.0 plotly>=5.18.0 torch>=2.0.0 toto-ts>=0.1.0 chronos-forecasting>=1.0.0 autogluon.timeseries>=1.5.0 statsforecast>=1.7.0 scikit-learn>=1.3.0 openpyxl>=3.1.0 # For Excel support Step 5.2: README.md for HF Space yaml--- title: Time Series Forecasting Playground emoji: ๐Ÿ”ฎ colorFrom: blue colorTo: purple sdk: gradio sdk_version: 4.44.0 app_file: app.py pinned: true license: apache-2.0 suggested_hardware: t4-small # GPU for Toto

Step 5.3: Hardware Considerations

CPU Mode: Enable fallback for Toto/Chronos with warning about slower inference GPU Mode: Use T4 or better for reasonable inference speed Memory: Toto requires ~2-3GB VRAM, Chronos-2 similar

Summary: Implementation Order StepDescriptionPriority1Project structure + data processing utilsHigh2Base model interface + Toto wrapperHigh3Basic Gradio UI (upload + single model)High4Visualization (forecast plot)High5Metrics module + ground truth comparisonMedium6Model comparison visualizationMedium7Add Chronos-2 modelMedium8Add statistical baselinesLow9Covariate supportLow10Batch processingLow11Example datasets + documentationMedium

This plan gives your coder agent everything needed to build the Space incrementally, starting with Toto as the first model, then expanding to support model comparison and additional models like Chronos-2.