Skip to content

Results

skforecast_ai.schemas.results

Classes:

Name Description
RenderedScript

Structured representation of a rendered forecasting script.

LLMContext

Everything ask() needs in order to explain a result object.

ExplainableResult

Capability shared by every result that can describe itself to an LLM.

CodeGenerationResult

Result of the forecast_code workflow.

SingleRunResult

Shared base for the result of a single forecasting or backtesting run.

ForecastResult

Result of the forecast workflow (executes the pipeline end-to-end).

BacktestResult

Result of the backtest workflow.

AskResult

Result of the ask workflow (requires LLM).

CandidateFailure

Reason why a single compare() candidate failed to run.

ComparisonResult

Result of the compare workflow (ranks several forecasters).

Classes

RenderedScript

Bases: BaseModel

Structured representation of a rendered forecasting script.

Splits the rendered script into logical sections so that forecast() can exec the core logic while forecast_code() returns the full standalone script.

Attributes:

Name Type Description
imports str

Import statements required by the script.

data_loading str

Code that loads data from CSV and sets up the index.

core str

Core execution logic (preprocessing, split, fit, predict, metrics). Operates on a pre-existing data DataFrame variable.

Attributes
imports instance-attribute
imports
data_loading instance-attribute
data_loading
core instance-attribute
core
full_script property
full_script

Return the complete standalone script (imports + loading + core).

executable property
executable

Return code suitable for exec() (imports + core, no CSV loading).

LLMContext

Bases: BaseModel

Everything ask() needs in order to explain a result object.

Produced by ExplainableResult._build_llm_context. Keeping the four fields in a single object means ask() never reads a result's own attributes, so a new kind of result can be explained without touching ask().

Attributes:

Name Type Description
text str

Rendered plain-text context block inserted into the user message.

profile ForecastingProfile, default None

Profile echoed back on AskResult and used to select skills.

plan ForecastPlan, default None

Plan echoed back on AskResult.

code str, default None

Generated script echoed back on AskResult. When not None, ask() strips code blocks from the LLM response, since a validated script already exists.

Attributes
model_config class-attribute instance-attribute
model_config = ConfigDict(arbitrary_types_allowed=True)
text instance-attribute
text
profile class-attribute instance-attribute
profile = None
plan class-attribute instance-attribute
plan = None
code class-attribute instance-attribute
code = None

ExplainableResult

Capability shared by every result that can describe itself to an LLM.

Mirrors DisplayMixin, which lets a result describe itself to a terminal. Subclasses must implement _build_llm_context, returning the context block plus the artifacts ask() echoes back on its AskResult.

Each result decides its own payload, so an aggregate result (for example ComparisonResult) can send a compact summary instead of the concatenated payloads of everything it wraps.

Methods:

Name Description
to_llm_context

Build the LLM context for this result.

Functions
_build_llm_context
_build_llm_context(*, send_data)
Source code in skforecast_ai/schemas/results.py
107
108
109
110
111
112
def _build_llm_context(
    self, *, send_data: bool
) -> LLMContext:  # pragma: no cover - overridden by subclasses
    raise NotImplementedError(
        f"{type(self).__name__} must implement _build_llm_context"
    )
to_llm_context
to_llm_context(*, send_data=False)

Build the LLM context for this result.

Parameters:

Name Type Description Default
send_data bool

Whether raw data values may be included. When False, only aggregate statistics are shown for row-level data. The decision belongs to the caller, so the privacy policy stays owned by ForecastingAssistant.

False

Returns:

Name Type Description
context LLMContext

Rendered context block plus the artifacts ask() echoes back.

Source code in skforecast_ai/schemas/results.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def to_llm_context(self, *, send_data: bool = False) -> LLMContext:
    """
    Build the LLM context for this result.

    Parameters
    ----------
    send_data : bool, default False
        Whether raw data values may be included. When False, only
        aggregate statistics are shown for row-level data. The
        decision belongs to the caller, so the privacy policy stays
        owned by `ForecastingAssistant`.

    Returns
    -------
    context : LLMContext
        Rendered context block plus the artifacts `ask()` echoes back.
    """

    return self._build_llm_context(send_data=send_data)

