8787class ForecastAgentOutput (BaseModel ):
8888 """The output of the forecasting agent."""
8989
90- tsfeatures_results : list [str ] = Field (
91- description = (
92- "The time series features that were considered as a list of strings of "
93- "feature names and their values separated by colons."
94- )
95- )
9690 tsfeatures_analysis : str = Field (
9791 description = (
9892 "Analysis of what the time series features reveal about the data "
@@ -108,12 +102,6 @@ class ForecastAgentOutput(BaseModel):
108102 "strengths, and typical use cases."
109103 )
110104 )
111- cross_validation_results : list [str ] = Field (
112- description = (
113- "The cross-validation results as a string of model names "
114- "and their scores separated by colons."
115- )
116- )
117105 model_comparison : str = Field (
118106 description = (
119107 "Detailed comparison of model performances, explaining why certain "
@@ -126,12 +114,6 @@ class ForecastAgentOutput(BaseModel):
126114 reason_for_selection : str = Field (
127115 description = "Explanation for why the selected model was chosen"
128116 )
129- forecast : list [str ] = Field (
130- description = (
131- "The forecasted values for the time series as a list of strings of "
132- "periods and their values separated by colons."
133- )
134- )
135117 forecast_analysis : str = Field (
136118 description = (
137119 "Detailed interpretation of the forecast, including trends, patterns, "
@@ -324,7 +306,7 @@ def __init__(
324306
325307 3. Final Model Selection and Forecasting:
326308 - Choose the best performing model with clear justification
327- - Generate and analyze the forecast
309+ - Generate the forecast using just the selected model
328310 - Interpret trends and patterns in the forecast
329311 - Discuss reliability and potential uncertainties
330312 - Address any specific aspects from the user's prompt
@@ -364,10 +346,17 @@ def __init__(
364346 )
365347
366348 @self .forecasting_agent .system_prompt
367- async def add_time_series (ctx : RunContext [ExperimentDataset ]) -> str :
349+ async def add_time_series (
350+ ctx : RunContext [ExperimentDataset ],
351+ ) -> str :
352+ df_agg = ctx .deps .df .groupby ("unique_id" ).agg (list )
368353 output = (
369- f"The time series is: { ctx .deps .df ['y' ].tolist ()} , "
370- f"the date column is: { ctx .deps .df ['ds' ].tolist ()} "
354+ "these are the time series in json format where the key is the "
355+ "identifier of the time series and the values is also a json "
356+ "of two elements: "
357+ "the first element is the date column and the second element is the "
358+ "value column."
359+ f"{ df_agg .to_json (orient = 'index' )} "
371360 )
372361 return output
373362
@@ -384,15 +373,27 @@ async def tsfeatures_tool(
384373 f"{ ', ' .join (TSFEATURES .keys ())} "
385374 )
386375 callable_features .append (TSFEATURES [feature ])
387- features_df = _get_feats (
388- index = ctx .deps .df ["unique_id" ].iloc [0 ],
389- ts = ctx .deps .df ,
390- features = callable_features ,
391- freq = ctx .deps .seasonality ,
392- )
393- return "," .join (
394- [f"{ col } : { features_df [col ].iloc [0 ]} " for col in features_df .columns ]
376+ features_df : pd .DataFrame | None = None
377+ for uid in ctx .deps .df ["unique_id" ].unique ():
378+ features_df_uid = _get_feats (
379+ index = uid ,
380+ ts = ctx .deps .df ,
381+ features = callable_features ,
382+ freq = ctx .deps .seasonality ,
383+ )
384+ if features_df is None :
385+ features_df = features_df_uid
386+ else :
387+ features_df = pd .concat ([features_df , features_df_uid ])
388+ features_df = features_df .rename_axis ("unique_id" ) # type: ignore
389+ self .features_df = features_df
390+ output = (
391+ "these are the time series features in json format where the key is "
392+ "the identifier of the time series and the values is also a json of "
393+ "feature names and their values."
394+ f"{ features_df .to_json (orient = 'index' )} "
395395 )
396+ return output
396397
397398 @self .forecasting_agent .tool
398399 async def cross_validation_tool (
@@ -429,6 +430,7 @@ async def cross_validation_tool(
429430 ["metric" ],
430431 as_index = False ,
431432 ).mean (numeric_only = True )
433+ self .eval_df = eval_df
432434 return ", " .join (
433435 [
434436 f"{ model .alias } : { eval_df [model .alias ].iloc [0 ]} "
@@ -437,19 +439,25 @@ async def cross_validation_tool(
437439 )
438440
439441 @self .forecasting_agent .tool
440- async def forecast_tool (ctx : RunContext [ExperimentDataset ], model : str ) -> str :
442+ async def forecast_tool (
443+ ctx : RunContext [ExperimentDataset ],
444+ model : str ,
445+ ) -> str :
441446 callable_model = MODELS [model ]
442447 fcst_df = callable_model .forecast (
443448 df = ctx .deps .df ,
444449 h = ctx .deps .h ,
445450 freq = ctx .deps .freq ,
446451 )
447- output = "," .join (
448- [
449- f"{ row ['ds' ].strftime ('%Y-%m-%d' )} : { row [model ]} "
450- for _ , row in fcst_df .iterrows ()
451- ]
452+ df_agg = fcst_df .groupby ("unique_id" ).agg (list )
453+ output = (
454+ "these are the forecasted values in json format where the key is the "
455+ "identifier of the time series and the values is also a json of two "
456+ "elements: the first element is the date column and the second "
457+ "element is the value column."
458+ f"{ df_agg .to_json (orient = 'index' )} "
452459 )
460+ self .fcst_df = fcst_df
453461 return output
454462
455463 @self .forecasting_agent .output_validator
@@ -519,5 +527,7 @@ def forecast(
519527 user_prompt = query ,
520528 deps = dataset ,
521529 )
522-
530+ result .fcst_df = self .fcst_df
531+ result .eval_df = self .eval_df
532+ result .features_df = self .features_df
523533 return result
0 commit comments