From 837f8525d89aec2faa4318630b58b889e2eb7d92 Mon Sep 17 00:00:00 2001 From: James Pine Date: Fri, 13 Mar 2026 06:35:39 -0700 Subject: [PATCH] feat: add auto-chunking limit slider to settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Persisted setting (default 800 chars) controls how long text is split before generation. Lower values improve quality for long outputs by keeping each chunk well within the model's context window. - Slider in Server Connection settings (100–2000 chars, step 50) - Stored in localStorage via Zustand persist - Passed as max_chunk_chars on every generation request - Frontend text limit raised to 50,000 to match backend --- .../ServerSettings/ConnectionForm.tsx | 39 +++++++++++++++---- app/src/lib/api/types.ts | 1 + app/src/lib/hooks/useGenerationForm.ts | 5 ++- app/src/stores/serverStore.ts | 6 +++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/app/src/components/ServerSettings/ConnectionForm.tsx b/app/src/components/ServerSettings/ConnectionForm.tsx index 44eeb4e6..a5521870 100644 --- a/app/src/components/ServerSettings/ConnectionForm.tsx +++ b/app/src/components/ServerSettings/ConnectionForm.tsx @@ -4,6 +4,7 @@ import { useForm } from 'react-hook-form'; import * as z from 'zod'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Checkbox } from '@/components/ui/checkbox'; import { Form, FormControl, @@ -14,10 +15,10 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { Checkbox } from '@/components/ui/checkbox'; +import { Slider } from '@/components/ui/slider'; import { useToast } from '@/components/ui/use-toast'; -import { useServerStore } from '@/stores/serverStore'; import { usePlatform } from '@/platform/PlatformContext'; +import { useServerStore } from '@/stores/serverStore'; const connectionSchema = z.object({ serverUrl: z.string().url('Please enter a valid URL'), @@ -33,6 +34,8 @@ export function ConnectionForm() { const setKeepServerRunningOnClose = useServerStore((state) => state.setKeepServerRunningOnClose); const mode = useServerStore((state) => state.mode); const setMode = useServerStore((state) => state.setMode); + const maxChunkChars = useServerStore((state) => state.maxChunkChars); + const setMaxChunkChars = useServerStore((state) => state.setMaxChunkChars); const { toast } = useToast(); const form = useForm({ @@ -59,11 +62,7 @@ export function ConnectionForm() { } return ( - + Server Connection @@ -153,6 +152,32 @@ export function ConnectionForm() { )} + +
+
+
+ + + {maxChunkChars} chars + +
+ setMaxChunkChars(value)} + min={100} + max={2000} + step={50} + aria-label="Auto-chunking character limit" + /> +

+ Long text is split into chunks at sentence boundaries before generating. Lower values + can improve quality for long outputs. Default is 800. +

+
+
); diff --git a/app/src/lib/api/types.ts b/app/src/lib/api/types.ts index fe5f05a1..d8ad6b13 100644 --- a/app/src/lib/api/types.ts +++ b/app/src/lib/api/types.ts @@ -36,6 +36,7 @@ export interface GenerationRequest { model_size?: '1.7B' | '0.6B'; engine?: 'qwen' | 'luxtts' | 'chatterbox' | 'chatterbox_turbo'; instruct?: string; + max_chunk_chars?: number; } export interface GenerationResponse { diff --git a/app/src/lib/hooks/useGenerationForm.ts b/app/src/lib/hooks/useGenerationForm.ts index 5a83ce41..e1244d22 100644 --- a/app/src/lib/hooks/useGenerationForm.ts +++ b/app/src/lib/hooks/useGenerationForm.ts @@ -9,9 +9,10 @@ import { useGeneration } from '@/lib/hooks/useGeneration'; import { useModelDownloadToast } from '@/lib/hooks/useModelDownloadToast'; import { useGenerationStore } from '@/stores/generationStore'; import { usePlayerStore } from '@/stores/playerStore'; +import { useServerStore } from '@/stores/serverStore'; const generationSchema = z.object({ - text: z.string().min(1, 'Text is required').max(5000), + text: z.string().min(1, 'Text is required').max(50000), language: z.enum(LANGUAGE_CODES as [LanguageCode, ...LanguageCode[]]), seed: z.number().int().optional(), modelSize: z.enum(['1.7B', '0.6B']).optional(), @@ -31,6 +32,7 @@ export function useGenerationForm(options: UseGenerationFormOptions = {}) { const generation = useGeneration(); const setAudioWithAutoPlay = usePlayerStore((state) => state.setAudioWithAutoPlay); const setIsGenerating = useGenerationStore((state) => state.setIsGenerating); + const maxChunkChars = useServerStore((state) => state.maxChunkChars); const [downloadingModelName, setDownloadingModelName] = useState(null); const [downloadingDisplayName, setDownloadingDisplayName] = useState(null); @@ -110,6 +112,7 @@ export function useGenerationForm(options: UseGenerationFormOptions = {}) { model_size: isQwen ? data.modelSize : undefined, engine, instruct: isQwen ? data.instruct || undefined : undefined, + max_chunk_chars: maxChunkChars, }); toast({ diff --git a/app/src/stores/serverStore.ts b/app/src/stores/serverStore.ts index 36d9f0af..9d4ad89c 100644 --- a/app/src/stores/serverStore.ts +++ b/app/src/stores/serverStore.ts @@ -13,6 +13,9 @@ interface ServerStore { keepServerRunningOnClose: boolean; setKeepServerRunningOnClose: (keepRunning: boolean) => void; + + maxChunkChars: number; + setMaxChunkChars: (value: number) => void; } export const useServerStore = create()( @@ -29,6 +32,9 @@ export const useServerStore = create()( keepServerRunningOnClose: false, setKeepServerRunningOnClose: (keepRunning) => set({ keepServerRunningOnClose: keepRunning }), + + maxChunkChars: 800, + setMaxChunkChars: (value) => set({ maxChunkChars: value }), }), { name: 'voicebox-server',