CodeGenerationResult

Bases: DisplayMixin, BaseModel

Result of the forecast_code workflow.

Attributes:

Name Type Description
profile ForecastingProfile

Profile of the input dataset and high-level modeling decisions.

plan ForecastPlan

Detailed forecasting plan.

code str

Generated Python script.

Attributes
profile instance-attribute
profile
plan instance-attribute
plan
code instance-attribute
code
Functions
_rich_body
_rich_body(console, options)
Source code in skforecast_ai/schemas/results.py
153
154
155
156
157
def _rich_body(
    self, console: Console, options: ConsoleOptions
) -> RenderResult:
    yield render_profile(self.profile)
    yield render_plan(self.plan)

SingleRunResult

Bases: DisplayMixin, ExplainableResult, BaseModel

Shared base for the result of a single forecasting or backtesting run.

Declares the fields that every single run produces, and renders them into an LLM context block through _build_llm_context. Concrete results (for example ForecastResult and BacktestResult) inherit from this class and add the fields specific to them.

Aggregate results that wrap several runs (for example ComparisonResult) do not inherit from this class; they implement ExplainableResult directly so they can send a compact summary rather than a concatenation of everything they wrap.

Attributes:

Name Type Description
profile ForecastingProfile

Profile of the input dataset and high-level modeling decisions.

plan ForecastPlan

Detailed forecasting plan that was executed.

code str

Generated Python script equivalent to the execution.

predictions pandas DataFrame

Forecasted values produced by the run.

metrics pandas DataFrame

Evaluation metrics produced by the run.

Attributes
model_config class-attribute instance-attribute
model_config = ConfigDict(arbitrary_types_allowed=True)
profile instance-attribute
profile
plan instance-attribute
plan
code instance-attribute
code
predictions instance-attribute
predictions
metrics instance-attribute
metrics
Functions
_build_llm_context
_build_llm_context(*, send_data)

Describe a single run to the LLM.

Parameters:

Name Type Description Default
send_data bool

Whether raw prediction values may be included.

required

Returns:

Name Type Description
context LLMContext

Context block covering the profile, plan, cross-validation configuration, deterministic summary, metrics, and predictions of this run.

Source code in skforecast_ai/schemas/results.py
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
def _build_llm_context(self, *, send_data: bool) -> LLMContext:
    """
    Describe a single run to the LLM.

    Parameters
    ----------
    send_data : bool
        Whether raw prediction values may be included.

    Returns
    -------
    context : LLMContext
        Context block covering the profile, plan, cross-validation
        configuration, deterministic summary, metrics, and predictions
        of this run.
    """

    # Deferred import: `llm.context` imports from this package, so a
    # module-level import here would be circular.
    from ..llm.context import (
        join_sections,
        render_cv_section,
        render_dataset_section,
        render_deterministic_summary_section,
        render_metrics_section,
        render_plan_section,
        render_predictions_section,
        render_profile_decision_section,
    )

    # Only runs that were cross-validated carry these fields. Sending
    # the deterministic explanation matters: it already states facts
    # such as the fold count, which the LLM would otherwise try to
    # re-derive from the truncated prediction table.
    cv_config = getattr(self, "cv_config", None)
    explanation = getattr(self, "explanation", None)

    return LLMContext(
        text    = join_sections([
                      render_dataset_section(self.profile),
                      render_profile_decision_section(self.profile),
                      render_plan_section(self.plan),
                      render_cv_section(cv_config),
                      render_deterministic_summary_section(explanation),
                      render_metrics_section(
                          self.metrics,
                          has_predictions = self.predictions is not None,
                      ),
                      render_predictions_section(
                          self.predictions, send_data=send_data
                      ),
                  ]),
        profile = self.profile,
        plan    = self.plan,
        code    = self.code,
    )

ForecastResult

Bases: SingleRunResult

Result of the forecast workflow (executes the pipeline end-to-end).

Attributes:

Name Type Description
profile ForecastingProfile

Profile of the input dataset and high-level modeling decisions.

plan ForecastPlan

Detailed forecasting plan that was executed.

code str

Generated Python script equivalent to the execution.

metrics pandas DataFrame, None

Evaluation metrics. DataFrame with columns ['series', 'MAE', 'MSE', 'MASE']. For single-series tasks this contains one row; for multi-series tasks one row per level. None in prediction mode (test_size=None), where there is no ground truth to evaluate against.

predictions pandas DataFrame

Forecasted values for the requested steps. When prediction intervals (or quantiles) are requested, the corresponding bound columns are included alongside the point predictions.

Functions
_rich_body
_rich_body(console, options)
Source code in skforecast_ai/schemas/results.py
278
279
280
281
282
283
284
285
def _rich_body(
    self, console: Console, options: ConsoleOptions
) -> RenderResult:
    yield render_profile(self.profile)
    yield render_plan(self.plan)
    if self.metrics is not None:
        yield render_metrics(self.metrics, title="Forecast Metrics")
    yield render_dataframe(self.predictions, title="Predictions")

BacktestResult

Bases: SingleRunResult

Result of the backtest workflow.

Attributes:

Name Type Description
profile ForecastingProfile

Profile of the input dataset and high-level modeling decisions.

plan ForecastPlan

Detailed forecasting plan that was executed.

cv_config dict

Resolved TimeSeriesFold parameters plus the resulting n_folds, for traceability.

metrics pandas DataFrame

Backtesting metric values returned by skforecast.

predictions pandas DataFrame

Full backtest predictions across all folds.

code str

Generated Python script reproducing the backtesting workflow.

explanation str

Human-readable explanation of the backtesting configuration and results summary.

Attributes
cv_config instance-attribute
cv_config
explanation instance-attribute
explanation
_explanation_title class-attribute
_explanation_title = 'Backtest Explanation'
Functions
_rich_body
_rich_body(console, options)
Source code in skforecast_ai/schemas/results.py
317
318
319
320
321
322
323
324
325
def _rich_body(
    self, console: Console, options: ConsoleOptions
) -> RenderResult:
    yield render_explanation(self.explanation, title="Backtest Explanation")
    yield render_cv_config(self.cv_config)
    yield render_metrics(self.metrics, title="Backtest Metrics")
    yield render_dataframe(self.predictions, title="Backtest Predictions")
    yield render_profile(self.profile)
    yield render_plan(self.plan)

AskResult

Bases: DisplayMixin, BaseModel

Result of the ask workflow (requires LLM).

Attributes:

Name Type Description
profile ForecastingProfile, default None

Profile of the input dataset and high-level modeling decisions, if data was provided.

plan ForecastPlan, default None

Detailed forecasting plan, if the agent produced one.

code str, default None

Generated Python script, if the agent produced one.

explanation str

LLM-generated explanation or response.

Attributes
profile class-attribute instance-attribute
profile = None
plan class-attribute instance-attribute
plan = None
code class-attribute instance-attribute
code = None
explanation instance-attribute
explanation
_explanation_title class-attribute
_explanation_title = 'Assistant Response'
Functions
_rich_body
_rich_body(console, options)
Source code in skforecast_ai/schemas/results.py
352
353
354
355
def _rich_body(
    self, console: Console, options: ConsoleOptions
) -> RenderResult:
    yield render_explanation(self.explanation, title="Assistant Response")

CandidateFailure

Bases: BaseModel

Reason why a single compare() candidate failed to run.

Holds a plain-data snapshot of the failure instead of the live exception. An exception object keeps its traceback frames alive, and those frames reference the execution namespace (which contains a copy of the dataset, the fitted forecaster and the predictions), so retaining one per failed candidate would pin an unbounded amount of memory. The formatted traceback carries the same debugging information at a fixed, small cost, and keeps ComparisonResult serializable.

Attributes:

Name Type Description
error_type str

