-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
fix: align mimo tts style payload with official docs #6814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -44,35 +44,53 @@ def __init__( | |||||||||||
| self.set_model(provider_config.get("model", DEFAULT_MIMO_TTS_MODEL)) | ||||||||||||
| self.client = create_http_client(self.timeout, self.proxy) | ||||||||||||
|
|
||||||||||||
| def _build_user_prompt(self) -> str: | ||||||||||||
| prompt_parts: list[str] = [] | ||||||||||||
| def _build_user_prompt(self) -> str | None: | ||||||||||||
| seed_text = self.seed_text.strip() | ||||||||||||
| return seed_text or None | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line returns the
Suggested change
|
||||||||||||
|
|
||||||||||||
| def _build_style_prefix(self) -> str: | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||
| style_parts: list[str] = [] | ||||||||||||
|
|
||||||||||||
| if self.style_prompt.strip(): | ||||||||||||
| prompt_parts.append(self.style_prompt.strip()) | ||||||||||||
| style_parts.append(self.style_prompt.strip()) | ||||||||||||
| if self.dialect.strip(): | ||||||||||||
| prompt_parts.append(f"Please use {self.dialect.strip()} when speaking.") | ||||||||||||
| style_parts.append(self.dialect.strip()) | ||||||||||||
|
|
||||||||||||
| style_content = " ".join(style_parts).strip() | ||||||||||||
| if not style_content: | ||||||||||||
| return "" | ||||||||||||
|
|
||||||||||||
| if not prompt_parts: | ||||||||||||
| return self.seed_text | ||||||||||||
| # MiMo recommends using only the singing style tag at the very beginning. | ||||||||||||
| if "唱歌" in style_content: | ||||||||||||
| return "<style>唱歌</style>" | ||||||||||||
|
Comment on lines
+64
to
+65
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic handles the special case where the style includes "唱歌". It would be beneficial to add a comment explaining why this special handling is necessary, referencing the MiMo documentation if possible.
Suggested change
|
||||||||||||
|
|
||||||||||||
| if self.seed_text.strip(): | ||||||||||||
| prompt_parts.append(self.seed_text.strip()) | ||||||||||||
| return f"<style>{style_content}</style>" | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a comment here to explain that the
Suggested change
|
||||||||||||
|
|
||||||||||||
| return " ".join(prompt_parts) | ||||||||||||
| def _build_assistant_content(self, text: str) -> str: | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||
| return f"{self._build_style_prefix()}{text}" | ||||||||||||
|
|
||||||||||||
| def _build_payload(self, text: str) -> dict: | ||||||||||||
| return { | ||||||||||||
| "model": self.model_name, | ||||||||||||
| "messages": [ | ||||||||||||
| messages: list[dict[str, str]] = [] | ||||||||||||
|
|
||||||||||||
| user_prompt = self._build_user_prompt() | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a comment to clarify that
Suggested change
|
||||||||||||
| if user_prompt: | ||||||||||||
| messages.append( | ||||||||||||
| { | ||||||||||||
| "role": "user", | ||||||||||||
| "content": self._build_user_prompt(), | ||||||||||||
| }, | ||||||||||||
| { | ||||||||||||
| "role": "assistant", | ||||||||||||
| "content": text, | ||||||||||||
| }, | ||||||||||||
| ], | ||||||||||||
| "content": user_prompt, | ||||||||||||
| } | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| messages.append( | ||||||||||||
| { | ||||||||||||
| "role": "assistant", | ||||||||||||
| "content": self._build_assistant_content(text), | ||||||||||||
| } | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| return { | ||||||||||||
| "model": self.model_name, | ||||||||||||
| "messages": messages, | ||||||||||||
| "audio": { | ||||||||||||
| "format": self.audio_format, | ||||||||||||
| "voice": self.voice, | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,29 +35,96 @@ def _make_stt_provider(overrides: dict | None = None) -> ProviderMiMoSTTAPI: | |||||||||||||||||||||||
| return ProviderMiMoSTTAPI(provider_config=provider_config, provider_settings={}) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def test_mimo_tts_prompt_returns_seed_text_when_no_style_or_dialect(): | ||||||||||||||||||||||||
| def test_mimo_tts_user_prompt_returns_seed_text(): | ||||||||||||||||||||||||
| provider = _make_tts_provider() | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| assert provider._build_user_prompt() == "seed text" | ||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| asyncio.run(provider.terminate()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def test_mimo_tts_payload_includes_dialect_and_style_prompt(): | ||||||||||||||||||||||||
| def test_mimo_tts_assistant_content_prefixes_style_and_dialect(): | ||||||||||||||||||||||||
| provider = _make_tts_provider( | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "mimo-tts-style-prompt": "Please sound cheerful and lively.", | ||||||||||||||||||||||||
| "mimo-tts-dialect": "Sichuan dialect", | ||||||||||||||||||||||||
| "mimo-tts-style-prompt": "开心", | ||||||||||||||||||||||||
| "mimo-tts-dialect": "四川话", | ||||||||||||||||||||||||
| "mimo-tts-seed-text": "You are chatting with a close friend.", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| payload = provider._build_payload("hello") | ||||||||||||||||||||||||
|
Comment on lines
+46
to
55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Strengthen this test to fully validate the assistant message structure and messages length. Currently this only asserts the full first Suggested implementation: def test_mimo_tts_assistant_content_prefixes_style_and_dialect():
provider = _make_tts_provider(
{
"mimo-tts-style-prompt": "开心",
"mimo-tts-dialect": "四川话",
"mimo-tts-seed-text": "You are chatting with a close friend.",
}
)
try:
# Build the payload used to call the model
payload = provider._build_payload()
# Ensure we only ever send the expected two messages
assert len(payload["messages"]) == 2
# First message should still be the full user seed text as before
user_msg = payload["messages"][0]
assert user_msg["role"] == "user"
assert user_msg["content"] == "You are chatting with a close friend."
# Second message should be an assistant message whose content prefixes style and dialect
assistant_msg = payload["messages"][1]
assert assistant_msg["role"] == "assistant"
assert "开心" in assistant_msg["content"]
assert "四川话" in assistant_msg["content"]
asyncio.run(provider.terminate())Because I can only see part of the file, you may need to align the helper names and patterns:
|
||||||||||||||||||||||||
| assert payload["messages"][0]["content"] == ( | ||||||||||||||||||||||||
| "Please sound cheerful and lively. " | ||||||||||||||||||||||||
| "Please use Sichuan dialect when speaking. " | ||||||||||||||||||||||||
| "You are chatting with a close friend." | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| assert payload["messages"][0] == { | ||||||||||||||||||||||||
| "role": "user", | ||||||||||||||||||||||||
| "content": "You are chatting with a close friend.", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| assert payload["messages"][1]["content"] == "<style>开心 四川话</style>hello" | ||||||||||||||||||||||||
|
Comment on lines
55
to
+60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assertion assert payload["messages"][0]["role"] == "user"
assert payload["messages"][0]["content"] == "You are chatting with a close friend."
assert payload["messages"][1]["content"] == "<style>开心 四川话</style>hello"
Comment on lines
+56
to
+60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assertion
Suggested change
|
||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| asyncio.run(provider.terminate()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def test_mimo_tts_payload_omits_user_message_without_seed_text(): | ||||||||||||||||||||||||
| provider = _make_tts_provider( | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "mimo-tts-seed-text": "", | ||||||||||||||||||||||||
| "mimo-tts-style-prompt": "开心", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| payload = provider._build_payload("hello") | ||||||||||||||||||||||||
| assert payload["messages"] == [ | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "role": "assistant", | ||||||||||||||||||||||||
| "content": "<style>开心</style>hello", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+74
to
+78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assertion assert len(payload["messages"]) == 1
assert payload["messages"][0]["role"] == "assistant"
assert payload["messages"][0]["content"] == "<style>开心</style>hello" |
||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| asyncio.run(provider.terminate()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def test_mimo_tts_singing_style_uses_single_style_tag(): | ||||||||||||||||||||||||
| provider = _make_tts_provider( | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "mimo-tts-style-prompt": "唱歌 开心", | ||||||||||||||||||||||||
| "mimo-tts-dialect": "粤语", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| payload = provider._build_payload("歌词") | ||||||||||||||||||||||||
| assert payload["messages"][1]["content"] == "<style>唱歌</style>歌词" | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assertion assert payload["messages"][1]["role"] == "assistant"
assert payload["messages"][1]["content"] == "<style>唱歌</style>歌词" |
||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| asyncio.run(provider.terminate()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def test_mimo_tts_plain_text_stays_in_assistant_message_when_no_style(): | ||||||||||||||||||||||||
| provider = _make_tts_provider( | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "mimo-tts-seed-text": "", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| payload = provider._build_payload("hello") | ||||||||||||||||||||||||
| assert payload["messages"] == [ | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "role": "assistant", | ||||||||||||||||||||||||
| "content": "hello", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+106
to
+110
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assertion assert len(payload["messages"]) == 1
assert payload["messages"][0]["role"] == "assistant"
assert payload["messages"][0]["content"] == "hello" |
||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| asyncio.run(provider.terminate()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def test_mimo_tts_seed_text_is_not_prepended_to_assistant_content(): | ||||||||||||||||||||||||
| provider = _make_tts_provider( | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "mimo-tts-style-prompt": "开心", | ||||||||||||||||||||||||
| "mimo-tts-seed-text": "reference text", | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| payload = provider._build_payload("明天就是周五了") | ||||||||||||||||||||||||
| assert payload["messages"][0]["content"] == "reference text" | ||||||||||||||||||||||||
| assert payload["messages"][1]["content"] == "<style>开心</style>明天就是周五了" | ||||||||||||||||||||||||
| assert "reference text" not in payload["messages"][1]["content"] | ||||||||||||||||||||||||
|
Comment on lines
+125
to
+127
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These assertions check the content of the user and assistant messages. It would be more robust to also assert the assert payload["messages"][0]["role"] == "user"
assert payload["messages"][0]["content"] == "reference text"
assert payload["messages"][1]["role"] == "assistant"
assert payload["messages"][1]["content"] == "<style>开心</style>明天就是周五了"
assert "reference text" not in payload["messages"][1]["content"] |
||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| asyncio.run(provider.terminate()) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -129,7 +196,10 @@ async def fake_post(_url, headers=None, json=None): | |||||||||||||||||||||||
| assert result == "transcribed text" | ||||||||||||||||||||||||
| assert captured["json"]["messages"][0]["content"] == "system prompt" | ||||||||||||||||||||||||
| assert captured["json"]["messages"][1]["content"][0]["type"] == "input_audio" | ||||||||||||||||||||||||
| assert captured["json"]["messages"][1]["content"][0]["input_audio"]["data"] == "ZmFrZQ==" | ||||||||||||||||||||||||
| assert ( | ||||||||||||||||||||||||
| captured["json"]["messages"][1]["content"][0]["input_audio"]["data"] | ||||||||||||||||||||||||
| == "ZmFrZQ==" | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| assert captured["json"]["messages"][1]["content"][1]["text"] == "user prompt" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function
_build_user_promptis now returningNonein some cases. It would be helpful to add a docstring to explain the function's purpose, return value, and the conditions under which it returnsNone.