Skip to content

新增支持豆包语音合成2.0功能 - #2067

Merged
creamlike1024 merged 5 commits into
QuantumNous:mainfrom
feitianbubu:pr/add-doubao-audio
Oct 17, 2025
Merged

新增支持豆包语音合成2.0功能#2067
creamlike1024 merged 5 commits into
QuantumNous:mainfrom
feitianbubu:pr/add-doubao-audio

Conversation

@feitianbubu

@feitianbubu feitianbubu commented Oct 17, 2025

Copy link
Copy Markdown
Member

豆包音包接口: https://www.volcengine.com/docs/6561/1257584
豆包音色列表: https://www.volcengine.com/docs/6561/1257544
渠道选择:字节火山方舟,豆包通用渠道
豆包语音apiKey官方特殊格式: AppId|AccessToken
模型id: seed-tts-1.1
请求格式:
{
"input": "你是一个好孩子",
"model": "seed-tts-1.1",
"response_format": "mp3",
"speed": 1,
"voice": "zh_female_vv_uranus_bigtts"
}
返回格式:
image

Summary by CodeRabbit

  • New Features

    • Added Volcengine text-to-speech (TTS) integration with voice, speed, and encoding options and improved audio streaming.
    • Added Volcengine channel support (AppId + AccessToken) for voice input.
  • Style / UI

    • Updated channel authentication prompt to include AppId|AccessToken format.

@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

Walkthrough

Implements Volcengine TTS: adds Volcengine TTS data models and response handling; extends the Volcengine relay adaptor (ConvertAudioRequest, GetRequestURL, SetupRequestHeader, DoResponse) to support audio speech; and updates the UI prompt for Volcengine channel authentication.

Changes

Cohort / File(s) Summary
Volcengine Adaptor Methods
relay/channel/volcengine/adaptor.go
Added ConvertAudioRequest implementation (validates RelayMode, parses auth, maps voice/speed/encoding, builds JSON payload); GetRequestURL extended for RelayModeAudioSpeech (uses official TTS URL or local /v1/audio/speech); SetupRequestHeader extracts Bearer token, sets Content-Type for audio speech; DoResponse routes RelayModeAudioSpeech through TTS response handler before falling back to standard flow.
Volcengine TTS Integration
relay/channel/volcengine/tts.go
New file introducing Volcengine TTS types (VolcengineTTSRequest, VolcengineTTSApp, VolcengineTTSUser, VolcengineTTSAudio, VolcengineTTSReqInfo, VolcengineTTSExtraParam, VolcengineTTSCacheConfig, VolcengineTTSResponse, VolcengineTTSAdditionInfo), voice/encoding mappings, request-id helper, encoding/content-type resolution, and handleTTSResponse to parse responses, validate code, decode base64 audio, stream bytes, and produce usage metrics.
UI Channel Configuration
web/src/components/table/channels/modals/EditChannelModal.jsx
Added case 45 to type2secretPrompt(type) switch to return the prompt: "请输入渠道对应的鉴权密钥, 豆包语音输入:AppId

Sequence Diagram

sequenceDiagram
    participant Client
    participant Adaptor as Volcengine Adaptor
    participant VolcAPI as Volcengine API
    participant TTSHandler as TTS Handler

    Client->>Adaptor: Audio request (RelayModeAudioSpeech, voice, speed, encoding)
    Adaptor->>Adaptor: ConvertAudioRequest -> build VolcengineTTSRequest JSON
    Adaptor->>Adaptor: SetupRequestHeader -> extract Bearer, set Content-Type
    Adaptor->>VolcAPI: POST /v1/tts or /v1/audio/speech (JSON + Bearer)
    VolcAPI-->>Adaptor: JSON response (code, base64 audio, extras)
    Adaptor->>TTSHandler: DoResponse -> handleTTSResponse
    TTSHandler->>TTSHandler: validate code, decode base64, determine Content-Type
    TTSHandler-->>Client: stream audio bytes + usage headers/metrics
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • seefs001

Poem

