Changelog¶
All significant changes to this project are documented in this release file.
| Legend | |
|---|---|
| Feature | New feature |
| Enhancement | Improvement in existing functionality |
| API Change | Changes in the API |
| Fix | Bug fix |
| Docs | Documentation improvement |
0.4.2 Oct 12, 2026¶
The main changes in this release are:
- Fix
ForecastingAssistant.compare()withoutcandidates, on the strategy of a refined plan, ranks that plan as the row'Strategy plan', and its summary says how many configurations beat the baseline (#71).
Before upgrading
Some results change with this version, without any change in your code:
compare()withoutcandidates, on aCVResultcreated for a plan that is none of the candidates of the profile, has one more row,'Strategy plan', which runs that plan. Passcandidatesto choose the rows.compare(): the sentence ofexplanationabout the baseline counts the configurations that beat it instead of those that do not, so code that parses that text must be updated.
Added
- New field
strategy_plan_nameofComparisonResult: the row of the comparison that runs the plan the strategy ofcreate_cv()was created for, or None (#71).
Changed
-
The first start of the MCP server fits in the 30 seconds Claude Code waits: the server connects before loading pandas, scikit-learn and skforecast, which it loads in the background (7 seconds instead of 39 on macOS with an empty
uvxcache; the first tool waits for that loading, about 30 seconds the first time).import skforecast_aino longer loads them either. See MCP server for coding agents (#72). -
Dependencies: statsmodels 0.15 is allowed (
statsmodels>=0.13.2,<0.16, as in skforecast), the minimum pandas is 2.2, the one skforecast 0.26 already required, and numpy, scikit-learn and scipy are declared as direct dependencies, with the minimum versions of skforecast.
Fixed
-
CSV files separated by
;or tabulators, with,as decimal mark (what a spreadsheet exports in most of Europe), failed with a target column not found. The Python API, the CLI and the MCP server now detect the separator and the decimal mark (or take them assepanddecimalofForecastingAssistant.profile()), the generated script reads the file with them, and the MCP server also accepts.tsvand.csv.gzfiles. See the CLI guide (#74). -
ForecastingAssistant.compare()withoutcandidates, on the strategy of a plan refined with another estimator, lags or window features, did not run that plan. It now runs as the row'Strategy plan', and the summary names that row and counts the configurations that beat the baseline, not those that do not (#71). -
ForecastingAssistant.compare(): when the two best configurations have the same value of the ranking metric, the summary says they are tied instead of "0.0% ahead" (#71). -
The
costofcreate_cvin the MCP server, for acomparewithoutcandidates, counted forecasters thatcomparedoes not run (the multivariate one with several series) and only one of the estimators it compares when a single forecaster is left. It now counts the candidates thatcompareruns, the plan of the strategy included. See MCP server for coding agents (#71). -
pip install "skforecast-ai[llm]"did not install the Groq client, so agroq:model failed with anImportErrorunless thegroqextra was installed too. Thellmextra now includes it, as the installation guide says.
0.4.1 Oct 9, 2026¶
The main changes in this release are:
- Enhancement New option
--allow-project-dirofskforecast-ai mcp, which the plugin of Claude Code now uses: the server reads the project of the session from the environment and does not start in the home directory. See MCP server for coding agents.
Added
- New option
--allow-project-dirofskforecast-ai mcp, in place of--allow-dir: the allowed directory is the project of the session, read from the environment variableCLAUDE_PROJECT_DIRthat Claude Code sets. The server stops if it is not set, or if it is the home directory or the root of a disk. See MCP server for coding agents (#61).
Changed
- The plugin of Claude Code starts the server with
uvx skforecast-ai-mcp==<version> --allow-project-dir. It reads the same project as before, but no longer starts when Claude Code is opened in the home directory or in the root of a disk: open it in the folder of the project (#61).
Fixed
0.4.0 Oct 9, 2026¶
The main changes in this release are:
-
Feature New MCP server, so coding agents (Claude Code, Cursor and other MCP clients) can run the deterministic workflow as tools: profile a CSV file, plan, backtest, compare and forecast, each result with the script that produced it. It installs as a Claude Code plugin or with the new
mcpextra. See MCP server for coding agents (#35, #36) -
Feature New keyword-only arguments in
ForecastingAssistant.plan()and the methods that build a plan choose a decision instead of the rules (metric,use_exog,differentiation,calendar_features,target_transformer,dropna_from_series), andexog_columnsinprofile()chooses the exogenous columns. The CLI takes the same options. (#41) -
Feature
ForecastingAssistant.compare()adds a seasonal naive baseline (ForecasterEquivalentDate) to the leaderboard and says whether the best configuration beats it.'ForecasterEquivalentDate'can also be chosen asforecaster. (#27) -
Feature
ForecasterFoundationforecasts several series and follows what skforecast declares for the chosen model (context length, exogenous variables, quantiles) instead of assuming Chronos-2. The newfoundationextra installs the backend of Chronos-2. (#29) -
Feature Every error derives from the new
SkforecastAIError, with a stablecode, the argument at fault infieldand, for some, a remedy inhint, so a program can react to it without parsing the message. See Exceptions and warnings (#32) -
API Change
ForecastingAssistant.create_cv()trains the forecaster once by default (refit=False) instead of refitting it in every fold, so the metrics ofbacktest()andcompare()with the default strategy change. Passrefit=Trueto keep the previous behavior. (#33) -
API Change Arguments and plans that were ignored, or that failed inside the generated script, now raise before anything runs: invalid arguments of
plan(), aForecastPlanedited by hand or loaded from JSON that the scripts do not support, and a plan or a profile that does not match the data. (#31, #33, #37) -
API Change skforecast-ai now requires
skforecast>=0.26.0, andlightgbmis a dependency. -
Fix
forecast()andbacktest()check the data before running (dates, missing values, future exogenous variables) and raiseInvalidInputErrornaming the rows, where they failed inside the script or returned missing or shifted predictions without an error. (#32, #37, #43, #47) -
Fix A crafted value in a plan or a profile loaded from JSON, or in a column name of a CSV, could inject code into the generated script that
forecast()andbacktest()execute. No value can run as code any longer. (#31)
Before upgrading
Some results change with this version, without any change in your code:
create_cv(),backtest()andcompare(): the default strategy trains the forecaster once instead of refitting it in every fold, so the metrics change. Passrefit=Trueto keep the previous behavior.compare(): the leaderboard has one more row, the baseline (baseline=Falseleaves it out), and with several series it comparesForecasterRecursiveMultiSeriesagainstForecasterFoundationby default (passcandidatesto compare the estimators as before).ForecasterStats(Auto-ARIMA): predictions and metrics change for quarterly data and for multiplied frequencies such as'2MS'or'3h', which now get a seasonal period.backtest()andcompare()with anintervalon several series: the bounds change, because they are now estimated by bootstrapping, as inforecast().forecast()with aForecasterFoundationplan and anintervalreturns thepred,lower_boundandupper_boundcolumns instead of oneq_*column per quantile.ForecasterDirectMultiVariateplans that do not use the exogenous columns: predictions change, because those columns are no longer fitted as series.profile(): rows that are not in date order and day-first text dates are read as the generated script reads them, which can change the lags and the predictions.
And some code needs to be updated: arguments that were ignored now raise, the estimator of a ForecasterFoundation plan is the Hugging Face model ID, forecast() in evaluation mode needs a test set of exactly steps observations, the result keyword of ask() is removed and the minimum version of skforecast is 0.26.0. See Changed.
Added
Plans and overrides
-
New keyword-only arguments
metric,use_exog,differentiation,calendar_features,target_transformeranddropna_from_seriesinForecastingAssistant.plan(),refine_plan(),forecast(),backtest()and their*_code()methods choose a decision instead of the rules; all butmetricare also keys of the candidates ofcompare(). A value the data or the forecaster cannot apply raisesValueError.backtest()andbacktest_code()also takelagsandwindow_features. (#41) -
New keyword-only
exog_columnsargument inForecastingAssistant.profile()chooses the exogenous columns (an empty list for none) instead of using every column that is not the target, the date or the series id. The others are listed in the newDataProfile.unused_columns, and neither the plan nor the script uses them. (#41) -
ForecastPlan.overridden_fieldsnames the decisions you made instead of the rules, andForecastPlan.warningsholds the text of the warnings thatForecastingAssistant.plan()emits, so both travel with the plan, its JSON and the results built from it.refine_plan()warns with the newPlanEditsDiscardedWarningwhen it does not keep values edited by hand in the plan. (#34, #41) -
'ForecasterEquivalentDate'can be chosen asforecasterinForecastingAssistant.plan(), every method that builds a plan and the CLI--forecaster. Its plan has the task type'baseline', anoffsetchosen as for the baseline ofcompare(), no estimator or features, and conformal prediction intervals. (#27, #37)
Backtesting and comparison
-
ForecastingAssistant.compare()adds a baseline to the leaderboard: a seasonal naiveForecasterEquivalentDatethat repeats the value observed one seasonal period earlier, or the last observed value when the data has no usable seasonality. Its row is identified byComparisonResult.baseline_name, and the explanation says whether the best configuration beats it. It is not added in some cases, such as multi-series data or a target with missing values. Passbaseline=False(--no-baselinein the CLI) to keep the previous output. (#27) -
The cross-validation explanation of
ForecastingAssistant.create_cv(),backtest()andcompare()states the cost of the backtest: how many times the forecaster is trained (cv_config['n_fits']) or, forForecasterFoundation, how many inference windows it runs (cv_config['inference_windows']).backtest()andcompare()warn with skforecast'sLongTrainingWarningabove 50 estimator fits or 2000 windows, andcompare()withoutcandidatesleaves out the alternatives above 500 fits; pass them incandidatesto run them anyway. (#33, #43) -
The result of
ForecastingAssistant.create_cv()says where each value of the strategy comes from:overridden_fields,fields_without_effect,llm_configuredanddefaults_explanation.backtest()andcompare()carry the same undercv_names when they receive that result. (#45) -
ForecastingAssistant.create_cv()warns when a plan with prediction intervals estimated from residuals gets a strategy whose first training window leaves fewer than 100 rows to train on, which makes the intervals too narrow. Its error for a strategy with fewer than 2 folds carries ahintwith the ways out. (#47) -
ForecastingAssistant.compare()accepts aprogress_callback, called with aCompareProgresswhen each candidate starts and ends, to report progress outside a notebook. An exception it raises stops the comparison before the next candidate, so it can also cancel a long comparison. (#34)
Foundation models
-
ForecasterFoundationforecasts several series, in wide and long format.backtest()reports the metrics per series and aggregated,ForecastingAssistant.compare()ranks it together withForecasterRecursiveMultiSerieson the average across series, and Chronos-2 can share information across series withestimator_kwargs={'cross_learning': True}. (#29) -
New
foundationextra (pip install "skforecast-ai[foundation]") with the backend of Chronos-2, the default foundation model. When it is not installed,ForecastingAssistant.compare()withoutcandidatesleavesForecasterFoundationout with the newMissingBackendWarninginstead of running a candidate that fails. (#29) -
ForecasterFoundationplans follow what skforecast declares for the chosen model instead of assuming Chronos-2: its defaultcontext_length, whether it accepts exogenous variables (and categorical ones) and the quantiles it predicts. Anintervalthe model cannot predict raisesValueErrorwhen the plan is built, and the explanation says how much history the model reads and when its weights have a restrictive license, are gated or need an account with their provider. (#29, #41)
Results and explanations
-
New
describe()method on every result thatask()accepts,ForecastingProfileincluded. It returns, without an LLM, a plain-text description of the result, with the predictions summarized instead of listed and a length that does not grow with the number of series. See Results (#33, #34) -
The explanation of a
ForecasterDirectMultiVariateplan names the series it predicts, the first one oftarget. (#41)
Errors and warnings
- Every error of skforecast-ai derives from the new
SkforecastAIError, with a stablecode, the argument at fault infieldand, for some, a remedy inhint, so a program can react to it without parsing the message. Invalid inputs raiseInvalidInputError,InvalidInputTypeError,DataNotFoundErrororDataContentError, which are still aValueError, aTypeErroror aFileNotFoundErrorwith the same messages. See Exceptions and warnings (#32, #37, #47)
LLM layer
- Better answers from
ForecastingAssistant.ask(). The skills andllms-base.txtare synced from skforecast 0.26.x, and the skills are chosen by the plan being asked about (statistical models, baselines, foundation models). What the LLM receives lists the decisions you made instead of the rules and the warnings of the plan, so the answers no longer present a chosen value as a recommendation, and it points tobacktest()andcompare()to measure accuracy or choose between models. (#28, #41, #46)
MCP server
- New MCP server (
skforecast-ai mcp, extramcp), so coding agents can profile a CSV file, plan, backtest, compare and forecast as tools, each result with the script that produced it. In Claude Code it installs as a plugin. It never returns rows of your data, only reads CSV files inside--allow-dirand, unless--allow-modelnames another, only runs the foundation models whose license does not restrict commercial use. See MCP server for coding agents and the reference (#35, #36)
CLI
-
The commands
plan,refine-plan,forecast,backtest,forecast-codeandbacktest-codetake the new overrides (--metric,--use-exog,--differentiation,--calendar-features,--target-transformer,--dropna-from-series), withautoto go back to the rule when refining a saved plan, andprofileandplantake--exog-columns.forecastandbacktestalso take--lagsand--window-features. See the CLI guide (#41) -
python -m skforecast_airuns the CLI, like theskforecast-aicommand. (#36)
Changed
Plans and overrides
-
ForecastingAssistant.plan()and every method that builds a plan raiseValueErrorbefore rendering the script: an unsupported estimator, anestimator_kwargsname the estimator does not accept, anintervalthat is not[lower, upper]quantiles, astepsthat is not an integer of at least 1, and dates without an inferable frequency. So dolagsorwindow_featurespassed forForecasterStats,ForecasterFoundationorForecasterEquivalentDate, which do not use them (the first two ignored them silently). (#27, #31) -
A
ForecastPlanbuilt by hand or loaded from JSON raisesValidationErrorfor what the generated scripts do not support: aforecasterthat is not supported or does not matchtask_type, a forecaster argument or a blocking preprocessing step thatplan()does not generate, or anintervalwithout aninterval_method.forecast(),backtest(), their*_code()methods andask()validate a received plan, and aDataProfilewhosefrequencyis not a pandas alias raises too. Plans saved with 0.3.1 load unchanged. (#31, #33) -
A
planpassed toForecastingAssistant.forecast(),backtest()or their*_code()methods, and aprofilepassed withdatato them or tocompare(), are checked against the data: another frequency or shape, or exogenous variables the data does not have, raiseValueErrorinstead of running with lags and features that do not fit. Data with new rows or series run with a refreshed profile, noted inDataProfile.warnings; callprofile()andplan()again for data of another structure. (#37, #41)
Forecasting
-
ForecastingAssistant.forecast()andforecast_code()in evaluation mode raiseValueErrorunless the test set holds exactlystepsobservations. A longer test set was scored on its firststepsrows without saying so, and a shorter one failed inside the script. Usebacktest()to evaluate over a longer period. -
ForecastingAssistant.forecast()withouttest_size, given a plan that carries theend_trainof an evaluation, raisesValueErrorinstead of evaluating the same dates again: passtest_size, orplan.model_copy(update={'end_train': None})to forecast the future. (#37)
Backtesting and comparison
-
ForecastingAssistant.create_cv()trains the forecaster once by default (refit=False, the skforecast default) instead of refitting it in every fold, which could take an hour instead of seconds on two years of hourly data. The metrics ofbacktest()andcompare()with the default strategy change; passrefit=True(--refitin the CLI) to keep the previous behavior.ForecasterStats, which skforecast refits in every fold anyway, now trains on a window of fixed size by default, so its metrics change too. (#33) -
With several series,
ForecastingAssistant.compare()withoutcandidatescomparesForecasterRecursiveMultiSeriesagainstForecasterFoundation(Chronos-2), as it does on a single series, instead of the estimators ofForecasterRecursiveMultiSeries. Passcandidatesto compare the estimators as before. (#29) -
ForecastingAssistant.backtest()andbacktest_code()given theCVResultofcreate_cv()without aplanor model arguments run the plan of thatCVResultinstead of a new default one. ACVResultcreated for data of another structure raisesValueError. Passcv=cv_result.cvto backtest a new default plan as before. (#37) -
ForecastingAssistant.create_cv()raisesValueErrorwhenskip_foldsnames folds that do not exist, which were ignored. (#37)
Foundation models
-
The
estimatorof aForecasterFoundationplan is the Hugging Face model ID (default'autogluon/chronos-2-small') instead of the'Chronos-2'label, for exampleestimator='google/timesfm-3.0-pytorch'. An ID that no skforecast adapter serves,'Chronos-2'included, raisesValueError. (#29, #31) -
ForecastingAssistant.forecast()with aForecasterFoundationplan and anintervalreturns thepred,lower_boundandupper_boundcolumns of the other forecasters instead of oneq_*column per quantile.backtest()keeps the quantile columns. (#29)
Statistical models
- Auto-ARIMA (
ForecasterStats) now gets a seasonal period for more frequencies: the anchored ones pandas infers for quarterly data ('QS-OCT','QE-DEC': 4) and the multiplied ones whose cycle is a whole number of steps from 2 to 12 ('2MS': 6,'3h': 8). Its predictions, its metrics and the winner ofForecastingAssistant.compare()can change on such data. For a longer cycle, ask for it withestimator_kwargs={'m': 26}inplan(). (#37, #43)
Data and profiles
- A
date_columnthat does not hold dates raisesValueErrorinstead of being used as an exogenous variable, and so does aseries_id_columnthat is also the target or the date column inForecastingAssistant.profile(). (#37)
LLM layer
- The
resultkeyword ofForecastingAssistant.ask(), deprecated in 0.3.0, is removed and raisesTypeError: pass the object to explain ascontext(ask(prompt, context=result)). (#37)
CLI
-
The commands that take
--from-planexit with code 1 when--stepsdiffers from the steps of the plan, which was ignored: omit it, or change the horizon withrefine-plan --steps.--formatexits with code 2 for a value the command does not accept, which printed the default output. (#37) -
Clearer
--helptexts:--intervaltakes quantiles, what--base-urlmeans for each provider, the fields a--candidatesconfig accepts, and the help ofask --send-data-to-llmno longer describes it as permission to send raw data, which the CLI never sends. (#28)
Performance
- Faster on large long-format data, with the same results:
ForecastingAssistant.profile()takes about 15 % less on 913,000 rows of 500 series, and the methods that receive data with a saved profile profile the same data once per assistant instead of on every call (backtest_code()from 1.7 s to 0.15 s). (#42)
Dependencies
- skforecast-ai now requires
skforecast>=0.26.0, andlightgbmis a dependency:LGBMRegressoris the recommended estimator from 250 observations, so a clean install failed on the default plan with anImportErrorinside the script.
Documentation
-
New documentation home page, and new animations in the Agentic forecasting user guide: what reaches the LLM and how its suggestions are validated, how
create_cv()andbacktest()validate the way you deploy, and howcompare()picks the model by measured performance. (#28, #36) -
The Quick start section is reorganized into Installation, Your first forecast, now a notebook with its outputs, and the new Ask the assistant. Using the CLI is rewritten as a shorter guide organized by task, and the API reference opens with a table of the methods of
ForecastingAssistant. (#28)
Fixed
Data and profiles
-
ForecastingAssistant.profile()reads rows that are not in date order sorted, as the generated script does, and says so inDataProfile.warnings: descending dates gave a script that failed, and shuffled rows gave other lags and predictions without any warning. A timestamp with several rows of different values raisesValueErrorsuggesting the column to pass asseries_id_column, instead of silently keeping the first one. (#32, #47) -
Text dates, in a CSV or in a
date_column, are read as the generated script reads them, so for day-first dates such as13/01/2012the profile, the lags andforecast()now match the script. Dates written in more than one format, and a date column with empty cells or mixed UTC offsets, raiseInvalidInputErrornaming the column, where the script failed or the dates silently became an exogenous variable. (#32) -
ForecastingAssistant.profile()infers the frequency despite missing timestamps, which left it unknown and made every forecaster fail with "yhas a pandas DatetimeIndex without a frequency". For long-format data it reads the frequency and the missing timestamps of every series, not only of the first one: series of different frequencies raiseInvalidInputError, where the generated script resampled them without an error. (#27, #32) -
A profile, a plan or a result saved with
pickleby an earlier version loads with the fields added since, where reading one of them raisedAttributeError. (#45)
Plans and overrides
-
ForecastingAssistant.plan()and every method that builds a plan raiseInvalidInputErrorfor plans whose generated script always failed:ForecasterDirectMultiVariateon long-format data with several series, a multi-series forecaster on a single series, and an exogenous column named like a lag or a window feature (such aslag_1). (#32) -
ForecastingAssistant.plan()leaves out the calendar features whose columns already exist among the exogenous variables (for examplemonthorhour), which madeforecast()andbacktest()fail with "Duplicated feature names detected". The data column is used instead, and the plan explanation names the skipped features. -
ForecastingAssistant.refine_plan()no longer carries over values that do not apply to the refined plan: switching forecaster family re-derives the estimator, lags and window features (an ML plan refined withforecaster="ForecasterStats"keptRidge), and a newestimatordrops the previousestimator_kwargs. With an LLM, a suggestion of lags or window features that a check of the plan rejects is discarded with aUserWarning. (#27, #37) -
Plans with missing values drop rows only when the estimator cannot handle them. The missing timestamps restored by
asfreq()now count as missing values, so aRidgeplan drops those rows instead of failing with "Input X contains NaN", andRandomForestRegressoris treated as NaN-tolerant, as it is since scikit-learn 1.4. (#27) -
estimator_kwargswith numpy values (for example taken fromnp.logspace) madeforecast(),backtest()andcompare()fail withNameError: name 'np' is not defined, and the plan could not be saved as JSON. They are now stored as the Python values they hold. -
The script of a
ForecasterDirectMultiVariateplan that does not use the exogenous columns fitted them as series too. It now fits only the target series, so its predictions change. (#41) -
Infinite values of the target, an
estimator_kwargsname that the ARIMA model ofForecasterStatsdoes not accept, a foundation model whose backend package is not installed and acontext_lengththat is not a positive integer raise before the generated script runs, where it failed inside it.ForecastingAssistant.profile()of a target that is almost all infinite no longer raises aValueErrorthat did not say why. (#37, #41)
Forecasting
-
ForecastingAssistant.forecast()checks the futureexogand the end of the target before running: missing or misdated rows, final rows without a target value and missing values that the predictions read raiseInvalidInputError, where they gave missing, shifted or wrong predictions without an error. What LightGBM and the other tolerant estimators can read gives a warning instead. A plan withuse_exog=Falseno longer requires a futureexog. (#27, #32) -
In evaluation mode (
test_size),ForecastingAssistant.forecast()checks the training partition as in prediction mode, where a missing value read by the lags, or a series without data at the split, failed inside the script or scored shifted predictions. For data recorded within the day, a training set that ended at midnight no longer includes that whole day, and with long-format series that start on different dates the split no longer falls after the data. (#37, #41, #43) -
ForecastingAssistant.forecast()in evaluation mode computes the same eight regression metrics as backtesting; it left out, without saying so, any other than MAE, MSE, MASE and MAPE.compare()raisesValueErrorbefore running any candidate for a metric it cannot rank, a metric listed twice or an invalidinterval, instead of failing every candidate. (#37)
Backtesting and comparison
-
ForecastingAssistant.backtest()raisesInvalidInputErrorbefore running where the script failed inside skforecast: a missing target value that a lag or a test fold reads ("Input contains NaN"), a series with fewer values than its lags read, a first training window shorter than the forecaster needs, or a direct forecaster backtested with agap.create_cv()warns when it builds such a strategy, and each candidate ofcompare()reports the same message. (#27, #37, #41, #43, #47) -
ForecastingAssistant.backtest()andbacktest_code()failed with "Cannot compare tz-naive and tz-aware timestamps" when the dates of the data have a time zone andinitial_train_sizeis a date without one (the default ofcreate_cv()), and raisedTypeErrorfor aTimeSeriesFoldwhoseinitial_train_sizeis a pandas Timestamp. The date is now read in the time zone of the data, kept in the newDataProfile.time_zone. -
ForecastingAssistant.backtest()andcompare()with anintervalon several series computed conformal prediction intervals forForecasterRecursiveMultiSeriesandForecasterDirectMultiVariate, while the plan andforecast()use bootstrapping. They now use bootstrapping too, so the bounds of these backtests change; the point predictions and the metrics do not. (#33) -
With
refit=Falseor an integerrefit, the backtesting script,cv_configand explanation of aForecasterStatsplan said that it was trained once (or every n folds) on a growing window, while skforecast refits ARIMA models in every fold. They now state what runs, andForecastingAssistant.create_cv()warns with skforecast'sIgnoredArgumentWarningwhen arefitorfixed_train_sizepassed for it does not run; the metrics do not change. (#33) -
ForecastingAssistant.create_cv()with apromptcould return a strategy whose first training window was too short for the forecaster of the plan, which failed inbacktest(). A suggestion below the minimum the plan needs is now retried, falling back to the deterministic defaults after three attempts. (#45)
Generated scripts
-
A crafted value could inject code into the generated script, which
forecast()andbacktest()execute: a field of a plan or of a profile loaded from JSON, a foundation model ID or a column name of a CSV. A name that must come from a fixed set raisesValueErrorwhen the script is rendered, and any other value is written as a quoted literal. A column name with a quote no longer gives a script that fails withSyntaxError. (#31) -
ForecastingAssistant.forecast(),backtest()andcompare()given a CSV path returned a script that loaded'data.csv'instead of that file. The script now loads the CSV path or URL passed, also inforecast_code(),backtest_code()and the CLI. (#34) -
The generated script, run as a file, did not read the data as
forecast()does in two cases: data without dates, where it failed, and long-format data with exogenous variables, where the dates ofexog_future.csvwere read as text and it gave other predictions. It now reads them the same way. (#34)
Errors and warnings
-
Invalid inputs that raised an error of pandas, skforecast or Python (
KeyError,AttributeError, "could not convert string to float", aParserError) now raiseInvalidInputError, still aValueError, naming the argument at fault: aseries_id_columnthat does not exist, a target with text values, a CSV file that cannot be read... See Exceptions and warnings (#37) -
A generated script that does not compile raises
ForecastExecutionErrorinforecast()andbacktest(), like a script that fails while it runs, instead of a bareSyntaxError.ForecastExecutionErrorhas two new attributes,failed_lineandfailed_statement: the line ofgenerated_codewhere the error was raised and the statement that holds it. (#31) -
ForecastingAssistant.forecast()did not show the warnings that skforecast emits while the generated script runs, such asMissingValuesWarningwhen the estimator is trained with missing values. They are now shown once it ends, and your warning filters still decide which ones. (#37)
LLM layer
-
Without the API key of the provider,
ForecastingAssistant.ask(),refine_plan(prompt=...)andcreate_cv(prompt=...)raised aUserErrorof pydantic-ai.ask()now raisesLLMCallError, and the other two return the deterministic result with aUserWarning, as when the call fails. (#46) -
ForecastingAssistant.ask()about the result ofbacktest_code()was told that the script trains on all the data and forecasts the future, without metrics. It now receives it as a backtest, with its cross-validation strategy and number of folds, like the result ofbacktest(). (#34) -
Two explanations were wrong, and
ask()repeated them. The explanation ofForecastingAssistant.compare()said that multi-series candidates were ranked by the metric "pooled across series", when it is averaged across series, and the plan explanation said "NaN rows kept (NaN-tolerant estimator)" even when no value was missing. (#29) -
A profile or a plan loaded from JSON, or a column name of the data, could add or close sections of what
ForecastingAssistant.ask()sends to the LLM through a line break or a tag. Those texts are now escaped. (#37)
CLI
-
Errors and warnings go to standard error instead of standard output, where they broke the JSON of
--format jsonfor a program reading it. With--format jsonan error is a JSON object{"error": {"code", "message", "field", "hint"}}, and an error that has a remedy prints it asTip: ...after the message. See Using the CLI (#37) -
skforecast-ai config setno longer acceptsoutput.format. No command read it, so the setting had no effect.config showlists it, and any other unknown key found in the file, as ignored. (#28)
0.3.1 Sep 11, 2026¶
Added
-
Feature
ForecastingAssistant.check_llm()and thecheck-llmCLI command report how the LLM configuration resolves before any workflow runs: provider and model, where the credentials come from and whether the environment variable is set, whatbase_urlmeans for the provider, whether the extras are installed and, for Ollama, whether the server answers. Withtest_call=True(--test-call) a one-line prompt is sent to the model. The result is anLLMCheckResultwith anokfield; the CLI exits with code 1 when a check fails. Credential values are never shown. -
Enhancement New user guide Configuring the LLM: model strings, credentials per provider, the meaning of
base_url, local models with Ollama, OpenAI-compatible endpoints, what is sent to the LLM, and troubleshooting. The quick start, the installation page, the CLI reference and the notebooks link to it, and the examples use the same set of model names throughout. -
Docs New API page for the exceptions and warnings the package exports, a user guide on the skills
ask()sends to the LLM (which exist, how they are selected, how to pick them withskills=and--skills, and how they follow the Agent Skills standard), and an "Under the hood" section in the first forecast guide showingprofile(),plan(),forecast_code()andforecast()chained step by step.
Fixed
-
Fix
base_urlis honoured foropenai:and for any prefix that is not built in when noapi_keyis given: the assistant builds an OpenAI-compatible client at that endpoint (local servers accept the placeholder key pydantic-ai sends whenOPENAI_API_KEYis unset). Previously the endpoint was silently dropped and the bare provider string went to pydantic-ai. -
Fix The docstring of
send_data_to_llmdescribed the flag as permission to send raw input data. Input data is never sent; the flag acknowledges that the predictions and metrics of a result passed toask()are sent, and silencesDataSentToLLMWarning. The docstring now says so.
0.3.0 Sep 11, 2026¶
Added
-
Feature
ForecastingAssistant.ask()can explain a generated script (CodeGenerationResult, fromforecast_code()andbacktest_code()) and a cross-validation strategy (CVResult) before anything is executed. Neither carries data values, so they never triggerDataSentToLLMWarning. -
Feature Every result (
ForecastResult,BacktestResult,ComparisonResult,CodeGenerationResult,CVResult) serializes to JSON withmodel_dump(mode="json")andmodel_dump_json(); DataFrames become lists of row records. The JSON output of the CLI commands is exactly this dump. -
Enhancement
ForecastingAssistant.ask()sends a richer context to the LLM: date range and target statistics, missing values, significant lags, the suggested features, a summary of the generated script, and per-series statistics of the predictions for multi-series results. Answers are better grounded and questions about one series can be answered.
Changed
-
API Change
ForecastingAssistant.create_cv()returns aCVResultinstead of a(TimeSeriesFold, str)tuple: the splitter isresult.cv, the explanationresult.explanation, and the result also carriescv_config(withn_folds), acodesnippet and theprofileandplanit came from.backtest(),backtest_code()andcompare()accept it directly ascv. Unpacking it as before raises aTypeErrorthat points to the new attributes. -
API Change
ForecastingAssistant.ask()takes the object to explain as a singlecontextargument (aForecastingProfile, optionally withplan=, or any result). It no longer profiles or plans on its own, so thedata,target,date_column,series_id_column,profileandstepsarguments are removed: callprofile()orplan()first and pass the object.result=keeps working as a deprecated alias until 0.4.0. TheaskCLI command gains--from-profile. -
API Change
ForecastingAssistant.ask()raisesLLMCallErrorwhen the call to the LLM fails, with the provider exception available asoriginal_error. Previously it returned an answer starting with[LLM unavailable], which a pipeline could mistake for a real one.refine_plan()andcreate_cv()keep falling back to their deterministic output with a warning. -
API Change Arguments already recorded in a
profileorplanbecome optional: withprofile,target,date_columnandseries_id_columndefault to the profile's values inforecast(),forecast_code(),backtest(),backtest_code()andcompare(); withplan,stepsdefaults toplan.stepsinforecast()andforecast_code(). A value that conflicts with the profile or plan raisesValueErrorinstead of being silently ignored. The same rule applies toforecaster,estimator,estimator_kwargs,lagsandwindow_featurespassed alongside aplan, which previously only emittedIgnoredArgumentWarning.intervalis the exception: as a prediction-time option, it replaces the interval of a pre-built plan (forecast(plan=best_plan, interval=[0.1, 0.9])now produces the bounds), and None keeps the plan's intervals. -
API Change
ForecastingAssistant.compare()no longer ranks multi-series forecasters (scored on the average across all series) against multivariate forecasters (scored on the single series they predict): mixing them incandidatesraisesValueError. For multi-series data the automatic candidates compare the estimators ofForecasterRecursiveMultiSeriesinstead (rows namedForecasterRecursiveMultiSeries+<estimator>). -
Enhancement The overrides of
refine_plan()and the candidate configurations ofcompare()are typed (RefinePlanOverridesandCandidateConfiginskforecast_ai.schemas), so editors autocomplete the accepted keys. Behaviour is unchanged. -
Enhancement The LLM suggestions of
ForecastingAssistant.refine_plan()(lags, window features) andForecastingAssistant.create_cv()(fold parameters) are validated by the Pydantic output schema and by the same checks the explicit arguments go through. An invalid suggestion is sent back to the model with the concrete error and, if it persists, the deterministic result is used with aUserWarning; previously a malformed suggestion could crashrefine_plan()or surface a pandas error fromcreate_cv(). The agents' instructions state the constraints (positive unique lags, ISO dates within the dataset range) and the CV agent receives the first and last date of the series. -
Enhancement CLI:
--initial-train-sizeaccepts an ISO date marking the end of the initial training set, and--lags auto/--window-features autore-run the deterministic selection when refining a saved plan (refine-plan,backtest-code --from-plan,forecast-code --from-plan).
Fixed
-
Fix
ForecastingAssistant.plan(),refine_plan(),compare()and the CLI--lagsoption reject invalid lag specifications with aValueError(0, an empty list, non-positive or non-integer values, booleans, duplicates) instead of passing them to skforecast, where an empty list silently trained without lag features and duplicates produced repeated features.window_featuresentries that repeat the same statistic with the same window size are rejected for the same reason. -
Fix
ForecastingAssistant.create_cv()accepts apandas Timestampasinitial_train_size(documented but previously failing) and raises a clearValueErrornaminginitial_train_sizewhen a date string cannot be parsed or when the dataset has no datetime index with a known frequency. Previously the date was located on a fabricated daily index or a raw pandas error surfaced. The same check protectsbacktest()andcompare()when given a date-basedTimeSeriesFold. -
Fix CLI:
--no-refit,--fixed-train-sizeand--gap 0had no effect inbacktest,backtest-codeandcomparebecause they matched the declared default and were dropped. The six cross-validation options are now forwarded tocreate_cv()only when passed, and the assistant decides the rest. -
Fix The script produced by
ForecastingAssistant.backtest_code()loads the data from the CSV path it was given, asforecast_code()already did. Previously it always readdata.csv.backtest_code()also acceptsdata=Nonewhenprofileandplanare given, so the CLI commandbacktest-code --from-planrenders the script withoutDATA, asforecast-codedoes. -
Fix CLI:
forecast-codeandbacktest-codeapply the modeling flags given alongside--from-plan(--estimator,--interval,--lags, ...) on top of the saved plan, asforecastandbacktestalready did, instead of ignoring them. The confirmation messages of--output-codeand--output-predictionsgo to stderr, so--format jsonoutput stays parseable when both are combined. -
Fix
BacktestResult.explanationfor a multi-series backtest reported a value matching no row of the metrics table (a mean over the per-series and aggregate rows). It now reports theaveragerow and says so. -
Fix
ForecastingAssistant.refine_plan()keeps thellm_refined_fieldsmarks of the fields it does not override, so LLM-suggested lags stay flagged after refining another field.
0.2.0 Aug 3, 2026¶
Added
- Feature
ForecastingAssistant.compare()backtests several forecaster/estimator configurations with the same cross-validation strategy and returns a metric-rankedComparisonResultleaderboard. Each successful candidate is available incandidates, a name-keyed mapping ofBacktestResultordered best to worst, and the winning configuration is exposed asbest_name/best_candidatefor direct reuse. A matchingcompareCLI command reports the leaderboard and supports--candidates,--metric,--from-profile, and--output-code.
Changed
- API Change
ForecastingAssistant.ask()now accepts a singleresultparameter in place of the previousforecast_resultandbacktest_resultparameters. Update calls fromask(forecast_result=...)/ask(backtest_result=...)toask(result=...). AnyExplainableResultis accepted, includingForecastResult,BacktestResult, andComparisonResult.
Fixed
- API Change
ForecastingAssistant.ask()now treats a suppliedresultas the single source of truth. Previously an explicitprofileorplantook precedence over the result's own while the context and code still came from the result, so the returned artifacts could describe different states.data,target,date_column,series_id_column,profile,plan, andstepsare now ignored with anIgnoredArgumentWarningwhenresultis provided.
0.1.0 Jul 13, 2026¶
First public release. skforecast-ai wraps the skforecast engine in a deterministic, rule-based assistant that profiles the data, selects a model, evaluates it, and returns the forecast together with the exact, runnable script that produced it. An optional LLM layer explains the decisions without ever changing them.
Maturity
The underlying forecasting engine (skforecast) is mature and production-grade. The skforecast-ai assistant layer is at 0.1.0, so its public API may still change.
Added
ForecastingAssistant: the main entry point, covering the full workflow:profile(),plan(),refine_plan(),forecast()/forecast_code(),create_cv(),backtest()/backtest_code(), andask().- Typer-based CLI mirroring the programmatic API, with persistent configuration.