Refactor oneliner storage to use enum
The oneliner is now stored as `OnelinerState` enum which can represent three states: `Ready`, `Empty` or `Error`. This allows the client to differentiate between a case with no medical content and a case where the oneliner generation failed. The `OnelinerState` enum is serialized to JSON and stored in `oneliner.json` file. The `OnelinerEntry` struct has been updated to reflect this change. The client-desktop application has been updated to handle the new `OnelinerState` enum and display appropriate UI elements for each state. The server-side code has also been updated to read and write the `OnelinerState` enum, and to handle the new file format. The `reset_case_artefacts` function in `server/src/routes/case_actions.rs` has been updated to remove `oneliner.txt` and create `oneliner.json` instead. The tests have been updated to reflect these changes.
This commit is contained in:
@@ -9,6 +9,40 @@ use serde::{Deserialize, Serialize};
|
||||
/// HTTP path of the oneliners endpoint.
|
||||
pub const ONELINERS_PATH: &str = "/api/oneliners";
|
||||
|
||||
/// Filename used by the server to persist [`OnelinerState`] per case
|
||||
/// (inside the case directory). Shared so both writer and readers
|
||||
/// agree on the exact path.
|
||||
pub const ONELINER_FILENAME: &str = "oneliner.json";
|
||||
|
||||
/// Outcome of the LLM oneliner generation for a single case.
|
||||
///
|
||||
/// Three disjoint outcomes used to collapse onto the same filesystem
|
||||
/// representation (missing file). Making them first-class lets the UI
|
||||
/// distinguish "LLM said nothing medical" (valid empty result) from
|
||||
/// "LLM call errored" (transient failure worth retrying) from "file
|
||||
/// simply isn't there yet" (no state persisted).
|
||||
///
|
||||
/// Serialized as internally-tagged JSON: `{"kind":"ready","text":...}`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(tag = "kind", rename_all = "lowercase")]
|
||||
pub enum OnelinerState {
|
||||
/// LLM produced a usable one-sentence summary.
|
||||
Ready {
|
||||
text: String,
|
||||
/// RFC3339 UTC timestamp of the LLM call that produced this
|
||||
/// text. Diagnostic only — the file mtime drives ETag/sort.
|
||||
generated_at: String,
|
||||
},
|
||||
/// LLM deliberately returned an empty answer — the transcript
|
||||
/// contained no medical content. This is a valid outcome, not a
|
||||
/// failure, and recovery must not retry it.
|
||||
Empty { generated_at: String },
|
||||
/// LLM call failed (timeout, HTTP, parse, …). Details stay in the
|
||||
/// server logs on purpose; the UI surfaces only "error", and
|
||||
/// startup recovery retries this variant.
|
||||
Error { generated_at: String },
|
||||
}
|
||||
|
||||
/// Full response body of `GET /api/oneliners`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OnelinersResponse {
|
||||
@@ -29,14 +63,14 @@ pub struct OnelinersResponse {
|
||||
/// including addenda). This is the **primary sort key** for clients:
|
||||
/// "when did the doctor last work on this case?". Absent only if the
|
||||
/// case has no recordings at all (shouldn't happen in normal flow).
|
||||
/// - `updated_at` — `oneliner.txt` mtime, i.e. when the oneliner text
|
||||
/// was last regenerated. Diagnostic only — not for sort or display.
|
||||
/// - `oneliner` — the text itself; `None` while transcription / LLM
|
||||
/// generation is still pending.
|
||||
/// - `updated_at` — `oneliner.json` mtime, i.e. when the oneliner state
|
||||
/// was last written. Diagnostic only — not for sort or display.
|
||||
/// - `oneliner` — the persisted state; `None` while no state file has
|
||||
/// been written yet for this case (fresh case, nothing transcribed).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OnelinerEntry {
|
||||
pub case_id: String,
|
||||
pub oneliner: Option<String>,
|
||||
pub oneliner: Option<OnelinerState>,
|
||||
pub created_at: String,
|
||||
pub last_recording_at: Option<String>,
|
||||
pub updated_at: Option<String>,
|
||||
|
||||
Reference in New Issue
Block a user