- Require 'model' in the input schema and accept 'model_id' as a
selector-compatible alias (tts_selector exposes model_id); add a
selector-routing regression test
- Document s2.1-pro-free as promotional (free through end of July 2026,
Fair Use, no SLA, possible request retention, commercial-use
restrictions) in PROVIDERS.md and the Layer 3 skill; estimate_cost()
falls back to the paid s2.1-pro rate after the promo window
- Normalize voice_id/reference_id and model_id/model aliases before
computing the idempotency key, and include all output-affecting inputs
(bitrate, sample_rate, temperature, top_p, repetition_penalty, latency,
prosody, normalize, chunk_length) with API defaults applied
- Declare env:FISH_AUDIO_API_KEY in dependencies so registry metadata
reports the requirement
- Add fish_audio to the TTS provider set in the phase3 registry contract
test
Add FishAudioTTS (capability=tts) so tts_selector auto-discovers a new
high-quality, voice-clone-capable provider. Backend model is required per
call: s1 (previous flagship, kept for compatibility), s2-pro (first S2
generation), s2.1-pro (latest flagship — inline emotion tags, 80+
languages), s2.1-pro-free (free tier for drafts). s1-mini and the
speech-1.x tier have been removed from the current fish.audio API and are
no longer supported. Voice cloning via reference_id with voice_id as a
selector-compatible alias. Adds temperature/top_p/repetition_penalty
sampling controls, optional sample_rate, opus output format, and a "low"
latency tier. Cost is estimated per UTF-8 byte to match fish.audio
billing. Includes a Layer 3 skill, .env.example entry, and unit tests.
Verified end-to-end with s2.1-pro + reference_id: generated a 7-segment
Japanese narration successfully.
execute() sends sampleCount=number_of_images and estimate_cost() bills
0.04 * n, but result handling decoded only predictions[0] and wrote it to
a single output_path. Images 2..n were dropped: never decoded, never
written, absent from artifacts. The user paid for n and received one.
The result also misreported the drop rather than failing loudly --
images_generated returned len(predictions) (what the API sent) while
artifacts held a single path, so an agent picking between variants read a
count that did not match the artifact list.
Add _output_paths() and loop over every prediction, mirroring the pattern
already used by openai_image and grok_image: suffix multi-image paths
_1/_2/... so none overwrite each other, keep the exact requested path when
n=1, return all paths in artifacts, and report images_generated as the
count actually written.
Closes#388
The CI Linux FFmpeg build carried the keyed frame forward without an alpha
plane, so overlay drew opaque green over the background (corner stayed green)
instead of compositing — the E2E test failed there even though it passed on
macOS/Windows.
Force `format=yuva420p` immediately after chromakey so the keyed transparency
always has an explicit alpha plane, and size the background to the frame up
front (color=...:size=WxH, passing the probed width/height into
_process_chromakey) instead of scaling a 1x1 source with scale2ref — dropping
scale2ref also removes the format negotiation that discarded the alpha on some
builds. Output is flattened to yuv420p after the overlay.