Class name of the root-cause exception, for example 'ImportError'.

message str

Message of the root-cause exception.

traceback str

Full formatted traceback of the failure.

generated_code str, default None

Generated script that failed, when the failure happened while executing rendered code. None for failures raised before execution, such as an invalid plan.

Methods:

Name Description
from_exception

Build a CandidateFailure from the exception a candidate raised.

summary

Build a concise one-line "ErrorType: message" summary.

Attributes
error_type instance-attribute
error_type
message instance-attribute
message
traceback instance-attribute
traceback
generated_code class-attribute instance-attribute
generated_code = None
Functions
from_exception classmethod
from_exception(exc)

Build a CandidateFailure from the exception a candidate raised.

A ForecastExecutionError wraps the generated code and the formatted execution traceback; it is unwrapped to its original_error root cause so the failure reports the underlying reason rather than the verbose execution-context message.

Parameters:

Name Type Description Default
exc Exception

Exception raised while evaluating a candidate.

required

Returns:

Name Type Description
failure CandidateFailure

Plain-data snapshot of the failure.

Source code in skforecast_ai/schemas/results.py
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
@classmethod
def from_exception(cls, exc: Exception) -> CandidateFailure:
    """
    Build a `CandidateFailure` from the exception a candidate raised.

    A `ForecastExecutionError` wraps the generated code and the
    formatted execution traceback; it is unwrapped to its
    `original_error` root cause so the failure reports the underlying
    reason rather than the verbose execution-context message.

    Parameters
    ----------
    exc : Exception
        Exception raised while evaluating a candidate.

    Returns
    -------
    failure : CandidateFailure
        Plain-data snapshot of the failure.
    """

    from ..exceptions import ForecastExecutionError

    if isinstance(exc, ForecastExecutionError):
        root = exc.original_error
        formatted = exc.execution_traceback
        generated_code = exc.generated_code
    else:
        root = exc
        formatted = "".join(
            traceback.format_exception(type(exc), exc, exc.__traceback__)
        )
        generated_code = None

    return cls(
        error_type     = type(root).__name__,
        message        = str(root),
        traceback      = formatted,
        generated_code = generated_code,
    )
summary
summary(max_length=200)

Build a concise one-line "ErrorType: message" summary.

Parameters:

Name Type Description Default
max_length int

Maximum length of the returned summary. Longer summaries are truncated with a trailing ellipsis.

200

Returns:

Name Type Description
summary str

Single-line summary of the failure.

Source code in skforecast_ai/schemas/results.py
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
def summary(self, max_length: int = 200) -> str:
    """
    Build a concise one-line `"ErrorType: message"` summary.

    Parameters
    ----------
    max_length : int, default 200
        Maximum length of the returned summary. Longer summaries are
        truncated with a trailing ellipsis.

    Returns
    -------
    summary : str
        Single-line summary of the failure.
    """

    lines = [line.strip() for line in self.message.splitlines() if line.strip()]
    first_line = lines[0] if lines else ""
    summary = f"{self.error_type}: {first_line}" if first_line else self.error_type
    if len(summary) > max_length:
        summary = summary[: max_length - 3].rstrip() + "..."

    return summary

ComparisonResult

Bases: DisplayMixin, ExplainableResult, BaseModel

Result of the compare workflow (ranks several forecasters).

Backtests several forecaster/estimator configurations with the same cross-validation strategy and returns a metric-ranked leaderboard plus the winning configuration as a reusable BacktestResult.

Attributes:

Name Type Description
profile ForecastingProfile

Shared profile used for every candidate.

cv_config dict

Resolved TimeSeriesFold parameters plus the resulting n_folds, applied identically to every candidate.

results pandas DataFrame

Ranked comparison table, one row per candidate sorted best to worst by ranking_metric. Columns are ['rank', 'name', 'forecaster', 'estimator', <metric columns...>], plus an 'error' column when at least one candidate failed.

candidates dict

