support POST to voices for custom voice creation - #657
Conversation
WalkthroughA single Gin route handler for Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
and this should not regress the existing functionality. i did some pretty thorough testing with my frontend. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@proxy/proxymanager.go`:
- Line 361: The new POST route registration
pm.ginEngine.POST("/v1/audio/voices", pm.apiKeyAuth(), pm.trackInflight(),
pm.proxyOAIPostFormHandler) switched to a multipart/form handler but lacks a
regression test; add a unit/integration test that performs a multipart POST to
"/v1/audio/voices" (modeled after the existing /v1/audio/transcriptions
multipart test) asserting auth, proper proxyOAIPostFormHandler invocation,
expected downstream request/body handling and response codes; update or add the
test to the same test suite that covers proxy handlers and run make test-dev
(and make test-all before finalizing) to ensure no regressions.
- Line 361: The route registration pm.ginEngine.POST("/v1/audio/voices",
pm.apiKeyAuth(), pm.trackInflight(), pm.proxyOAIPostFormHandler) is using
proxyOAIPostFormHandler which rejects requests missing a "model" form field (see
the model extraction in proxyOAIPostFormHandler), but OpenAI's /v1/audio/voices
POST does not include a model field; either register a new handler that mirrors
OpenAI's required fields (accepts audio_sample, consent, name and skips model
validation) or remove/disable this route until such a handler exists; implement
the new handler (e.g., proxyOAIFormVoiceHandler) that forwards form-data to the
upstream without requiring model, update the route to use it instead of
proxyOAIPostFormHandler, and add a regression test that posts
audio_sample/consent/name to /v1/audio/voices and asserts the proxy accepts and
forwards the request.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4798e35f-ee19-437e-9edc-7be640d663cc
📒 Files selected for processing (1)
proxy/proxymanager.go
| // Support audio/speech endpoint | ||
| pm.ginEngine.POST("/v1/audio/speech", pm.apiKeyAuth(), pm.trackInflight(), pm.proxyInferenceHandler) | ||
| pm.ginEngine.POST("/v1/audio/voices", pm.apiKeyAuth(), pm.trackInflight(), pm.proxyInferenceHandler) | ||
| pm.ginEngine.POST("/v1/audio/voices", pm.apiKeyAuth(), pm.trackInflight(), pm.proxyOAIPostFormHandler) |
There was a problem hiding this comment.
Add regression coverage for the new /v1/audio/voices multipart handler wiring
This route changed handler type, but there is no corresponding test shown (unlike /v1/audio/transcriptions). Please add a multipart POST test for /v1/audio/voices to lock behavior and prevent regressions.
As per coding guidelines proxy/**/*.go: "Use make test-dev after running new tests for a quick overall test run when changes are made to any code under the proxy/ directory", and **/*.go: "Use make test-all before completing work to include long running concurrency tests".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@proxy/proxymanager.go` at line 361, The new POST route registration
pm.ginEngine.POST("/v1/audio/voices", pm.apiKeyAuth(), pm.trackInflight(),
pm.proxyOAIPostFormHandler) switched to a multipart/form handler but lacks a
regression test; add a unit/integration test that performs a multipart POST to
"/v1/audio/voices" (modeled after the existing /v1/audio/transcriptions
multipart test) asserting auth, proper proxyOAIPostFormHandler invocation,
expected downstream request/body handling and response codes; update or add the
test to the same test suite that covers proxy handlers and run make test-dev
(and make test-all before finalizing) to ensure no regressions.
🧩 Analysis chain
🌐 Web query:
In the official OpenAI API reference, what multipart form fields are required for POST /v1/audio/voices (Create voice)?
💡 Result:
The required multipart form fields for POST /v1/audio/voices (Create voice) are: audio_sample, consent, and name. These are shown in the official curl examples in the OpenAI API reference: curl https://api.openai.com/v1/audio/voices \ -H 'Content-Type: multipart/form-data' \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -F 'audio_sample=@/path/to/audio_sample' \ -F consent=consent \ -F name=name and curl https://api.openai.com/v1/audio/voices \ -X POST \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -F "name=My new voice" \ -F "consent=cons_1234" \ -F "audio_sample=@$HOME/audio_sample.wav;type=audio/x-wav" - audio_sample: The audio file sample for the custom voice (file upload). - consent: The ID of a voice consent recording (obtained via POST /v1/audio/voice_consents). - name: A name for the voice (string).
Citations:
- 1: https://developers.openai.com/api/reference/resources/audio/subresources/voices
- 2: https://developers.openai.com/api/reference/resources/audio/subresources/voices/methods/create/
🏁 Script executed:
# First, check the proxyOAIPostFormHandler to see if it requires model field
rg -A 50 "func.*proxyOAIPostFormHandler" proxy/proxymanager.go | head -80Repository: mostlygeek/llama-swap
Length of output: 2045
🏁 Script executed:
# Check for any existing tests related to /v1/audio/voices
rg "/v1/audio/voices" proxy/ --type goRepository: mostlygeek/llama-swap
Length of output: 607
🏁 Script executed:
# Search for proxyOAIVoicesCreateHandler or similar handlers
rg "proxyOAIVoices" proxy/ --type goRepository: mostlygeek/llama-swap
Length of output: 47
🏁 Script executed:
# Check formatting of the file
gofmt -l proxy/proxymanager.goRepository: mostlygeek/llama-swap
Length of output: 47
🏁 Script executed:
# Search for multipart handlers in the codebase
rg "func.*Handler" proxy/proxymanager.go | grep -i "form\|multipart"Repository: mostlygeek/llama-swap
Length of output: 130
🏁 Script executed:
# Check for POST /v1/audio/voices test coverage
rg -B 5 -A 15 "POST.*audio/voices\|audio/voices.*POST" proxy/proxymanager_test.goRepository: mostlygeek/llama-swap
Length of output: 47
🏁 Script executed:
# Search for what other endpoints use proxyOAIPostFormHandler
rg "proxyOAIPostFormHandler" proxy/proxymanager.goRepository: mostlygeek/llama-swap
Length of output: 450
🏁 Script executed:
# Check all POST handlers to find alternatives
rg "func.*Handler.*\(c \*gin.Context\)" proxy/proxymanager.go | grep -i postRepository: mostlygeek/llama-swap
Length of output: 130
🏁 Script executed:
# Look for any existing voice-related handlers
rg -i "voice" proxy/proxymanager.goRepository: mostlygeek/llama-swap
Length of output: 268
🏁 Script executed:
# Check what the transcriptions endpoint actually requires from OpenAI API docs
# and verify the handler is appropriate for it
rg -B 2 -A 2 "audio/transcriptions" proxy/proxymanager.goRepository: mostlygeek/llama-swap
Length of output: 592
🏁 Script executed:
# Check if proxyOAIPostFormHandler is specifically designed to require model
# and verify OpenAI API requirements for transcriptions and images/edits
rg -B 5 -A 15 "POST /v1/audio/transcriptions\|POST /v1/images/edits" proxy/proxymanager.goRepository: mostlygeek/llama-swap
Length of output: 47
🏁 Script executed:
# Look for any handler that does NOT require model field
rg "FormValue" proxy/proxymanager.go | head -20Repository: mostlygeek/llama-swap
Length of output: 112
🏁 Script executed:
# Verify the exact requirements for OpenAI's transcriptions and images/edits endpoints
# to understand if model is expected in those APIs too
web_search "OpenAI API audio transcriptions POST form fields required model"Repository: mostlygeek/llama-swap
Length of output: 115
🏁 Script executed:
# Check the proxyOAIPostFormHandler more thoroughly - see the full implementation
# to understand if model extraction is conditional or always required
sed -n '847,977p' proxy/proxymanager.goRepository: mostlygeek/llama-swap
Length of output: 4636
API contract mismatch: /v1/audio/voices POST handler requires model field that OpenAI API does not provide
Line 361 routes POST /v1/audio/voices to pm.proxyOAIPostFormHandler, which rejects requests with a 400 error unless a model form field is present (line 857–858). However, the OpenAI API for this endpoint requires audio_sample, consent, and name fields—it does not include a model field. This routing will break all OpenAI-compatible voice creation requests.
The handler's model extraction is necessary for the proxy's routing logic, but POST /v1/audio/voices is fundamentally incompatible with this design. Either:
- A new handler is needed that does not require
model, or - This endpoint should not be exposed in the proxy until a compatible handler exists
Add a regression test for this endpoint if proceeding with the fix.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@proxy/proxymanager.go` at line 361, The route registration
pm.ginEngine.POST("/v1/audio/voices", pm.apiKeyAuth(), pm.trackInflight(),
pm.proxyOAIPostFormHandler) is using proxyOAIPostFormHandler which rejects
requests missing a "model" form field (see the model extraction in
proxyOAIPostFormHandler), but OpenAI's /v1/audio/voices POST does not include a
model field; either register a new handler that mirrors OpenAI's required fields
(accepts audio_sample, consent, name and skips model validation) or
remove/disable this route until such a handler exists; implement the new handler
(e.g., proxyOAIFormVoiceHandler) that forwards form-data to the upstream without
requiring model, update the route to use it instead of proxyOAIPostFormHandler,
and add a regression test that posts audio_sample/consent/name to
/v1/audio/voices and asserts the proxy accepts and forwards the request.
|
Hi, Do you have a configuration for how a backend to test this with? |
|
making use of this: https://github.com/khimaros/qwen3-tts.cpp "qwen3-tts-server": |
qwen3-tts-server
--port ${PORT}
--hf-repo-v khimaros/Qwen3-TTS-Tokenizer-12Hz-GGUF:F16
--threads 16
--verbose
"qwen3-tts-1.7b-base:Q8_0":
macros: {modso: "audio", tuned: "base"}
metadata: *default_metadata
cmd: |
${qwen3-tts-server}
--hf-repo khimaros/Qwen3-TTS-12Hz-1.7B-Base-GGUF:Q8_0
"qwen3-tts-1.7b-customvoice:Q8_0":
macros: {modso: "audio", tuned: "base"}
metadata: *default_metadata
cmd: |
${qwen3-tts-server}
--hf-repo khimaros/Qwen3-TTS-12Hz-1.7B-CustomVoice-GGUF:Q8_0
"qwen3-tts-1.7b-voicedesign:Q8_0":
macros: {modso: "audio", tuned: "base"}
metadata: *default_metadata
cmd: |
${qwen3-tts-server}
--hf-repo khimaros/Qwen3-TTS-12Hz-1.7B-VoiceDesign-GGUF:Q8_0 |
|
also with https://github.com/khimaros/s2.cpp |
|
some example clients here: https://github.com/khimaros/flow |
this conforms to the OpenAI voices endpoint https://developers.openai.com/api/reference/resources/audio/subresources/voices/methods/create