diff --git a/devnotes/v2.0.0/sprint-trackers/asr-profile-provider-frontend-sprint-tracker.md b/devnotes/v2.0.0/sprint-trackers/asr-profile-provider-frontend-sprint-tracker.md index 438aec98..d32bbe4e 100644 --- a/devnotes/v2.0.0/sprint-trackers/asr-profile-provider-frontend-sprint-tracker.md +++ b/devnotes/v2.0.0/sprint-trackers/asr-profile-provider-frontend-sprint-tracker.md @@ -2,7 +2,7 @@ Run ID: `ASR-PROFILE-FE` -Status: completed through ASR-PROFILE-FE-Sprint 2. +Status: completed through ASR-PROFILE-FE-Sprint 3. This tracker belongs to `devnotes/v2.0.0/sprint-plans/asr-profile-provider-frontend-sprint-plan.md`. @@ -201,32 +201,36 @@ Commit: ## ASR-PROFILE-FE-Sprint 3: Profile Dialog Revamp -Status: pending +Status: completed Planned tasks: -- [ ] Keep profile name, description, and default toggle. -- [ ] Render a transcription step selector using transcription model cards. -- [ ] Save the first pipeline step as `kind`, `provider`, `model`, `model_family` if required, and `options`. -- [ ] Add an optional diarization step toggle. -- [ ] Render diarization parameters from a diarization model schema where available. -- [ ] Remove hard-coded language/task/thread/tail-padding/chunking/diarization controls from `ASRProfileDialog.tsx`. -- [ ] Add compact summaries for selected model, installed/download state, reload-required changes, and advanced fields. +- [x] Keep profile name, description, and default toggle. +- [x] Render a transcription step selector using transcription model cards. +- [x] Save the first pipeline step as `kind`, `provider`, `model`, `model_family` if required, and `options`. +- [x] Add an optional diarization step toggle. +- [x] Render diarization parameters from a diarization model schema where available. +- [x] Remove hard-coded language/task/thread/tail-padding/chunking/diarization controls from `ASRProfileDialog.tsx`. +- [x] Add compact summaries for selected model, installed/download state, reload-required changes, and advanced fields. Acceptance checks: -- [ ] The dialog exposes every parameter in the selected transcription model schema. -- [ ] Parakeet TDT v2/v3 show Parakeet-supported parameters and no Whisper-only parameters. -- [ ] Whisper models show Whisper-specific language/task/timestamp controls. -- [ ] Diarization is descriptor-driven or explicitly blocked pending a backend model-card endpoint. +- [x] The dialog exposes every parameter in the selected transcription model schema. +- [x] Parakeet TDT v2/v3 show Parakeet-supported parameters and no Whisper-only parameters. +- [x] Whisper models show Whisper-specific language/task/timestamp controls. +- [x] Diarization is descriptor-driven or explicitly blocked pending a backend model-card endpoint. Verification: -- [ ] Pending. +- [x] `npm --prefix web/frontend run build` +- [x] `npm --prefix web/frontend run lint` (passes with existing warnings outside this sprint) +- [x] `git diff --check -- web/frontend/src/features/settings/components/ASRProfileDialog.tsx web/frontend/src/features/settings/pages/SettingsPage.tsx devnotes/v2.0.0/sprint-trackers/asr-profile-provider-frontend-sprint-tracker.md` Artifacts: -- Pending. +- `web/frontend/src/features/settings/components/ASRProfileDialog.tsx` +- `web/frontend/src/features/settings/pages/SettingsPage.tsx` +- `devnotes/v2.0.0/sprint-trackers/asr-profile-provider-frontend-sprint-tracker.md` Commit: diff --git a/web/frontend/src/features/settings/components/ASRProfileDialog.tsx b/web/frontend/src/features/settings/components/ASRProfileDialog.tsx index 6bffc577..cb254c47 100644 --- a/web/frontend/src/features/settings/components/ASRProfileDialog.tsx +++ b/web/frontend/src/features/settings/components/ASRProfileDialog.tsx @@ -7,12 +7,16 @@ import { type TranscriptionProfile, type TranscriptionProfileOptions, normalizeProfileOptions, + type ASRStep, } from "../api/profilesApi"; +import { ASRParameterForm } from "./ASRParameterForm"; +import { resolveParameterValues, sanitizeParameterValues } from "./asrParameterValues"; type ASRProfileDialogProps = { open: boolean; profile: TranscriptionProfile | null; models: TranscriptionModel[]; + diarizationModels: TranscriptionModel[]; onClose: () => void; onSave: (profile: { id?: string; @@ -30,7 +34,7 @@ const fallbackModels: TranscriptionModel[] = [ { id: "parakeet-v3", display_name: "NVIDIA Parakeet TDT v3", provider: "local", installed: false, default: false, capabilities: { transcription: true, word_timestamps: true } }, ]; -export function ASRProfileDialog({ open, profile, models, onClose, onSave }: ASRProfileDialogProps) { +export function ASRProfileDialog({ open, profile, models, diarizationModels, onClose, onSave }: ASRProfileDialogProps) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [isDefault, setIsDefault] = useState(false); @@ -38,6 +42,7 @@ export function ASRProfileDialog({ open, profile, models, onClose, onSave }: ASR const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const availableModels = models.length ? models : fallbackModels; + const availableDiarizationModels = diarizationModels; const modelOptions = useMemo(() => { return availableModels.map((model) => ({ @@ -48,7 +53,11 @@ export function ASRProfileDialog({ open, profile, models, onClose, onSave }: ASR }, [availableModels]); const transcriptionStep = options.pipeline.find((step) => step.kind === "transcription"); + const diarizationStep = options.pipeline.find((step) => step.kind === "diarization"); const selectedModelID = transcriptionStep?.model || availableModels.find((model) => model.default)?.id || availableModels[0]?.id || ""; + const selectedModel = availableModels.find((model) => model.id === selectedModelID) || null; + const selectedDiarizationModelID = diarizationStep?.model || availableDiarizationModels.find((model) => model.default)?.id || availableDiarizationModels[0]?.id || ""; + const selectedDiarizationModel = availableDiarizationModels.find((model) => model.id === selectedDiarizationModelID) || null; useEffect(() => { if (!open) return; @@ -65,6 +74,27 @@ export function ASRProfileDialog({ open, profile, models, onClose, onSave }: ASR setOptions((current) => withTranscriptionModel(current, availableModels, modelID)); }; + const updateTranscriptionOptions = (values: Record) => { + setOptions((current) => updateStepOptions(current, "transcription", values)); + }; + + const updateDiarizationModel = (modelID: string) => { + setOptions((current) => withDiarizationModel(current, availableDiarizationModels, modelID)); + }; + + const updateDiarizationOptions = (values: Record) => { + setOptions((current) => updateStepOptions(current, "diarization", values)); + }; + + const toggleDiarization = (enabled: boolean) => { + setOptions((current) => { + if (!enabled) { + return { pipeline: current.pipeline.filter((step) => step.kind !== "diarization") }; + } + return withDiarizationModel(current, availableDiarizationModels, selectedDiarizationModelID); + }); + }; + const submit = async () => { const cleanName = name.trim(); if (!cleanName) { @@ -79,7 +109,7 @@ export function ASRProfileDialog({ open, profile, models, onClose, onSave }: ASR name: cleanName, description: description.trim(), is_default: isDefault, - options: ensureTranscriptionStep(options, availableModels), + options: prepareProfileOptionsForSave(options, availableModels, availableDiarizationModels), }); onClose(); } catch (err) { @@ -120,6 +150,31 @@ export function ASRProfileDialog({ open, profile, models, onClose, onSave }: ASR + + + +
+

Diarization

+ + {diarizationStep && availableDiarizationModels.length > 0 ? ( +
+ +
+ ) : null} + {diarizationStep && availableDiarizationModels.length === 0 ?
No diarization model card is available.
: null} +
+ + {diarizationStep ? ( + + ) : null}