mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-30 18:09:05 -04:00
* feat(ui): add voice library workflow Give administrators a production-ready flow to record or upload consented reference audio, manage reusable profiles, inspect API usage, discover compatible models, and hand a saved voice directly to text-to-speech. Assisted-by: Codex:gpt-5 * feat(voice): add managed voice cloning profiles Make reusable reference voices manageable through the admin API instead of requiring model-directory and YAML edits. Discover compatible installed and gallery models from server-side backend capabilities, retain explicit model configuration controls, and stage saved references for supported backends. Expose profile management through REST and MCP, document backend-specific behavior, and cover the workflow from profile creation through real Qwen3-TTS synthesis. Harden the agent-job HTTP test against completion racing cancellation. Assisted-by: Codex:gpt-5 --------- Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
55 lines
2.1 KiB
Go
55 lines
2.1 KiB
Go
package localaitools
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
func registerVoiceProfileTools(s *mcp.Server, client LocalAIClient, opts Options) {
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: ToolListVoiceProfiles,
|
|
Description: "List reusable voice-cloning profiles. Returns stable voice URI values suitable for TTSRequest.voice; filesystem paths are never exposed.",
|
|
}, func(ctx context.Context, _ *mcp.CallToolRequest, _ struct{}) (*mcp.CallToolResult, any, error) {
|
|
profiles, err := client.ListVoiceProfiles(ctx)
|
|
if err != nil {
|
|
return errorResult(err), nil, nil
|
|
}
|
|
return jsonResult(profiles), nil, nil
|
|
})
|
|
|
|
if opts.DisableMutating {
|
|
return
|
|
}
|
|
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: ToolCreateVoiceProfile,
|
|
Description: "Create a reusable voice-cloning profile from a base64 16-bit PCM WAV (mono 24 kHz recommended) and its exact transcript. consent_confirmed must be true. Requires user confirmation per safety rule 1.",
|
|
}, func(ctx context.Context, _ *mcp.CallToolRequest, args CreateVoiceProfileRequest) (*mcp.CallToolResult, any, error) {
|
|
if args.Name == "" || args.Transcript == "" || args.AudioBase64 == "" {
|
|
return errorResultf("name, transcript, and audio_base64 are required"), nil, nil
|
|
}
|
|
if !args.ConsentConfirmed {
|
|
return errorResultf("consent_confirmed must be true"), nil, nil
|
|
}
|
|
profile, err := client.CreateVoiceProfile(ctx, args)
|
|
if err != nil {
|
|
return errorResult(err), nil, nil
|
|
}
|
|
return jsonResult(profile), nil, nil
|
|
})
|
|
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: ToolDeleteVoiceProfile,
|
|
Description: "Permanently delete a reusable voice-cloning profile by opaque UUID. Requires user confirmation per safety rule 1.",
|
|
}, func(ctx context.Context, _ *mcp.CallToolRequest, args DeleteVoiceProfileRequest) (*mcp.CallToolResult, any, error) {
|
|
if args.ID == "" {
|
|
return errorResultf("id is required"), nil, nil
|
|
}
|
|
if err := client.DeleteVoiceProfile(ctx, args.ID); err != nil {
|
|
return errorResult(err), nil, nil
|
|
}
|
|
return jsonResult(map[string]any{"deleted": true, "id": args.ID}), nil, nil
|
|
})
|
|
}
|