From 93d2c6941bb7b31eb079bbb0377dac3028ba0a6c Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Mon, 15 Dec 2025 15:16:55 -0500
Subject: [PATCH 01/14] feat: adapter for sktime forecasters
---
timecopilot/models/sktime.py | 88 ++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 timecopilot/models/sktime.py
diff --git a/timecopilot/models/sktime.py b/timecopilot/models/sktime.py
new file mode 100644
index 0000000..60c47c6
--- /dev/null
+++ b/timecopilot/models/sktime.py
@@ -0,0 +1,88 @@
+from typing import Any
+
+import numpy as np
+import pandas as pd
+
+from .utils.forecaster import Forecaster
+
+# from sktime.forecasting.base import BaseForecaster, ForecastingHorizon
+
+# NOTE: SKTime notes
+# https://www.sktime.net/en/stable/examples/01_forecasting.html#
+# https://www.sktime.net/en/stable/api_reference/auto_generated/sktime.forecasting.base.BaseForecaster.html
+# NOTE: forecaster setup args vary, that setup is currently being left to users
+# TODO: some forecasters require horizon be provided in fit() call, account for that
+# TODO: exogenous data support
+# TODO: different alias for different sktime forecasters
+# should this be required?
+
+
+class SKTimeAdapter(Forecaster):
+ """
+ Wrapper for SKTime Forecaster models for time series forecasting.
+
+
+ See the [official documentation](https://www.sktime.net/en/stable/api_reference/auto_generated/sktime.forecasting.base.BaseForecaster.html)
+ for more details.
+ """
+
+ def __init__(
+ self,
+ model,
+ # model: BaseForecaster,
+ alias: str = "SKTimeAdapter",
+ *args: Any,
+ **kwargs: Any,
+ ):
+ """
+ Args:
+ alias (str, optional): Custom name for the model instance.
+ Default is "SKTimeAdapter".
+ *args: Additional positional arguments passed to SKTimeAdapter.
+ **kwargs: Additional keyword arguments passed to SKTimeAdapter.
+ """
+ super().__init__(*args, **kwargs)
+ self.alias = alias
+ self.model = model
+
+ def forecast(
+ self,
+ df: pd.DataFrame,
+ h: int,
+ freq: str | None = None,
+ level: list[int | float] | None = None,
+ quantiles: list[float] | None = None,
+ ) -> pd.DataFrame:
+ # TODO: support for exogenous data
+ # TODO: add support for level for sktime models that can support it
+ # TODO: add support for quantiles for sktime models that can support it
+ if level is not None:
+ raise ValueError(
+ "Level and quantiles are not supported for adapted sktime models yet."
+ )
+ # NOTE: may not be needed
+ freq = self._maybe_infer_freq(df, freq)
+ forecast_horizon = np.arange(1, 1 + h)
+ id_col = "unique_id"
+ datetime_col = "ds"
+ y_col = "y"
+ df = df.copy()
+ df[datetime_col] = pd.to_datetime(df[datetime_col])
+ df = df.set_index([id_col, datetime_col])
+
+ # some sktime models require fh be passed in fit()
+ model = self.model
+ model.fit(y=df, fh=forecast_horizon)
+ # fh doesn't need to be passed in predict because it is being passed in fit
+ # if quantiles is not None:
+ # print("sktime quantile pred")
+ # fcst_df = model.predict_quantiles(
+ # alpha=qc.quantiles
+ # )
+ # fcst_df = fcst_df.reset_index()
+ # return fcst_df
+ fcst_df = model.predict()
+ fcst_df = fcst_df.reset_index()
+ # fcst_df = qc.maybe_convert_quantiles_to_level(fcst_df, models=[self.alias])
+ fcst_df.rename(columns={y_col: self.alias}, inplace=True)
+ return fcst_df
From f7663c4139b640386ceca7bb92e509779d8451c8 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Mon, 15 Dec 2025 15:18:06 -0500
Subject: [PATCH 02/14] docs: example notebook for sktime
---
docs/examples/sktime.ipynb | 416 +++++++++++++++++++++++++++++++++++++
1 file changed, 416 insertions(+)
create mode 100644 docs/examples/sktime.ipynb
diff --git a/docs/examples/sktime.ipynb b/docs/examples/sktime.ipynb
new file mode 100644
index 0000000..02f4f61
--- /dev/null
+++ b/docs/examples/sktime.ipynb
@@ -0,0 +1,416 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "e890f3b3",
+ "metadata": {},
+ "source": [
+ "# Using sktime models\n",
+ "\n",
+ "This is an example for using sktime based models with the `timecopilot` library."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6140af37",
+ "metadata": {},
+ "source": [
+ "## imports"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "f5c85b29",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import nest_asyncio\n",
+ "nest_asyncio.apply()\n",
+ "\n",
+ "import timecopilot\n",
+ "import sktime as skt\n",
+ "import pandas as pd"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "694b7854",
+ "metadata": {},
+ "source": [
+ "## Setup the sktime model and adapt it to TimeCopilot\n",
+ "\n",
+ "sktime models need to be adapted to work properly with TimeCopilot. This is done by creating your model with sktime and passing it through SKTimeAdapter. Some sktime models may require more configuration to function properly with the data you intend to use it on. For example, when using sktime's NaiveForecaster with yearly data you might want to initialize it with an `sp` argument of `12` like this `NaiveForecaster(sp=12)`.\n",
+ "\n",
+ "The `Alias` argument should also be provided, especially if you plan on adding multiple sktime forecasters. If you add multiple sktime models without specifying aliases, TimeCopilot will not be able to properly call all of them."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "e1870fe0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from sktime.forecasting.trend import TrendForecaster\n",
+ "from timecopilot.models.sktime import SKTimeAdapter\n",
+ "\n",
+ "trend_forecaster = TrendForecaster()\n",
+ "\n",
+ "adapted_model = SKTimeAdapter(\n",
+ " model=trend_forecaster,\n",
+ " alias=\"TrendForecaster\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "37419dd4",
+ "metadata": {},
+ "source": [
+ "## Create a TimeCopilot instance with your sktime model\n",
+ "\n",
+ "You will need to specify the forecasters you're using when using sktime models. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "f793b038",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tc = timecopilot.TimeCopilot(\n",
+ " llm=\"openai:gpt-4o\",\n",
+ " forecasters=[\n",
+ " adapted_model,\n",
+ " ],\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "401d5b6f",
+ "metadata": {},
+ "source": [
+ "### Extending default model list with an sktime adapted model\n",
+ "\n",
+ "if you want to use the default list with the addition of your sktime model you could make a copy of the default list and append your model to it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "76505216",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model_list = timecopilot.agent.DEFAULT_MODELS.copy()\n",
+ "model_list.append(adapted_model)\n",
+ "\n",
+ "tc = timecopilot.TimeCopilot(\n",
+ " llm=\"openai:gpt-4o\",\n",
+ " forecasters=model_list\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2d6f09e4",
+ "metadata": {},
+ "source": [
+ "## Forecasting \n",
+ "Once that setup is complete, you can use TimeCopilot with your adapted sktime model the same way you'd normally use TimeCopilot"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "e9122229",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df = pd.read_csv(\"https://timecopilot.s3.amazonaws.com/public/data/air_passengers.csv\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "f95e1578",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "1it [00:00, 4.70it/s]\n",
+ "1it [00:00, 223.32it/s]\n",
+ "11it [00:00, 77.11it/s]\n"
+ ]
+ }
+ ],
+ "source": [
+ "result = tc.forecast(\n",
+ " df=df,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "7355c143",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The 'AirPassengers' time series has a series length of 144 with a clear seasonal pattern identified using key features. The high 'seasonal_strength' of 0.981 suggests strong seasonality, evident from the 12-month seasonal period. The time series also exhibits trends, shown by a 'trend' score of 0.997, and moderate curvature at 1.069. The high autocorrelation 'x_acf1' at 0.948 indicates the persistence of patterns over time. The Holt-Winters parameters suggest a stable level (alpha ~1) with no trend component (beta ~0) and significant seasonal smoothing (gamma ~0.75). These features suggest that both trend and seasonality are prominent and need to be captured by the model.\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(result.output.tsfeatures_analysis)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "86acfa60",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " unique_id | \n",
+ " ds | \n",
+ " TrendForecaster | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " AirPassengers | \n",
+ " 1961-01-01 | \n",
+ " 473.023018 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " AirPassengers | \n",
+ " 1961-02-01 | \n",
+ " 475.729097 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " AirPassengers | \n",
+ " 1961-03-01 | \n",
+ " 478.173296 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " AirPassengers | \n",
+ " 1961-04-01 | \n",
+ " 480.879374 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " AirPassengers | \n",
+ " 1961-05-01 | \n",
+ " 483.498159 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " AirPassengers | \n",
+ " 1961-06-01 | \n",
+ " 486.204237 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " AirPassengers | \n",
+ " 1961-07-01 | \n",
+ " 488.823023 | \n",
+ "
\n",
+ " \n",
+ " | 7 | \n",
+ " AirPassengers | \n",
+ " 1961-08-01 | \n",
+ " 491.529101 | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " AirPassengers | \n",
+ " 1961-09-01 | \n",
+ " 494.235179 | \n",
+ "
\n",
+ " \n",
+ " | 9 | \n",
+ " AirPassengers | \n",
+ " 1961-10-01 | \n",
+ " 496.853964 | \n",
+ "
\n",
+ " \n",
+ " | 10 | \n",
+ " AirPassengers | \n",
+ " 1961-11-01 | \n",
+ " 499.560042 | \n",
+ "
\n",
+ " \n",
+ " | 11 | \n",
+ " AirPassengers | \n",
+ " 1961-12-01 | \n",
+ " 502.178827 | \n",
+ "
\n",
+ " \n",
+ " | 12 | \n",
+ " AirPassengers | \n",
+ " 1962-01-01 | \n",
+ " 504.884906 | \n",
+ "
\n",
+ " \n",
+ " | 13 | \n",
+ " AirPassengers | \n",
+ " 1962-02-01 | \n",
+ " 507.590984 | \n",
+ "
\n",
+ " \n",
+ " | 14 | \n",
+ " AirPassengers | \n",
+ " 1962-03-01 | \n",
+ " 510.035183 | \n",
+ "
\n",
+ " \n",
+ " | 15 | \n",
+ " AirPassengers | \n",
+ " 1962-04-01 | \n",
+ " 512.741261 | \n",
+ "
\n",
+ " \n",
+ " | 16 | \n",
+ " AirPassengers | \n",
+ " 1962-05-01 | \n",
+ " 515.360046 | \n",
+ "
\n",
+ " \n",
+ " | 17 | \n",
+ " AirPassengers | \n",
+ " 1962-06-01 | \n",
+ " 518.066125 | \n",
+ "
\n",
+ " \n",
+ " | 18 | \n",
+ " AirPassengers | \n",
+ " 1962-07-01 | \n",
+ " 520.684910 | \n",
+ "
\n",
+ " \n",
+ " | 19 | \n",
+ " AirPassengers | \n",
+ " 1962-08-01 | \n",
+ " 523.390988 | \n",
+ "
\n",
+ " \n",
+ " | 20 | \n",
+ " AirPassengers | \n",
+ " 1962-09-01 | \n",
+ " 526.097066 | \n",
+ "
\n",
+ " \n",
+ " | 21 | \n",
+ " AirPassengers | \n",
+ " 1962-10-01 | \n",
+ " 528.715851 | \n",
+ "
\n",
+ " \n",
+ " | 22 | \n",
+ " AirPassengers | \n",
+ " 1962-11-01 | \n",
+ " 531.421929 | \n",
+ "
\n",
+ " \n",
+ " | 23 | \n",
+ " AirPassengers | \n",
+ " 1962-12-01 | \n",
+ " 534.040714 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " unique_id ds TrendForecaster\n",
+ "0 AirPassengers 1961-01-01 473.023018\n",
+ "1 AirPassengers 1961-02-01 475.729097\n",
+ "2 AirPassengers 1961-03-01 478.173296\n",
+ "3 AirPassengers 1961-04-01 480.879374\n",
+ "4 AirPassengers 1961-05-01 483.498159\n",
+ "5 AirPassengers 1961-06-01 486.204237\n",
+ "6 AirPassengers 1961-07-01 488.823023\n",
+ "7 AirPassengers 1961-08-01 491.529101\n",
+ "8 AirPassengers 1961-09-01 494.235179\n",
+ "9 AirPassengers 1961-10-01 496.853964\n",
+ "10 AirPassengers 1961-11-01 499.560042\n",
+ "11 AirPassengers 1961-12-01 502.178827\n",
+ "12 AirPassengers 1962-01-01 504.884906\n",
+ "13 AirPassengers 1962-02-01 507.590984\n",
+ "14 AirPassengers 1962-03-01 510.035183\n",
+ "15 AirPassengers 1962-04-01 512.741261\n",
+ "16 AirPassengers 1962-05-01 515.360046\n",
+ "17 AirPassengers 1962-06-01 518.066125\n",
+ "18 AirPassengers 1962-07-01 520.684910\n",
+ "19 AirPassengers 1962-08-01 523.390988\n",
+ "20 AirPassengers 1962-09-01 526.097066\n",
+ "21 AirPassengers 1962-10-01 528.715851\n",
+ "22 AirPassengers 1962-11-01 531.421929\n",
+ "23 AirPassengers 1962-12-01 534.040714"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "result.fcst_df"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "timecopilot",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.14"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
From 3c33446a8a1005b95953f1330beab1cc646b5927 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Mon, 15 Dec 2025 16:38:28 -0500
Subject: [PATCH 03/14] docs: sktime adapter docstrings
---
timecopilot/models/sktime.py | 51 ++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/timecopilot/models/sktime.py b/timecopilot/models/sktime.py
index 60c47c6..9ad8f35 100644
--- a/timecopilot/models/sktime.py
+++ b/timecopilot/models/sktime.py
@@ -36,6 +36,7 @@ def __init__(
):
"""
Args:
+ model (sktime.forecasting.base.BaseForecaster): sktime forecasting model
alias (str, optional): Custom name for the model instance.
Default is "SKTimeAdapter".
*args: Additional positional arguments passed to SKTimeAdapter.
@@ -53,6 +54,56 @@ def forecast(
level: list[int | float] | None = None,
quantiles: list[float] | None = None,
) -> pd.DataFrame:
+ """
+ Generate forecasts for time series data using an sktime model.
+
+ This method produces point forecasts and, optionally, prediction
+ intervals or quantile forecasts. The input DataFrame can contain one
+ or multiple time series in stacked (long) format.
+
+ Prediction intervals and quantile forecasts are not currently supported
+ with sktime based models
+
+ Args:
+ df (pd.DataFrame):
+ DataFrame containing the time series to forecast. It must
+ include as columns:
+
+ - "unique_id": an ID column to distinguish multiple series.
+ - "ds": a time column indicating timestamps or periods.
+ - "y": a target column with the observed values.
+
+ h (int):
+ Forecast horizon specifying how many future steps to predict.
+ freq (str, optional):
+ Frequency of the time series (e.g. "D" for daily, "M" for
+ monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
+ pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
+ valid values. If not provided, the frequency will be inferred
+ from the data.
+ level (list[int | float], optional):
+ Confidence levels for prediction intervals, expressed as
+ percentages (e.g. [80, 95]). If provided, the returned
+ DataFrame will include lower and upper interval columns for
+ each specified level.
+ quantiles (list[float], optional):
+ List of quantiles to forecast, expressed as floats between 0
+ and 1. Should not be used simultaneously with `level`. When
+ provided, the output DataFrame will contain additional columns
+ named in the format "model-q-{percentile}", where {percentile}
+ = 100 × quantile value.
+
+ Returns:
+ pd.DataFrame:
+ DataFrame containing forecast results. Includes:
+
+ - point forecasts for each timestamp and series.
+ - prediction intervals if `level` is specified.
+ - quantile forecasts if `quantiles` is specified.
+
+ For multi-series data, the output retains the same unique
+ identifiers as the input DataFrame.
+ """
# TODO: support for exogenous data
# TODO: add support for level for sktime models that can support it
# TODO: add support for quantiles for sktime models that can support it
From fd40715c3d36dbbad872cdb36b5d547f2cfcf795 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Tue, 16 Dec 2025 09:49:52 -0500
Subject: [PATCH 04/14] feat: sktime adapter alias auto default
by default retrieve alias in sktime adapter from model type name
---
timecopilot/models/sktime.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/timecopilot/models/sktime.py b/timecopilot/models/sktime.py
index 9ad8f35..4e8a2f7 100644
--- a/timecopilot/models/sktime.py
+++ b/timecopilot/models/sktime.py
@@ -30,7 +30,7 @@ def __init__(
self,
model,
# model: BaseForecaster,
- alias: str = "SKTimeAdapter",
+ alias: str | None = None,
*args: Any,
**kwargs: Any,
):
@@ -38,12 +38,12 @@ def __init__(
Args:
model (sktime.forecasting.base.BaseForecaster): sktime forecasting model
alias (str, optional): Custom name for the model instance.
- Default is "SKTimeAdapter".
+ By default alias is retrieved from the type name of model.
*args: Additional positional arguments passed to SKTimeAdapter.
**kwargs: Additional keyword arguments passed to SKTimeAdapter.
"""
super().__init__(*args, **kwargs)
- self.alias = alias
+ self.alias = alias if alias is not None else type(model).__name__
self.model = model
def forecast(
From 3b1951389c16fa742d2fd19a4e026f6642512434 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Tue, 16 Dec 2025 09:54:55 -0500
Subject: [PATCH 05/14] docs: sktime nb added in mkdocs
---
mkdocs.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/mkdocs.yml b/mkdocs.yml
index d76eeef..bc346d6 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -28,6 +28,7 @@ nav:
- examples/gift-eval.ipynb
- examples/chronos-family.ipynb
- examples/cryptocurrency-quickstart.ipynb
+ - examples/sktime.ipynb
- Experiments:
- experiments/gift-eval.md
- experiments/fev.md
From 09e9e570d0f88081457f754f93ed743eee04612a Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Tue, 16 Dec 2025 10:08:25 -0500
Subject: [PATCH 06/14] refactor: move sktime adapter to adapters directory
---
timecopilot/models/{ => adapters}/sktime.py | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename timecopilot/models/{ => adapters}/sktime.py (100%)
diff --git a/timecopilot/models/sktime.py b/timecopilot/models/adapters/sktime.py
similarity index 100%
rename from timecopilot/models/sktime.py
rename to timecopilot/models/adapters/sktime.py
From 4c8124fece582099645cdfbbc405b543bd7a4a14 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Tue, 16 Dec 2025 10:09:51 -0500
Subject: [PATCH 07/14] docs: update sktime example
modify sktime adapter import in notebook example to reflect move to adapters directory
---
docs/examples/sktime.ipynb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/examples/sktime.ipynb b/docs/examples/sktime.ipynb
index 02f4f61..276e40b 100644
--- a/docs/examples/sktime.ipynb
+++ b/docs/examples/sktime.ipynb
@@ -47,13 +47,13 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"id": "e1870fe0",
"metadata": {},
"outputs": [],
"source": [
"from sktime.forecasting.trend import TrendForecaster\n",
- "from timecopilot.models.sktime import SKTimeAdapter\n",
+ "from timecopilot.models.adapters.sktime import SKTimeAdapter\n",
"\n",
"trend_forecaster = TrendForecaster()\n",
"\n",
From 4e262ea2c32d9646ef7d58e7a1da98b0420ac4cf Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Tue, 16 Dec 2025 12:51:35 -0500
Subject: [PATCH 08/14] docs: add adapters to api reference
---
docs/api/models/adapters/adapters.md | 8 ++++++++
mkdocs.yml | 1 +
timecopilot/models/adapters/__init__.py | 0
3 files changed, 9 insertions(+)
create mode 100644 docs/api/models/adapters/adapters.md
create mode 100644 timecopilot/models/adapters/__init__.py
diff --git a/docs/api/models/adapters/adapters.md b/docs/api/models/adapters/adapters.md
new file mode 100644
index 0000000..c56efa2
--- /dev/null
+++ b/docs/api/models/adapters/adapters.md
@@ -0,0 +1,8 @@
+# `timecopilot.models.adapters`
+
+
+::: timecopilot.models.adapters.sktime
+ options:
+ members:
+ - SKTimeAdapter
+
diff --git a/mkdocs.yml b/mkdocs.yml
index bc346d6..11da8ce 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -44,6 +44,7 @@ nav:
- api/models/ml.md
- api/models/neural.md
- api/models/ensembles.md
+ - api/models/adapters/adapters.md
- api/models/utils/forecaster.md
- api/gift-eval/gift-eval.md
- Changelogs:
diff --git a/timecopilot/models/adapters/__init__.py b/timecopilot/models/adapters/__init__.py
new file mode 100644
index 0000000..e69de29
From 7dcd7280657f494f7c97ab73010d25dc9eba6d5c Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Tue, 16 Dec 2025 14:59:27 -0500
Subject: [PATCH 09/14] docs: add example to sktime forecast docstring
---
timecopilot/models/adapters/sktime.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/timecopilot/models/adapters/sktime.py b/timecopilot/models/adapters/sktime.py
index 4e8a2f7..4155eac 100644
--- a/timecopilot/models/adapters/sktime.py
+++ b/timecopilot/models/adapters/sktime.py
@@ -54,6 +54,7 @@ def forecast(
level: list[int | float] | None = None,
quantiles: list[float] | None = None,
) -> pd.DataFrame:
+ # fmt: off
"""
Generate forecasts for time series data using an sktime model.
@@ -103,7 +104,22 @@ def forecast(
For multi-series data, the output retains the same unique
identifiers as the input DataFrame.
+
+ Example:
+ ```python
+ import pandas as pd
+ from timecopilot import TimeCopilot
+ from timecopilot.models.adapters.sktime import SKTimeAdapter
+ from sktime.forecasting.trends import TrendForecaster
+
+ df = pd.read_csv("https://timecopilot.s3.amazonaws.com/public/data/air_passengers.csv")
+ adapted_skt_model = SKTimeAdapter(TrendForecaster())
+ tc = TimeCopilot(llm="openai:gpt-4o", models=[adapted_skt_model])
+ result = tc.forecast(df, h=12, freq="MS")
+ print(result.output)
+ ```
"""
+ # fmt: on
# TODO: support for exogenous data
# TODO: add support for level for sktime models that can support it
# TODO: add support for quantiles for sktime models that can support it
From 4b8c00dcaf0a8bd42e635fa9bfc9ad66414f85cd Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Wed, 17 Dec 2025 11:42:16 -0500
Subject: [PATCH 10/14] fix: fix import error in sktime adapter
error was caused by moving sktime adapter into adapters directory without updating relative import
---
timecopilot/models/adapters/sktime.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/timecopilot/models/adapters/sktime.py b/timecopilot/models/adapters/sktime.py
index 4155eac..b5e4b62 100644
--- a/timecopilot/models/adapters/sktime.py
+++ b/timecopilot/models/adapters/sktime.py
@@ -3,7 +3,7 @@
import numpy as np
import pandas as pd
-from .utils.forecaster import Forecaster
+from ..utils.forecaster import Forecaster
# from sktime.forecasting.base import BaseForecaster, ForecastingHorizon
From e2a641727d73795a1473e115f58d141843156dd4 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Wed, 17 Dec 2025 15:03:07 -0500
Subject: [PATCH 11/14] ci: add sktime as dev dependency
---
pyproject.toml | 1 +
uv.lock | 34 +++++++++++++++++++++++++++++++++-
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 71a4d60..0e5769f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,6 +13,7 @@ dev = [
"pytest-rerunfailures>=15.1",
"pytest-xdist>=3.8.0",
"s3fs>=2025.3.0",
+ "sktime>=0.40.1",
]
docs = [
"mkdocs-include-markdown-plugin>=7.1.6",
diff --git a/uv.lock b/uv.lock
index f4b177c..39bbcae 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 2
+revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'linux'",
@@ -6585,6 +6585,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878, upload-time = "2025-02-26T09:15:14.99Z" },
]
+[[package]]
+name = "scikit-base"
+version = "0.13.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6b/4d/53b477e05ba7897026133543114c48c9e67646180b85a9e3bd6edb6e5223/scikit_base-0.13.0.tar.gz", hash = "sha256:436c26067173a7235c465c64ac105008f6f889f09308c5e4e4a16e21c38c3ddc", size = 127431, upload-time = "2025-10-08T20:39:35.509Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/1c/7c00d3823bef4ef2e5a95507475bc5cf3bdabf00c8577f8bb5ff406e9f2e/scikit_base-0.13.0-py3-none-any.whl", hash = "sha256:efe7df0f09854b9f28e9532f6be87ef433dd8f1358cabd24ebf168aa4ccca1fc", size = 151529, upload-time = "2025-10-08T20:39:33.951Z" },
+]
+
[[package]]
name = "scikit-learn"
version = "1.6.1"
@@ -6815,6 +6824,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
+[[package]]
+name = "sktime"
+version = "0.40.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "joblib" },
+ { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
+ { name = "packaging" },
+ { name = "pandas", version = "2.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
+ { name = "scikit-base" },
+ { name = "scikit-learn", version = "1.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
+ { name = "scipy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/84/7a/22b99a00a7cabaa34c350f78f33ae89711cfc08deef06043daee3835dbb3/sktime-0.40.1.tar.gz", hash = "sha256:bbf3c2971c7ad388fbebc1d1fb573e20f730e6486c00935b336d8edf13bca04d", size = 35197759, upload-time = "2025-11-25T00:03:38.744Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/0d/3b2e5b51593009f72fb7fb6956e6c652da1450c8c5484c40a276113c2114/sktime-0.40.1-py3-none-any.whl", hash = "sha256:130bb0c39ca4377e491c4dfa64c8ac67c3de2660dd406dd7690db6a8ac349c4e", size = 36265753, upload-time = "2025-11-25T00:03:34.522Z" },
+]
+
[[package]]
name = "smmap"
version = "5.0.2"
@@ -7304,6 +7334,7 @@ dev = [
{ name = "pytest-rerunfailures" },
{ name = "pytest-xdist" },
{ name = "s3fs" },
+ { name = "sktime" },
]
docs = [
{ name = "mkdocs" },
@@ -7356,6 +7387,7 @@ dev = [
{ name = "pytest-rerunfailures", specifier = ">=15.1" },
{ name = "pytest-xdist", specifier = ">=3.8.0" },
{ name = "s3fs", specifier = ">=2025.3.0" },
+ { name = "sktime", specifier = ">=0.40.1" },
]
docs = [
{ name = "mkdocs", specifier = ">=1.6.1" },
From f3d5498925c9d344b6416eec8d2037e5153b5331 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Fri, 19 Dec 2025 16:58:06 -0500
Subject: [PATCH 12/14] docs: contributing guide - new models note
---
docs/contributing.md | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/contributing.md b/docs/contributing.md
index 7f7fc23..9099ba0 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -77,6 +77,14 @@ All documentation files should use **kebab-case** (e.g., `model-hub.md`, `foreca
For further reference, see the [Google Developer Documentation Style Guide on file names](https://developers.google.com/style/filenames).
+### Adding Models & Adapters
+
+In order for new models and adapters to show up in TimeCopilot's documentation properly there are a couple steps to take:
+
+- Add the model/adapter in its own file or the appropriate file in `docs/api/models` or the appropriate subdirectory. For adapters, it may also be good to create an example notebook in `docs/examples` and add it to the `Examples` section of [mkdocs.yml](https://github.com/TimeCopilot/timecopilot/blob/main/mkdocs.yml).
+ - when creating a new file in `docs/api/models` for this step, add that file in the API Reference section of [mkdocs.yml](https://github.com/TimeCopilot/timecopilot/blob/main/mkdocs.yml)
+- A docstring should be present for the new model/adapter with an example in the `forecast()` method in a similar style to the [TimeCopilot agent query method](https://timecopilot.dev/api/agent/#timecopilot.agent.TimeCopilot.query). Note: when adding an adapter with its own dependencies you may need to add those dependencies as dev dependencies with `uv add new_dep --group dev`.
+
## Adding New Datasets
The datasets utilized in our documentation are hosted on AWS at `https://timecopilot.s3.amazonaws.com/public/data/`. If you wish to contribute additional datasets for your changes, please contact [@AzulGarza](http://github.com/AzulGarza) for guidance.
@@ -115,4 +123,4 @@ TimeCopilot uses some forked Python packages, maintained under custom names on P
- **uni2ts**:
- Forked from: [SalesforceAIResearch/uni2ts](https://github.com/SalesforceAIResearch/uni2ts)
- TimeCopilot fork: [AzulGarza/uni2ts](https://github.com/AzulGarza/uni2ts)
- - Published on PyPI as: [`timecopilot-uni2ts`](https://pypi.org/project/timecopilot-uni2ts/)
\ No newline at end of file
+ - Published on PyPI as: [`timecopilot-uni2ts`](https://pypi.org/project/timecopilot-uni2ts/)
From 42ddc41337f74f3f4b087545330d2431d0da5f89 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Fri, 19 Dec 2025 18:11:35 -0500
Subject: [PATCH 13/14] docs: sktime docstring example fix
---
timecopilot/models/adapters/sktime.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/timecopilot/models/adapters/sktime.py b/timecopilot/models/adapters/sktime.py
index b5e4b62..7b84d55 100644
--- a/timecopilot/models/adapters/sktime.py
+++ b/timecopilot/models/adapters/sktime.py
@@ -110,7 +110,7 @@ def forecast(
import pandas as pd
from timecopilot import TimeCopilot
from timecopilot.models.adapters.sktime import SKTimeAdapter
- from sktime.forecasting.trends import TrendForecaster
+ from sktime.forecasting.trend import TrendForecaster
df = pd.read_csv("https://timecopilot.s3.amazonaws.com/public/data/air_passengers.csv")
adapted_skt_model = SKTimeAdapter(TrendForecaster())
From 77a82a63cb1cf0e46f0d2699d77305e111820414 Mon Sep 17 00:00:00 2001
From: spolisar <22416070+spolisar@users.noreply.github.com>
Date: Mon, 5 Jan 2026 18:00:23 -0500
Subject: [PATCH 14/14] docs: fix sktime adapter example
---
timecopilot/models/adapters/sktime.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/timecopilot/models/adapters/sktime.py b/timecopilot/models/adapters/sktime.py
index 7b84d55..a258226 100644
--- a/timecopilot/models/adapters/sktime.py
+++ b/timecopilot/models/adapters/sktime.py
@@ -114,7 +114,7 @@ def forecast(
df = pd.read_csv("https://timecopilot.s3.amazonaws.com/public/data/air_passengers.csv")
adapted_skt_model = SKTimeAdapter(TrendForecaster())
- tc = TimeCopilot(llm="openai:gpt-4o", models=[adapted_skt_model])
+ tc = TimeCopilot(llm="openai:gpt-4o", forecasters=[adapted_skt_model])
result = tc.forecast(df, h=12, freq="MS")
print(result.output)
```