Refactor OnelinerDisplay states
Introduce `Generating` state and clarify `Empty` and `Missing` states. The `Pending` state now specifically refers to transcription being in progress. The `Generating` state covers the period after transcription is complete but before the LLM has produced the final oneliner state, or when a new state needs to be generated due to new recordings. The `Missing` state is collapsed into `Empty` as the UI treats them identically when no title is expected.
This commit is contained in:
@@ -33,18 +33,23 @@ use crate::transcribe::recovery as transcribe_recovery;
|
|||||||
/// exposes the raw [`OnelinerState`] and lets each client decide how
|
/// exposes the raw [`OnelinerState`] and lets each client decide how
|
||||||
/// to render transit states.
|
/// to render transit states.
|
||||||
enum OnelinerDisplay {
|
enum OnelinerDisplay {
|
||||||
|
/// LLM produced a usable title.
|
||||||
Ready(String),
|
Ready(String),
|
||||||
|
/// Stable "no title to expect" — either the LLM returned nothing
|
||||||
|
/// (no medical content in transcript) or every recording failed so
|
||||||
|
/// nothing can ever generate a title. UI treats both identically;
|
||||||
|
/// the reason is uninteresting to the doctor in the list view.
|
||||||
Empty,
|
Empty,
|
||||||
|
/// LLM call failed with transcripts available.
|
||||||
Error,
|
Error,
|
||||||
/// No state file on disk + at least one non-failed recording is
|
/// Transcription still running — a title cannot exist yet.
|
||||||
/// still awaiting its transcript. The oneliner will appear once
|
|
||||||
/// transcription finishes.
|
|
||||||
Pending,
|
Pending,
|
||||||
/// No state file on disk and no pending transcription. Rare in
|
/// Transcripts are done (or about to land) but the LLM hasn't
|
||||||
/// practice — the worker writes a state at the end of every job —
|
/// written a state yet, OR a previous state is obsolete because new
|
||||||
/// but possible right after a server crash between transcript and
|
/// recordings arrived. The worker is generating right now or will
|
||||||
/// oneliner write.
|
/// generate as soon as the last transcript is in. Covers the
|
||||||
Missing,
|
/// reset-just-clicked case and the post-restart recovery window.
|
||||||
|
Generating,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserCaseView {
|
struct UserCaseView {
|
||||||
@@ -527,12 +532,32 @@ async fn compute_case_view(
|
|||||||
.all(|r| r.transcript.is_some());
|
.all(|r| r.transcript.is_some());
|
||||||
|
|
||||||
let (state, _) = paths::read_oneliner_state(case_path).await;
|
let (state, _) = paths::read_oneliner_state(case_path).await;
|
||||||
let oneliner = match state {
|
let oneliner = if non_failed_count == 0 {
|
||||||
Some(OnelinerState::Ready { text, .. }) => OnelinerDisplay::Ready(text),
|
// All recordings failed — stable end state, no LLM will ever run.
|
||||||
Some(OnelinerState::Empty { .. }) => OnelinerDisplay::Empty,
|
// Missing state collapses to Empty ("unbenannt"): no title is
|
||||||
Some(OnelinerState::Error { .. }) => OnelinerDisplay::Error,
|
// expected, regardless of why.
|
||||||
None if !all_transcribed => OnelinerDisplay::Pending,
|
match state {
|
||||||
None => OnelinerDisplay::Missing,
|
Some(OnelinerState::Ready { text, .. }) => OnelinerDisplay::Ready(text),
|
||||||
|
Some(OnelinerState::Error { .. }) => OnelinerDisplay::Error,
|
||||||
|
Some(OnelinerState::Empty { .. }) | None => OnelinerDisplay::Empty,
|
||||||
|
}
|
||||||
|
} else if all_transcribed {
|
||||||
|
// Transcripts done — stable state, except for the brief window
|
||||||
|
// between transcript-write and state-write where the worker is
|
||||||
|
// still producing its result.
|
||||||
|
match state {
|
||||||
|
Some(OnelinerState::Ready { text, .. }) => OnelinerDisplay::Ready(text),
|
||||||
|
Some(OnelinerState::Empty { .. }) => OnelinerDisplay::Empty,
|
||||||
|
Some(OnelinerState::Error { .. }) => OnelinerDisplay::Error,
|
||||||
|
None => OnelinerDisplay::Generating,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Transcription still pending. Any persisted state is obsolete:
|
||||||
|
// the worker will regenerate once the last transcript lands.
|
||||||
|
match state {
|
||||||
|
None => OnelinerDisplay::Pending,
|
||||||
|
Some(_) => OnelinerDisplay::Generating,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let has_document = any_document_exists(case_path).await;
|
let has_document = any_document_exists(case_path).await;
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ try {
|
|||||||
<div class="check"><input type="checkbox" name="case_id" value="{{ case.case_id }}"></div>
|
<div class="check"><input type="checkbox" name="case_id" value="{{ case.case_id }}"></div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<a href="/web/cases/{{ case.case_id }}">
|
<a href="/web/cases/{{ case.case_id }}">
|
||||||
<div class="line1"><span class="time"><time datetime="{{ case.recorded_at_iso }}">{{ case.time_hms_utc }}</time></span>{% match case.oneliner %}{% when OnelinerDisplay::Ready with (t) %} — {{ t }}{% when OnelinerDisplay::Empty %} — <span class="empty">kein medizinischer Inhalt</span>{% when OnelinerDisplay::Error %} — <span class="empty">Oneliner-Fehler</span>{% when OnelinerDisplay::Pending %} — <span class="pending">wird transkribiert …</span>{% when OnelinerDisplay::Missing %} — <span class="empty">unbenannt</span>{% endmatch %}</div>
|
<div class="line1"><span class="time"><time datetime="{{ case.recorded_at_iso }}">{{ case.time_hms_utc }}</time></span>{% match case.oneliner %}{% when OnelinerDisplay::Ready with (t) %} — {{ t }}{% when OnelinerDisplay::Empty %} — <span class="empty">unbenannt</span>{% when OnelinerDisplay::Error %} — <span class="empty">Fehler</span>{% when OnelinerDisplay::Pending %} — <span class="pending">transkribiere …</span>{% when OnelinerDisplay::Generating %} — <span class="pending">generiere Titel …</span>{% endmatch %}</div>
|
||||||
<div class="line2">{{ case.recordings_count }} Aufnahmen{% if case.has_document %} — <span class="status-badge done">ausgewertet</span>{% else %} — <span class="status-badge open">offen</span>{% endif %}{% if case.analyzing %} <span class="label analyzing">wird analysiert</span>{% endif %}</div>
|
<div class="line2">{{ case.recordings_count }} Aufnahmen{% if case.has_document %} — <span class="status-badge done">ausgewertet</span>{% else %} — <span class="status-badge open">offen</span>{% endif %}{% if case.analyzing %} <span class="label analyzing">wird analysiert</span>{% endif %}</div>
|
||||||
{% if is_admin %}<div class="uuid admin-only">{{ case.case_id }}</div>{% endif %}
|
{% if is_admin %}<div class="uuid admin-only">{{ case.case_id }}</div>{% endif %}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user