use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
pub use super::pipeline_run::PipelineRunInfoDTO;
#[derive(Debug, Default)]
pub struct RememberFormDTO {
pub dataset_name: Option<String>,
pub dataset_id: super::util::DatasetIdRef,
pub node_set: Option<Vec<String>>,
pub run_in_background: Option<bool>,
pub custom_prompt: Option<String>,
pub chunks_per_batch: Option<u32>,
pub session_id: Option<String>,
}
pub struct UploadedFilePart {
pub file_name: Option<String>,
pub content_type: Option<String>,
pub temp_path: std::path::PathBuf,
pub byte_count: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
pub enum WireRememberStatus {
#[serde(rename = "running")]
Running,
#[serde(rename = "completed")]
Completed,
#[serde(rename = "errored")]
Errored,
#[serde(rename = "session_stored")]
SessionStored,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct RememberItemDTO {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub token_count: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct RememberResultDTO {
pub status: WireRememberStatus,
pub pipeline_run_id: Option<uuid::Uuid>,
pub dataset_id: Option<uuid::Uuid>,
pub dataset_name: String,
pub items_processed: u32,
pub elapsed_seconds: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Vec<RememberItemDTO>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entry_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entry_id: Option<String>,
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code — panics are acceptable failures"
)]
mod tests {
use super::*;
#[test]
fn wire_remember_status_serde_roundtrip() {
let cases = [
(WireRememberStatus::Running, "\"running\""),
(WireRememberStatus::Completed, "\"completed\""),
(WireRememberStatus::Errored, "\"errored\""),
(WireRememberStatus::SessionStored, "\"session_stored\""),
];
for (variant, expected) in cases {
let json = serde_json::to_string(&variant).expect("serialize");
assert_eq!(json, expected, "variant {variant:?} → {expected}");
let parsed: WireRememberStatus = serde_json::from_str(expected).expect("deserialize");
assert_eq!(parsed, variant, "round-trip {expected}");
}
}
#[test]
fn remember_result_dto_minimal_wire_shape() {
let dto = RememberResultDTO {
status: WireRememberStatus::Completed,
pipeline_run_id: None,
dataset_id: None,
dataset_name: "ds".into(),
items_processed: 0,
elapsed_seconds: None,
session_ids: None,
content_hash: None,
items: None,
error: None,
entry_type: None,
entry_id: None,
};
let v = serde_json::to_value(&dto).expect("to_value");
let obj = v.as_object().expect("object");
assert_eq!(obj["status"], "completed");
assert!(obj.contains_key("pipeline_run_id"));
assert!(obj["pipeline_run_id"].is_null());
assert!(obj.contains_key("dataset_id"));
assert!(obj["dataset_id"].is_null());
assert_eq!(obj["dataset_name"], "ds");
assert_eq!(obj["items_processed"], 0);
assert!(obj.contains_key("elapsed_seconds"));
assert!(obj["elapsed_seconds"].is_null());
assert!(!obj.contains_key("session_ids"));
assert!(!obj.contains_key("content_hash"));
assert!(!obj.contains_key("items"));
assert!(!obj.contains_key("error"));
assert!(!obj.contains_key("entry_type"));
assert!(!obj.contains_key("entry_id"));
}
#[test]
fn remember_result_dto_populated_wire_shape() {
let dto = RememberResultDTO {
status: WireRememberStatus::SessionStored,
pipeline_run_id: None,
dataset_id: None,
dataset_name: "ds".into(),
items_processed: 3,
elapsed_seconds: Some(1.25),
session_ids: Some(vec!["sess-1".into()]),
content_hash: Some("abc123".into()),
items: Some(vec![RememberItemDTO {
name: Some("doc.txt".into()),
content_hash: Some("hash".into()),
token_count: Some(42),
}]),
error: None,
entry_type: None,
entry_id: None,
};
let v = serde_json::to_value(&dto).expect("to_value");
let obj = v.as_object().expect("object");
assert_eq!(obj["status"], "session_stored");
assert_eq!(obj["items_processed"], 3);
assert_eq!(obj["elapsed_seconds"], 1.25);
assert_eq!(obj["session_ids"][0], "sess-1");
assert_eq!(obj["content_hash"], "abc123");
let items = obj["items"].as_array().expect("items array");
assert_eq!(items[0]["name"], "doc.txt");
assert_eq!(items[0]["content_hash"], "hash");
assert_eq!(items[0]["token_count"], 42);
assert!(!obj.contains_key("entry_type"));
assert!(!obj.contains_key("entry_id"));
}
#[test]
fn remember_result_dto_serializes_entry_fields_when_set() {
let dto = RememberResultDTO {
status: WireRememberStatus::SessionStored,
pipeline_run_id: None,
dataset_id: None,
dataset_name: "main_dataset".into(),
items_processed: 0,
elapsed_seconds: Some(0.01),
session_ids: Some(vec!["sess-1".into()]),
content_hash: None,
items: None,
error: None,
entry_type: Some("qa".into()),
entry_id: Some("qa-abc-123".into()),
};
let v = serde_json::to_value(&dto).expect("to_value");
let obj = v.as_object().expect("object");
assert_eq!(obj["status"], "session_stored");
assert_eq!(obj["entry_type"], "qa");
assert_eq!(obj["entry_id"], "qa-abc-123");
}
}