🐰✨ I hopped in code and found a stream,
Volcengine hums and feeds my dream,
From text to voice the bytes take flight,
I nibble bugs and stash them light,
豆包 sings — the day feels bright.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "新增支持豆包语音合成2.0功能" (Add support for Doubao voice synthesis 2.0 functionality) is concise, clear, and directly aligned with the main change across all modified files. The changes implement Volcengine TTS integration for Doubao voice synthesis 2.0 in the backend (adaptor.go and tts.go) and add corresponding frontend support in the channel configuration modal (EditChannelModal.jsx). The title accurately captures this primary objective without being overly broad, vague, or misleading, and uses specific terminology ("Doubao voice synthesis 2.0") that clearly communicates the feature being added.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8965fc4 and 6a24c37.

📒 Files selected for processing (1)
  • relay/channel/volcengine/tts.go (1 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
relay/channel/volcengine/tts.go (1)

156-204: Response handler logic is correct with minor style observation.

The function properly:

  • Reads and parses the response
  • Validates the success code (3000)
  • Decodes base64 audio data
  • Sets appropriate content type
  • Returns usage metrics

Note: Line 165's defer resp.Body.Close() comes after io.ReadAll (line 157), which is unusual but not incorrect since the body has already been consumed. Consider moving the defer immediately after the function signature for better clarity.

Optional style improvement:

 func handleTTSResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo, encoding string) (usage any, err *types.NewAPIError) {
+	defer resp.Body.Close()
 	body, readErr := io.ReadAll(resp.Body)
 	if readErr != nil {
 		return nil, types.NewErrorWithStatusCode(
 			errors.New("failed to read volcengine response"),
 			types.ErrorCodeReadResponseBodyFailed,
 			http.StatusInternalServerError,
 		)
 	}
-	defer resp.Body.Close()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd77516 and 8965fc4.

📒 Files selected for processing (3)
  • relay/channel/volcengine/adaptor.go (4 hunks)
  • relay/channel/volcengine/tts.go (1 hunks)
  • web/src/components/table/channels/modals/EditChannelModal.jsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
relay/channel/volcengine/adaptor.go (3)
relay/constant/relay_mode.go (1)
  • RelayModeAudioSpeech (35-35)
relay/channel/volcengine/tts.go (5)
  • VolcengineTTSRequest (18-23)
  • VolcengineTTSApp (25-29)
  • VolcengineTTSUser (31-33)
  • VolcengineTTSAudio (35-47)
  • VolcengineTTSReqInfo (49-58)
constant/channel.go (2)
  • ChannelBaseURLs (60-117)
  • ChannelTypeVolcEngine (45-45)
relay/channel/volcengine/tts.go (4)
dto/request_common.go (1)
  • Request (8-12)
relay/common/relay_info.go (1)
  • RelayInfo (75-122)
types/error.go (5)
  • NewAPIError (87-95)
  • NewErrorWithStatusCode (259-275)
  • ErrorCodeReadResponseBodyFailed (69-69)
  • ErrorCodeBadResponseBody (72-72)
  • ErrorCodeBadResponse (71-71)
dto/openai_response.go (1)
  • Usage (222-235)
🔇 Additional comments (8)
web/src/components/table/channels/modals/EditChannelModal.jsx (1)

110-111: LGTM! Prompt aligns with backend authentication format.

The authentication key format AppId|AccessToken correctly matches the backend parsing logic in relay/channel/volcengine/adaptor.go (line 108), which splits by "|" expecting exactly 2 parts.

relay/channel/volcengine/adaptor.go (4)

261-266: LGTM! URL routing logic appropriately handles official and proxy scenarios.

The conditional check ensures that official Volcengine channels use the correct TTS endpoint while allowing custom base URLs to use the standard OpenAI-compatible path.


320-323: LGTM! Response routing is clean and delegates appropriately.

The early return for audio speech mode correctly routes to the TTS-specific handler with the appropriate encoding parameter.


276-283: Authorization header format is correct.

The Volcengine TTS API officially uses Bearer;{ACCESS_TOKEN} format, confirming that line 279's implementation with "Bearer;" + parts[1] is correct. This is not a typo—it matches the official specification.


55-76: Verify hardcoded values against your Volcengine account requirements and consider documenting their source.

The web search found that cluster: "volcano_tts", rate: 24000 (Hz), and uid as a business-defined string are commonly used in Volcengine SDK and community examples. Your implementation aligns with these patterns.

However, ensure these defaults match your specific Volcengine account configuration. Additionally, consider adding code comments documenting why these values are hardcoded (e.g., "Volcengine service defaults per SDK examples") to clarify intent for future maintainers.

relay/channel/volcengine/tts.go (3)

18-87: LGTM! Well-structured data models for Volcengine TTS integration.

The type definitions are comprehensive with proper JSON tags and appropriate use of omitempty for optional fields.


115-154: LGTM! Mapping functions have appropriate fallbacks and validation.

The helper functions properly handle edge cases:

  • mapSpeedRatio clamps values to valid range [0.1, 2.0]
  • mapVoiceType and mapEncoding provide sensible fallbacks
  • getContentTypeByEncoding correctly maps to MIME types

206-208: LGTM! Standard UUID generation for request tracking.

Using uuid.New() from the google/uuid package is appropriate for generating unique request IDs.

Comment on lines +98 to +105
var responseFormatToEncodingMap = map[string]string{
"mp3": "mp3",
"opus": "ogg_opus",
"aac": "mp3",
"flac": "mp3",
"wav": "wav",
"pcm": "pcm",
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Review format mapping for potential user expectation mismatch.

Lines 101-102 map both "aac" and "flac" to "mp3". When users request these formats, they'll receive MP3 audio instead, which could cause issues if the client expects the requested format. Consider either:

  1. Returning an error for unsupported formats
  2. Documenting this limitation clearly
  3. Checking if Volcengine actually supports these formats

🌐 Web query:

Does Volcengine TTS API support aac and flac output formats?

💡 Result:

Yes. Volcengine's TTS supports AAC and FLAC as output formats (response_format can be mp3, opus, aac, flac, wav, pcm). [1][2]

Sources:
[1] developer.volcengine.com TTS article (response_format list).
[2] TTS API docs (response_format: mp3, opus, aac, flac, wav, pcm).


Fix incorrect format mappings: aac and flac should not map to mp3.

Volcengine's TTS API supports aac and flac as native output formats, but lines 101-102 incorrectly map both to "mp3". This causes users requesting these formats to receive MP3 audio instead, creating a format mismatch.

Correct these mappings:

  • "aac": "aac"
  • "flac": "flac"
🤖 Prompt for AI Agents
In relay/channel/volcengine/tts.go around lines 98 to 105 the mapping for "aac"
and "flac" incorrectly points to "mp3", causing requests for those formats to be
served as MP3; update the map so that "aac" maps to "aac" and "flac" maps to
"flac" (leave other entries unchanged) to ensure the TTS output encoding matches
the requested format.

Comment on lines +107 to +113
func parseVolcengineAuth(apiKey string) (appID, token string, err error) {
parts := strings.Split(apiKey, "|")
if len(parts) != 2 {
return "", "", errors.New("invalid api key format, expected: appid:access_token")
}
return parts[0], parts[1], nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix inconsistent error message.

Line 110's error message says "expected: appid:access_token" but the actual separator is "|" (pipe), not ":" (colon).

Apply this diff to fix the error message:

 func parseVolcengineAuth(apiKey string) (appID, token string, err error) {
 	parts := strings.Split(apiKey, "|")
 	if len(parts) != 2 {
-		return "", "", errors.New("invalid api key format, expected: appid:access_token")
+		return "", "", errors.New("invalid api key format, expected: AppId|AccessToken")
 	}
 	return parts[0], parts[1], nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func parseVolcengineAuth(apiKey string) (appID, token string, err error) {
parts := strings.Split(apiKey, "|")
if len(parts) != 2 {
return "", "", errors.New("invalid api key format, expected: appid:access_token")
}
return parts[0], parts[1], nil
}
func parseVolcengineAuth(apiKey string) (appID, token string, err error) {
parts := strings.Split(apiKey, "|")
if len(parts) != 2 {
return "", "", errors.New("invalid api key format, expected: AppId|AccessToken")
}
return parts[0], parts[1], nil
}
🤖 Prompt for AI Agents
In relay/channel/volcengine/tts.go around lines 107 to 113, the error string
wrongly describes the expected separator as ":" while the code splits on "|" —
update the error message to reflect the actual pipe separator (e.g. "invalid api
key format, expected: appid|access_token") so the message matches the parsing
logic.

@creamlike1024
creamlike1024 merged commit 1ec664a into QuantumNous:main Oct 17, 2025
1 check was pending
ennnnny pushed a commit to ennnnny/new-api that referenced this pull request Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants