@openclaw/inworld-speech 2026.7.1 → 2026.7.2-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/dist/speech-provider.js +10 -6
- package/dist/tts.js +7 -6
- package/npm-shrinkwrap.json +2 -2
- package/package.json +4 -4
package/dist/speech-provider.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_INWORLD_MODEL_ID, INWORLD_TTS_MODELS, inworldTTS, listInworldVoices, normalizeInworldBaseUrl } from "./tts.js";
|
|
2
2
|
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
3
|
-
import { asObject, parseSpeechDirectiveNumberOverride, trimToUndefined } from "openclaw/plugin-sdk/speech-core";
|
|
3
|
+
import { asObject, parseSpeechDirectiveNumberOverride, resolveSpeechProviderApiKey, trimToUndefined } from "openclaw/plugin-sdk/speech-core";
|
|
4
4
|
import { asFiniteNumberInRange } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
5
|
//#region extensions/inworld/speech-provider.ts
|
|
6
6
|
function normalizeInworldTemperature(value) {
|
|
@@ -33,6 +33,9 @@ function readInworldProviderConfig(config) {
|
|
|
33
33
|
temperature: normalizeInworldTemperature(config.temperature) ?? defaults.temperature
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
function resolveInworldApiKey(primary, fallback) {
|
|
37
|
+
return resolveSpeechProviderApiKey(primary, fallback, process.env.INWORLD_API_KEY);
|
|
38
|
+
}
|
|
36
39
|
function readInworldOverrides(overrides) {
|
|
37
40
|
if (!overrides) return {};
|
|
38
41
|
return {
|
|
@@ -107,18 +110,19 @@ function buildInworldSpeechProvider() {
|
|
|
107
110
|
}),
|
|
108
111
|
listVoices: async (req) => {
|
|
109
112
|
const config = req.providerConfig ? readInworldProviderConfig(req.providerConfig) : void 0;
|
|
110
|
-
const apiKey = req.apiKey
|
|
113
|
+
const apiKey = resolveInworldApiKey(req.apiKey, config?.apiKey);
|
|
111
114
|
if (!apiKey) throw new Error("Inworld API key missing");
|
|
112
115
|
return listInworldVoices({
|
|
113
116
|
apiKey,
|
|
114
|
-
baseUrl: req.baseUrl ?? config?.baseUrl
|
|
117
|
+
baseUrl: req.baseUrl ?? config?.baseUrl,
|
|
118
|
+
timeoutMs: req.timeoutMs
|
|
115
119
|
});
|
|
116
120
|
},
|
|
117
|
-
isConfigured: ({ providerConfig }) => Boolean(readInworldProviderConfig(providerConfig).apiKey
|
|
121
|
+
isConfigured: ({ providerConfig }) => Boolean(resolveInworldApiKey(readInworldProviderConfig(providerConfig).apiKey)),
|
|
118
122
|
synthesize: async (req) => {
|
|
119
123
|
const config = readInworldProviderConfig(req.providerConfig);
|
|
120
124
|
const overrides = readInworldOverrides(req.providerOverrides);
|
|
121
|
-
const apiKey = config.apiKey
|
|
125
|
+
const apiKey = resolveInworldApiKey(config.apiKey);
|
|
122
126
|
if (!apiKey) throw new Error("Inworld API key missing");
|
|
123
127
|
const useOpus = req.target === "voice-note";
|
|
124
128
|
const audioEncoding = useOpus ? "OGG_OPUS" : "MP3";
|
|
@@ -141,7 +145,7 @@ function buildInworldSpeechProvider() {
|
|
|
141
145
|
synthesizeTelephony: async (req) => {
|
|
142
146
|
const config = readInworldProviderConfig(req.providerConfig);
|
|
143
147
|
const overrides = readInworldOverrides(req.providerOverrides);
|
|
144
|
-
const apiKey = config.apiKey
|
|
148
|
+
const apiKey = resolveInworldApiKey(config.apiKey);
|
|
145
149
|
if (!apiKey) throw new Error("Inworld API key missing");
|
|
146
150
|
const sampleRate = 22050;
|
|
147
151
|
return {
|
package/dist/tts.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { MAX_AUDIO_BYTES } from "openclaw/plugin-sdk/media-runtime";
|
|
2
2
|
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
3
3
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
4
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
4
5
|
//#region extensions/inworld/tts.ts
|
|
5
6
|
const DEFAULT_INWORLD_BASE_URL = "https://api.inworld.ai";
|
|
6
7
|
const DEFAULT_INWORLD_VOICE_ID = "Sarah";
|
|
7
8
|
const DEFAULT_INWORLD_MODEL_ID = "inworld-tts-1.5-max";
|
|
8
9
|
const INWORLD_TTS_BODY_MAX_BYTES = MAX_AUDIO_BYTES * 2;
|
|
9
10
|
const INWORLD_VOICES_BODY_MAX_BYTES = MAX_AUDIO_BYTES;
|
|
10
|
-
const
|
|
11
|
+
const INWORLD_UPSTREAM_IDLE_TIMEOUT_MS = 3e4;
|
|
11
12
|
const INWORLD_ERROR_BODY_MAX_BYTES = 8 * 1024;
|
|
12
13
|
const INWORLD_ERROR_BODY_MAX_CHARS = 400;
|
|
13
14
|
const INWORLD_ERROR_BODY_READ_IDLE_TIMEOUT_MS = 1e4;
|
|
@@ -34,7 +35,7 @@ async function readInworldErrorBodySnippet(response) {
|
|
|
34
35
|
return error instanceof InworldErrorBodyOverflow ? "(error body exceeded diagnostic limit; truncated)" : "";
|
|
35
36
|
}
|
|
36
37
|
const collapsed = buffer.toString("utf8").replace(/\s+/g, " ").trim();
|
|
37
|
-
if (collapsed.length > INWORLD_ERROR_BODY_MAX_CHARS) return `${collapsed
|
|
38
|
+
if (collapsed.length > INWORLD_ERROR_BODY_MAX_CHARS) return `${truncateUtf16Safe(collapsed, INWORLD_ERROR_BODY_MAX_CHARS)}…`;
|
|
38
39
|
return collapsed;
|
|
39
40
|
}
|
|
40
41
|
const INWORLD_TTS_MODELS = [
|
|
@@ -93,7 +94,7 @@ async function inworldTTS(params) {
|
|
|
93
94
|
throw new Error(`Inworld TTS API error (${response.status}): ${errorBody}`);
|
|
94
95
|
}
|
|
95
96
|
const body = (await readResponseWithLimit(response, INWORLD_TTS_BODY_MAX_BYTES, {
|
|
96
|
-
chunkTimeoutMs:
|
|
97
|
+
chunkTimeoutMs: INWORLD_UPSTREAM_IDLE_TIMEOUT_MS,
|
|
97
98
|
onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`Inworld TTS audio stream too large: ${size} bytes (limit: ${maxBytes} bytes)`),
|
|
98
99
|
onIdleTimeout: ({ chunkTimeoutMs }) => /* @__PURE__ */ new Error(`Inworld TTS audio stream stalled: no data received for ${chunkTimeoutMs}ms`)
|
|
99
100
|
})).toString("utf8");
|
|
@@ -106,7 +107,7 @@ async function inworldTTS(params) {
|
|
|
106
107
|
try {
|
|
107
108
|
parsed = JSON.parse(trimmed);
|
|
108
109
|
} catch {
|
|
109
|
-
throw new Error(`Inworld TTS stream parse error: unexpected non-JSON line: ${trimmed
|
|
110
|
+
throw new Error(`Inworld TTS stream parse error: unexpected non-JSON line: ${truncateUtf16Safe(trimmed, 80)}`);
|
|
110
111
|
}
|
|
111
112
|
if (parsed.error) throw new Error(`Inworld TTS stream error (${parsed.error.code}): ${parsed.error.message}`);
|
|
112
113
|
if (parsed.result?.audioContent) {
|
|
@@ -131,7 +132,7 @@ async function listInworldVoices(params) {
|
|
|
131
132
|
method: "GET",
|
|
132
133
|
headers: { Authorization: `Basic ${params.apiKey}` }
|
|
133
134
|
},
|
|
134
|
-
timeoutMs: params.timeoutMs,
|
|
135
|
+
timeoutMs: params.timeoutMs ?? INWORLD_UPSTREAM_IDLE_TIMEOUT_MS,
|
|
135
136
|
policy: ssrfPolicyFromInworldBaseUrl(baseUrl),
|
|
136
137
|
auditContext: "inworld-voices"
|
|
137
138
|
});
|
|
@@ -141,7 +142,7 @@ async function listInworldVoices(params) {
|
|
|
141
142
|
throw new Error(`Inworld voices API error (${response.status}): ${errorBody}`);
|
|
142
143
|
}
|
|
143
144
|
const voicesBody = (await readResponseWithLimit(response, INWORLD_VOICES_BODY_MAX_BYTES, {
|
|
144
|
-
chunkTimeoutMs:
|
|
145
|
+
chunkTimeoutMs: INWORLD_UPSTREAM_IDLE_TIMEOUT_MS,
|
|
145
146
|
onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`Inworld voices response too large: ${size} bytes (limit: ${maxBytes} bytes)`),
|
|
146
147
|
onIdleTimeout: ({ chunkTimeoutMs }) => /* @__PURE__ */ new Error(`Inworld voices response stalled: no data received for ${chunkTimeoutMs}ms`)
|
|
147
148
|
})).toString("utf8");
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/inworld-speech",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/inworld-speech",
|
|
9
|
-
"version": "2026.7.
|
|
9
|
+
"version": "2026.7.2-beta.2"
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/inworld-speech",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.2",
|
|
4
4
|
"description": "OpenClaw Inworld speech plugin.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"minHostVersion": ">=2026.6.8"
|
|
19
19
|
},
|
|
20
20
|
"compat": {
|
|
21
|
-
"pluginApi": ">=2026.7.
|
|
21
|
+
"pluginApi": ">=2026.7.2-beta.2"
|
|
22
22
|
},
|
|
23
23
|
"build": {
|
|
24
|
-
"openclawVersion": "2026.7.
|
|
24
|
+
"openclawVersion": "2026.7.2-beta.2",
|
|
25
25
|
"bundledDist": false
|
|
26
26
|
},
|
|
27
27
|
"release": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"README.md"
|
|
40
40
|
],
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"openclaw": ">=2026.7.
|
|
42
|
+
"openclaw": ">=2026.7.2-beta.2"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
45
45
|
"openclaw": {
|