mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-18 06:10:43 -07:00
feat: model management improvements and folder migration
- Add model folder migration with byte-level progress tracking (backend + UI) - Custom models directory support via VOICEBOX_MODELS_DIR env var passed to sidecar - Hardcoded model descriptions displayed in model detail cards - Open model folder button in storage location row - Remove 'not downloaded' badge from model cards - Fix server settings scroll offset for audio player - Fix shell open permission to allow file paths - Add normalize toggle to generation settings
This commit is contained in:
@@ -316,6 +316,21 @@ class ApiClient {
|
||||
return this.request<ModelStatusListResponse>('/models/status');
|
||||
}
|
||||
|
||||
async getModelsCacheDir(): Promise<{ path: string }> {
|
||||
return this.request<{ path: string }>('/models/cache-dir');
|
||||
}
|
||||
|
||||
async migrateModels(destination: string): Promise<{ source: string; destination: string }> {
|
||||
return this.request('/models/migrate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ destination }),
|
||||
});
|
||||
}
|
||||
|
||||
getMigrationProgressUrl(): string {
|
||||
return `${this.getBaseUrl()}/models/migrate/progress`;
|
||||
}
|
||||
|
||||
async triggerModelDownload(modelName: string): Promise<{ message: string }> {
|
||||
console.log(
|
||||
'[API] triggerModelDownload called for:',
|
||||
|
||||
@@ -38,6 +38,7 @@ export interface GenerationRequest {
|
||||
instruct?: string;
|
||||
max_chunk_chars?: number;
|
||||
crossfade_ms?: number;
|
||||
normalize?: boolean;
|
||||
}
|
||||
|
||||
export interface GenerationResponse {
|
||||
|
||||
@@ -34,6 +34,7 @@ export function useGenerationForm(options: UseGenerationFormOptions = {}) {
|
||||
const setIsGenerating = useGenerationStore((state) => state.setIsGenerating);
|
||||
const maxChunkChars = useServerStore((state) => state.maxChunkChars);
|
||||
const crossfadeMs = useServerStore((state) => state.crossfadeMs);
|
||||
const normalizeAudio = useServerStore((state) => state.normalizeAudio);
|
||||
const [downloadingModelName, setDownloadingModelName] = useState<string | null>(null);
|
||||
const [downloadingDisplayName, setDownloadingDisplayName] = useState<string | null>(null);
|
||||
|
||||
@@ -115,6 +116,7 @@ export function useGenerationForm(options: UseGenerationFormOptions = {}) {
|
||||
instruct: isQwen ? data.instruct || undefined : undefined,
|
||||
max_chunk_chars: maxChunkChars,
|
||||
crossfade_ms: crossfadeMs,
|
||||
normalize: normalizeAudio,
|
||||
});
|
||||
|
||||
toast({
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { apiClient } from '@/lib/api/client';
|
||||
import { useGenerationStore } from '@/stores/generationStore';
|
||||
import type { ActiveDownloadTask } from '@/lib/api/types';
|
||||
import { useGenerationStore } from '@/stores/generationStore';
|
||||
|
||||
// Polling interval in milliseconds
|
||||
const POLL_INTERVAL = 2000;
|
||||
const POLL_INTERVAL = 30000;
|
||||
|
||||
/**
|
||||
* Hook to monitor active tasks (downloads and generations).
|
||||
* Polls the server periodically to catch downloads triggered from anywhere
|
||||
* (transcription, generation, explicit download, etc.).
|
||||
*
|
||||
*
|
||||
* Returns the active downloads so components can render download toasts.
|
||||
*/
|
||||
export function useRestoreActiveTasks() {
|
||||
const [activeDownloads, setActiveDownloads] = useState<ActiveDownloadTask[]>([]);
|
||||
const setIsGenerating = useGenerationStore((state) => state.setIsGenerating);
|
||||
const setActiveGenerationId = useGenerationStore((state) => state.setActiveGenerationId);
|
||||
|
||||
|
||||
// Track which downloads we've seen to detect new ones
|
||||
const seenDownloadsRef = useRef<Set<string>>(new Set());
|
||||
|
||||
@@ -41,14 +41,14 @@ export function useRestoreActiveTasks() {
|
||||
// Update active downloads
|
||||
// Keep track of all active downloads (including new ones)
|
||||
const currentDownloadNames = new Set(tasks.downloads.map((d) => d.model_name));
|
||||
|
||||
|
||||
// Remove completed downloads from our seen set
|
||||
for (const name of seenDownloadsRef.current) {
|
||||
if (!currentDownloadNames.has(name)) {
|
||||
seenDownloadsRef.current.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add new downloads to seen set
|
||||
for (const download of tasks.downloads) {
|
||||
seenDownloadsRef.current.add(download.model_name);
|
||||
|
||||
Reference in New Issue
Block a user