@openclaw/fish-audio-speech 2026.7.2-beta.7 → 2026.8.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,16 @@
2
2
 
3
3
  Official OpenClaw speech provider for Fish Audio's hosted S2.1 API.
4
4
 
5
- Configure `tts.provider: "fish-audio"` and set `FISH_API_KEY`. The provider
6
- supports buffered audio, HTTP-streamed playback, native Opus voice notes,
7
- 8 kHz PCM telephony, and Fish Audio voice discovery.
5
+ Install it with:
6
+
7
+ ```bash
8
+ openclaw plugins install @openclaw/fish-audio-speech
9
+ ```
10
+
11
+ The plugin id is `fish-audio-speech`; the speech provider and TTS config id
12
+ remain `fish-audio`. Configure `tts.provider: "fish-audio"` and set
13
+ `FISH_API_KEY`. The provider supports buffered audio, HTTP-streamed playback,
14
+ native Opus voice notes, 8 kHz PCM telephony, and Fish Audio voice discovery.
8
15
 
9
16
  See [Fish Audio](https://docs.openclaw.ai/providers/fish-audio) for setup,
10
17
  models, voice selection, expressive tags, and local macOS MLX usage.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { buildFishAudioSpeechProvider } from "./speech-provider.js";
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
- //#region extensions/fish-audio/index.ts
4
- var fish_audio_default = definePluginEntry({
5
- id: "fish-audio",
3
+ //#region extensions/fish-audio-speech/index.ts
4
+ var fish_audio_speech_default = definePluginEntry({
5
+ id: "fish-audio-speech",
6
6
  name: "Fish Audio Speech",
7
7
  description: "Hosted Fish Audio S2.1 text-to-speech provider",
8
8
  register(api) {
@@ -10,4 +10,4 @@ var fish_audio_default = definePluginEntry({
10
10
  }
11
11
  });
12
12
  //#endregion
13
- export { fish_audio_default as default };
13
+ export { fish_audio_speech_default as default };
@@ -1,8 +1,9 @@
1
1
  import { FISH_AUDIO_STREAM_MAX_BYTES, fishAudioTts, fishAudioTtsStream, listFishAudioVoices, normalizeFishAudioBaseUrl } from "./tts.js";
2
2
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
3
3
  import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
4
- import { asBoolean, asFiniteNumber, asObject, parseSpeechDirectiveNumberOverride, resolveSpeechProviderApiKey, trimToUndefined } from "openclaw/plugin-sdk/speech-core";
5
- //#region extensions/fish-audio/speech-provider.ts
4
+ import { asBoolean, parseSpeechDirectiveNumberOverride, resolveSpeechProviderApiKey, trimToUndefined } from "openclaw/plugin-sdk/speech-core";
5
+ import { asFiniteNumberInRange, asOptionalRecord, parseBooleanValue } from "openclaw/plugin-sdk/string-coerce-runtime";
6
+ //#region extensions/fish-audio-speech/speech-provider.ts
6
7
  const FISH_AUDIO_MODELS = [
7
8
  "s2.1-pro-free",
8
9
  "s2.1-pro",
@@ -25,14 +26,16 @@ function normalizeLatency(value) {
25
26
  throw new Error(`invalid Fish Audio latency "${latency}"`);
26
27
  }
27
28
  function normalizeNumber(value, min, max) {
28
- const number = asFiniteNumber(value);
29
- return number != null && number >= min && number <= max ? number : void 0;
29
+ return asFiniteNumberInRange(value, {
30
+ min,
31
+ max
32
+ });
30
33
  }
31
34
  function resolveReferenceId(raw) {
32
35
  return trimToUndefined(raw?.speakerVoiceId ?? raw?.voiceId ?? raw?.referenceId);
33
36
  }
34
37
  function normalizeProviderConfig(rawConfig) {
35
- const raw = asObject(asObject(rawConfig.providers)?.["fish-audio"]) ?? asObject(rawConfig["fish-audio"]);
38
+ const raw = asOptionalRecord(asOptionalRecord(rawConfig.providers)?.["fish-audio"]) ?? asOptionalRecord(rawConfig["fish-audio"]);
36
39
  return {
37
40
  apiKey: normalizeResolvedSecretInputString({
38
41
  value: raw?.apiKey,
@@ -50,7 +53,7 @@ function normalizeProviderConfig(rawConfig) {
50
53
  }
51
54
  function readProviderConfig(config) {
52
55
  const defaults = normalizeProviderConfig({});
53
- const raw = asObject(config) ?? {};
56
+ const raw = asOptionalRecord(config) ?? {};
54
57
  return {
55
58
  apiKey: trimToUndefined(raw.apiKey) ?? defaults.apiKey,
56
59
  baseUrl: normalizeFishAudioBaseUrl(trimToUndefined(raw.baseUrl) ?? defaults.baseUrl),
@@ -64,7 +67,7 @@ function readProviderConfig(config) {
64
67
  };
65
68
  }
66
69
  function readOverrides(overrides) {
67
- const raw = asObject(overrides) ?? {};
70
+ const raw = asOptionalRecord(overrides) ?? {};
68
71
  return {
69
72
  model: trimToUndefined(raw.model ?? raw.modelId) ? normalizeModel(raw.model ?? raw.modelId) : void 0,
70
73
  referenceId: resolveReferenceId(raw),
@@ -164,29 +167,12 @@ function parseDirectiveToken(ctx) {
164
167
  case "normalize":
165
168
  case "fish_normalize": {
166
169
  if (!ctx.policy.allowNormalization) return { handled: true };
167
- const value = ctx.value.trim().toLowerCase();
168
- if ([
169
- "true",
170
- "1",
171
- "yes",
172
- "on"
173
- ].includes(value)) return {
170
+ const normalize = parseBooleanValue(ctx.value);
171
+ if (normalize !== void 0) return {
174
172
  handled: true,
175
173
  overrides: {
176
174
  ...ctx.currentOverrides,
177
- normalize: true
178
- }
179
- };
180
- if ([
181
- "false",
182
- "0",
183
- "no",
184
- "off"
185
- ].includes(value)) return {
186
- handled: true,
187
- overrides: {
188
- ...ctx.currentOverrides,
189
- normalize: false
175
+ normalize
190
176
  }
191
177
  };
192
178
  return {
package/dist/tts.js CHANGED
@@ -1,8 +1,9 @@
1
+ import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
1
2
  import { MAX_AUDIO_BYTES } from "openclaw/plugin-sdk/media-runtime";
2
3
  import { assertOkOrThrowProviderError, assertProviderBinaryResponseContent, readProviderBinaryResponse, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
3
- import { asObject, trimToUndefined } from "openclaw/plugin-sdk/speech";
4
+ import { trimToUndefined } from "openclaw/plugin-sdk/speech";
4
5
  import { fetchWithSsrFGuard, ssrfPolicyFromHttpBaseUrlAllowedHostname } from "openclaw/plugin-sdk/ssrf-runtime";
5
- //#region extensions/fish-audio/tts.ts
6
+ //#region extensions/fish-audio-speech/tts.ts
6
7
  const FISH_AUDIO_BASE_URL = "https://api.fish.audio";
7
8
  const FISH_AUDIO_VOICES_MAX_BYTES = 2 * 1024 * 1024;
8
9
  const FISH_AUDIO_VOICE_PAGE_SIZE = 100;
@@ -139,7 +140,7 @@ async function fishAudioTtsStream(params) {
139
140
  }
140
141
  }
141
142
  function parseVoiceItem(value) {
142
- const item = asObject(value);
143
+ const item = asOptionalRecord(value);
143
144
  const id = trimToUndefined(item?.["_id"]);
144
145
  if (!id) return;
145
146
  const languages = Array.isArray(item?.languages) ? item.languages.flatMap((entry) => typeof entry === "string" && entry.trim() ? [entry.trim()] : []) : [];
@@ -1,5 +1,6 @@
1
1
  {
2
- "id": "fish-audio",
2
+ "id": "fish-audio-speech",
3
+ "legacyPluginIds": ["fish-audio"],
3
4
  "activation": {
4
5
  "onStartup": false
5
6
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fish-audio-speech",
3
- "version": "2026.7.2-beta.7",
3
+ "version": "2026.8.1-beta.2",
4
4
  "description": "OpenClaw Fish Audio speech plugin.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,10 +18,10 @@
18
18
  "minHostVersion": ">=2026.7.2"
19
19
  },
20
20
  "compat": {
21
- "pluginApi": ">=2026.7.2-beta.7"
21
+ "pluginApi": ">=2026.8.1-beta.2"
22
22
  },
23
23
  "build": {
24
- "openclawVersion": "2026.7.2-beta.7",
24
+ "openclawVersion": "2026.8.1-beta.2",
25
25
  "bundledDist": false
26
26
  },
27
27
  "release": {
@@ -38,7 +38,7 @@
38
38
  "README.md"
39
39
  ],
40
40
  "peerDependencies": {
41
- "openclaw": ">=2026.7.2-beta.7"
41
+ "openclaw": ">=2026.8.1-beta.2"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "openclaw": {