fix: refresh Fish Audio promotion and schema

This commit is contained in:
calesthio
2026-08-13 09:32:54 -07:00
parent 7052f3999b
commit 7f43667dfd
4 changed files with 19 additions and 11 deletions

View File

@@ -26,11 +26,11 @@ The backend model is chosen with the `model` **HTTP header**, not a body field.
`model` is **required — there is no default**. Pass one of:
- `s2.1-pro` — latest generation. Best quality: inline emotion tags, 80+ languages, multi-speaker. Hero narration.
- `s2.1-pro-free`**promotional** free access to s2.1-pro. Drafts, samples, and validation runs at $0 during the promo window only. Per the [fish.audio announcement](https://fish.audio/ar/blog/s2-1-pro-free-api/?articleLocale=en): free through end of July 2026, subject to Fair Use, no SLA/latency guarantee, requests may be retained, and commercial use is restricted. Never route production or client narration through it.
- `s2.1-pro-free`**promotional** free access to s2.1-pro. Drafts, samples, and validation runs at $0 during the promo window only. Per the [fish.audio announcement](https://fish.audio/ko/blog/s2-1-pro-free-api/?articleLocale=en): free through August 31, 2026, subject to Fair Use, no SLA/latency guarantee, requests may be retained, and commercial use is restricted. Never route production or client narration through it.
- `s2-pro` — first S2 generation. Stable high quality with emotion-tag support.
- `s1` — previous flagship. Kept for compatibility with existing integrations.
Billing is **per UTF-8 byte of input text** (not per character). CJK text and emoji cost 3-4x an ASCII character of the same visible length. Approximate: `s1` / `s2-pro` / `s2.1-pro` $15 per 1M bytes, `s2.1-pro-free` = $0 during the promo window only (the tool's `estimate_cost()` switches to the paid `s2.1-pro` rate after end of July 2026). Verify current pricing at https://fish.audio before large batches.
Billing is **per UTF-8 byte of input text** (not per character). CJK text and emoji cost 3-4x an ASCII character of the same visible length. Current list pricing: `s1` / `s2-pro` / `s2.1-pro` = $15 per 1M bytes, `s2.1-pro-free` = $0 during the promo window only (the tool's `estimate_cost()` switches to the paid `s2.1-pro` rate after August 31, 2026). Verify current pricing at https://docs.fish.audio/developer-guide/models-pricing/pricing-and-rate-limits before large batches.
## Inline emotion tags (S2 models only)

View File

@@ -323,13 +323,13 @@ No subscription — pure pay-as-you-go, no minimum spend.
| `s2-pro` | First S2 generation — stable high quality with emotion-tag support |
| `s1` | Previous flagship, kept for compatibility (no emotion tags) |
**`s2.1-pro-free` caveats — promotional, not a durable free tier.** Per the [fish.audio announcement](https://fish.audio/ar/blog/s2-1-pro-free-api/?articleLocale=en), free API access runs **through the end of July 2026** and is subject to Fair Use limits, carries **no SLA or latency guarantee**, requests **may be retained** by fish.audio, and **commercial use is restricted**. Don't route client work or production narration through it, and don't plan long-term costs at $0 — `fish_audio_tts.estimate_cost()` falls back to the paid `s2.1-pro` rate after the promotional window ends.
**`s2.1-pro-free` caveats — promotional, not a durable free tier.** Per the [fish.audio announcement](https://fish.audio/ko/blog/s2-1-pro-free-api/?articleLocale=en), free API access runs **through August 31, 2026** and is subject to Fair Use limits, carries **no SLA or latency guarantee**, requests **may be retained** by fish.audio, and **commercial use is restricted**. Don't route client work or production narration through it, and don't plan long-term costs at $0 — `fish_audio_tts.estimate_cost()` falls back to the paid `s2.1-pro` rate after the promotional window ends.
The legacy `speech-1.x` tier and `s1-mini` have been removed from the fish.audio API and are not supported.
#### Pricing
Billing is **per UTF-8 byte of input text** (not per character) — CJK text and emoji cost 3-4x an ASCII character of the same visible length. Approximate: `s1` / `s2-pro` / `s2.1-pro` $15 per 1M bytes; `s2.1-pro-free` is $0 only during the promotional window (through end of July 2026 — see caveats above). Verify current pricing at [fish.audio](https://fish.audio) before large batches.
Billing is **per UTF-8 byte of input text** (not per character) — CJK text and emoji cost 3-4x an ASCII character of the same visible length. Current list pricing: `s1` / `s2-pro` / `s2.1-pro` = $15 per 1M bytes; `s2.1-pro-free` is $0 only during the promotional window (through August 31, 2026 — see caveats above). Verify current pricing in the [official pricing guide](https://docs.fish.audio/developer-guide/models-pricing/pricing-and-rate-limits) before large batches.
---

View File

@@ -80,9 +80,13 @@ class TestModelContract:
assert result.success is False
assert "Unknown fish.audio model" in result.error
def test_schema_declares_model_required(self):
def test_schema_requires_model_or_selector_alias(self):
schema = FishAudioTTS.input_schema
assert "model" in schema["required"]
assert schema["required"] == ["text"]
assert {tuple(option["required"]) for option in schema["anyOf"]} == {
("model",),
("model_id",),
}
assert "model_id" in schema["properties"]
def test_model_id_alias_accepted(self, api_key, tmp_path):
@@ -252,12 +256,12 @@ class TestCost:
def test_s2_1_pro_free_costs_zero_during_promo_window(self):
tool = FishAudioTTS()
with patch.object(FishAudioTTS, "_today", return_value=date(2026, 7, 1)):
with patch.object(FishAudioTTS, "_today", return_value=date(2026, 8, 31)):
assert tool.estimate_cost({"text": "same text here", "model": "s2.1-pro-free"}) == 0.0
def test_s2_1_pro_free_charged_at_paid_rate_after_promo_window(self):
tool = FishAudioTTS()
with patch.object(FishAudioTTS, "_today", return_value=date(2026, 8, 1)):
with patch.object(FishAudioTTS, "_today", return_value=date(2026, 9, 1)):
free_after = tool.estimate_cost({"text": "same text here", "model": "s2.1-pro-free"})
paid = tool.estimate_cost({"text": "same text here", "model": "s2.1-pro"})
assert free_after == paid

View File

@@ -77,7 +77,11 @@ class FishAudioTTS(BaseTool):
input_schema = {
"type": "object",
"required": ["text", "model"],
"required": ["text"],
"anyOf": [
{"required": ["model"]},
{"required": ["model_id"]},
],
"properties": {
"text": {"type": "string", "description": "Text to convert to speech"},
"model": {
@@ -229,11 +233,11 @@ class FishAudioTTS(BaseTool):
_DEFAULT_RATE = 0.000015
# s2.1-pro-free is a promotion, not a durable free tier: free API access runs
# through the end of July 2026, subject to Fair Use, with no SLA/latency
# through August 31, 2026, subject to Fair Use, with no SLA/latency
# guarantee, possible request retention, and commercial-use restrictions
# (https://fish.audio/ar/blog/s2-1-pro-free-api/?articleLocale=en). After the
# window, cost planning falls back to the paid s2.1-pro rate.
_S21_PRO_FREE_PROMO_END = date(2026, 7, 31)
_S21_PRO_FREE_PROMO_END = date(2026, 8, 31)
# Effective API defaults for output-affecting inputs. Applied when computing
# the idempotency key so an omitted field and its explicit default hash the