Mapping of candidate name to the full BacktestResult of every candidate that ran successfully, ordered best to worst. Never empty, so best_name and best_candidate are always resolvable.

failures dict

Mapping of candidate name to a CandidateFailure describing why it failed, in the order the candidates were evaluated. Empty when every candidate succeeded. Each entry carries the root-cause type and message, the full formatted traceback, and the generated code that failed.

ranking_metric str

Name of the metric used to sort results.

explanation str

Human-readable summary of the comparison.

best_name str

Name of the top-ranked candidate.

best_candidate BacktestResult

Top-ranked candidate. Always present: a comparison in which every candidate fails raises AllCandidatesFailedError instead of returning a result.

Notes

Every candidate name appears in exactly one of candidates and failures, never in both and never in neither. The two mappings therefore partition the candidates that were evaluated, and their union matches the 'name' column of results.

best_name and best_candidate are plain properties rather than fields, so the winning BacktestResult is not serialized a second time by model_dump().

Attributes
model_config class-attribute instance-attribute
model_config = ConfigDict(arbitrary_types_allowed=True)
profile instance-attribute
profile
cv_config instance-attribute
cv_config
results instance-attribute
results
candidates class-attribute instance-attribute
candidates = Field(min_length=1)
failures class-attribute instance-attribute
failures = Field(default_factory=dict)
ranking_metric instance-attribute
ranking_metric
explanation instance-attribute
explanation
_explanation_title class-attribute
_explanation_title = 'Comparison Explanation'
best_name property
best_name

Return the name of the top-ranked candidate.

best_candidate property
best_candidate

Return the BacktestResult of the top-ranked candidate.

Functions
_build_llm_context
_build_llm_context(*, send_data)

Describe the comparison to the LLM.

Sends the leaderboard, the shared profile and cross-validation strategy, one line per failure, and the winning candidate's plan. The non-winning candidates' plans, code, and predictions are withheld: the leaderboard already carries the numbers a ranking question needs, so the payload does not grow with the number of candidates. A specific candidate can still be explained by passing candidates['<name>'] to ask() directly.

Parameters:

Name Type Description Default
send_data bool

Whether raw data values may be included. Has no effect here: a comparison renders aggregated leaderboard metrics only, never row-level predictions. The parameter is part of the ExplainableResult interface.

required

Returns:

Name Type Description
context LLMContext

Context block for the comparison. The echoed plan and code are the winning candidate's, since that is the actionable output of a comparison.

Source code in skforecast_ai/schemas/results.py
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
def _build_llm_context(self, *, send_data: bool) -> LLMContext:
    """
    Describe the comparison to the LLM.

    Sends the leaderboard, the shared profile and cross-validation
    strategy, one line per failure, and the winning candidate's plan.
    The non-winning candidates' plans, code, and predictions are
    withheld: the leaderboard already carries the numbers a ranking
    question needs, so the payload does not grow with the number of
    candidates. A specific candidate can still be explained by passing
    `candidates['<name>']` to `ask()` directly.

    Parameters
    ----------
    send_data : bool
        Whether raw data values may be included. Has no effect here:
        a comparison renders aggregated leaderboard metrics only,
        never row-level predictions. The parameter is part of the
        `ExplainableResult` interface.

    Returns
    -------
    context : LLMContext
        Context block for the comparison. The echoed `plan` and `code`
        are the winning candidate's, since that is the actionable
        output of a comparison.
    """

    # Deferred import: `llm.context` imports from this package, so a
    # module-level import here would be circular.
    from ..llm.context import build_comparison_context

    best = self.best_candidate

    return LLMContext(
        text    = build_comparison_context(self),
        profile = self.profile,
        plan    = best.plan,
        code    = best.code,
    )
_rich_body
_rich_body(console, options)
Source code in skforecast_ai/schemas/results.py
574
575
576
577
578
def _rich_body(
    self, console: Console, options: ConsoleOptions
) -> RenderResult:
    yield render_explanation(self.explanation, title="Comparison Explanation")
    yield render_dataframe(self.results, title="Comparison Results")

Functions