1use std::collections::BTreeMap;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use serde::Serialize;
5
6use crate::forecast::QuantileMatrix;
7
8#[derive(Serialize)]
9#[serde(untagged)]
10pub enum ForecastOutput {
11 Univariate {
12 point: Vec<f32>,
13 quantiles: BTreeMap<String, Vec<f32>>,
14 },
15 Multivariate {
16 variates: Vec<VariateForecast>,
17 },
18}
19
20#[derive(Serialize)]
21pub struct VariateForecast {
22 pub point: Vec<f32>,
23 pub quantiles: BTreeMap<String, Vec<f32>>,
24}
25
26pub fn quantile_matrix_to_output(
33 qmat: &QuantileMatrix,
34 quantile_levels: &[f32],
35 median_idx: usize,
36) -> ForecastOutput {
37 let n_var = qmat.first().map(|q| q.len()).unwrap_or(0);
38
39 let quantiles_for = |vi: usize| -> BTreeMap<String, Vec<f32>> {
40 let mut quantiles = BTreeMap::new();
41 for (qi, &level) in quantile_levels.iter().enumerate() {
42 if let Some(row) = qmat.get(qi) {
43 if let Some(q) = row.get(vi) {
44 quantiles.insert(format!("{level:.2}"), q.clone());
45 }
46 }
47 }
48 quantiles
49 };
50 let point_for = |vi: usize| -> Vec<f32> {
51 qmat.get(median_idx).and_then(|v| v.get(vi)).cloned().unwrap_or_default()
52 };
53
54 if n_var <= 1 {
55 ForecastOutput::Univariate {
56 point: point_for(0),
57 quantiles: quantiles_for(0),
58 }
59 } else {
60 let variates = (0..n_var)
61 .map(|vi| VariateForecast { point: point_for(vi), quantiles: quantiles_for(vi) })
62 .collect();
63 ForecastOutput::Multivariate { variates }
64 }
65}
66
67pub fn forecast_response_json(
70 model_name: &str,
71 context_length: usize,
72 forecast_length: usize,
73 outputs: Vec<ForecastOutput>,
74) -> anyhow::Result<String> {
75 #[derive(Serialize)]
76 struct ForecastResponse {
77 id: String,
78 object: &'static str,
79 created: u64,
80 model: String,
81 choices: Vec<Choice>,
82 usage: Usage,
83 }
84 #[derive(Serialize)]
85 struct Choice {
86 index: usize,
87 forecast: ForecastOutput,
88 finish_reason: &'static str,
89 }
90 #[derive(Serialize)]
91 struct Usage {
92 context_length: usize,
93 forecast_length: usize,
94 }
95
96 let created = SystemTime::now()
97 .duration_since(UNIX_EPOCH)
98 .unwrap_or_default()
99 .as_secs();
100
101 let resp = ForecastResponse {
102 id: format!("forecast-{created:016x}"),
103 object: "forecast",
104 created,
105 model: model_name.to_string(),
106 choices: outputs
107 .into_iter()
108 .enumerate()
109 .map(|(i, forecast)| Choice { index: i, forecast, finish_reason: "stop" })
110 .collect(),
111 usage: Usage { context_length, forecast_length },
112 };
113
114 Ok(serde_json::to_string_pretty(&resp)?)
115}