@absolutejs/voice 0.0.22-beta.675 → 0.0.22-beta.677
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/CHANGELOG.md +19 -0
- package/changelog.json +32 -0
- package/dist/client/htmxBootstrap.js +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/index.js +30 -5
- package/dist/testing/index.js +30 -5
- package/package.json +8 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@absolutejs/voice`.
|
|
4
|
+
|
|
5
|
+
This file is generated by `absolute-changelog` from the entries in
|
|
6
|
+
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
|
+
`changelog/unreleased/`.
|
|
8
|
+
|
|
9
|
+
## 0.0.22-beta.677 — 2026-09-16
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Preserve nested OpenAI stream error codes and messages so token rate limits activate provider fallback and cooldowns.** (`createOpenAIVoiceAssistantModel`)
|
|
14
|
+
|
|
15
|
+
## 0.0.22-beta.676 — 2026-09-16
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Surface failed and incomplete OpenAI response streams as errors so voice provider fallback can recover without executing partial tool calls.** (`createOpenAIVoiceAssistantModel`)
|
package/changelog.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contract": 1,
|
|
3
|
+
"name": "@absolutejs/voice",
|
|
4
|
+
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "fixed",
|
|
9
|
+
"summary": "Preserve nested OpenAI stream error codes and messages so token rate limits activate provider fallback and cooldowns.",
|
|
10
|
+
"symbols": [
|
|
11
|
+
"createOpenAIVoiceAssistantModel"
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"date": "2026-09-16",
|
|
16
|
+
"version": "0.0.22-beta.677"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"changes": [
|
|
20
|
+
{
|
|
21
|
+
"kind": "fixed",
|
|
22
|
+
"summary": "Surface failed and incomplete OpenAI response streams as errors so voice provider fallback can recover without executing partial tool calls.",
|
|
23
|
+
"symbols": [
|
|
24
|
+
"createOpenAIVoiceAssistantModel"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"date": "2026-09-16",
|
|
29
|
+
"version": "0.0.22-beta.676"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -316,7 +316,7 @@ var serverMessageToAction = (message) => {
|
|
|
316
316
|
}
|
|
317
317
|
};
|
|
318
318
|
|
|
319
|
-
// node_modules/@absolutejs/media/dist/index.js
|
|
319
|
+
// ../../home/alexkahn/abs/voice/node_modules/@absolutejs/media/dist/index.js
|
|
320
320
|
var TAU = Math.PI * 2;
|
|
321
321
|
var pushIssue = (issues, severity, code, message) => {
|
|
322
322
|
issues.push({ code, message, severity });
|
package/dist/embed/index.js
CHANGED
|
@@ -313,7 +313,7 @@ var serverMessageToAction = (message) => {
|
|
|
313
313
|
}
|
|
314
314
|
};
|
|
315
315
|
|
|
316
|
-
// node_modules/@absolutejs/media/dist/index.js
|
|
316
|
+
// ../../home/alexkahn/abs/voice/node_modules/@absolutejs/media/dist/index.js
|
|
317
317
|
var TAU = Math.PI * 2;
|
|
318
318
|
var pushIssue = (issues, severity, code, message) => {
|
|
319
319
|
issues.push({ code, message, severity });
|
package/dist/index.js
CHANGED
|
@@ -46434,7 +46434,7 @@ var sleep7 = (ms) => new Promise((resolve2) => {
|
|
|
46434
46434
|
setTimeout(resolve2, ms);
|
|
46435
46435
|
});
|
|
46436
46436
|
var errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
46437
|
-
var defaultIsRateLimitError = (error) => /(\b429\b|rate limit|quota|too many requests)/i.test(errorMessage(error));
|
|
46437
|
+
var defaultIsRateLimitError = (error) => /(\b429\b|rate[ _-]limit|quota|too many requests)/i.test(errorMessage(error));
|
|
46438
46438
|
var normalizeRouteOutput = (output) => {
|
|
46439
46439
|
const result = {};
|
|
46440
46440
|
if (typeof output.assistantText === "string") {
|
|
@@ -46991,6 +46991,15 @@ var finalizeToolCalls = (calls) => [...calls.values()].filter((call) => call.nam
|
|
|
46991
46991
|
id: call.id,
|
|
46992
46992
|
name: call.name
|
|
46993
46993
|
}));
|
|
46994
|
+
var openAIResponseError = (response) => {
|
|
46995
|
+
if (response.status === "failed") {
|
|
46996
|
+
return new Error(`OpenAI voice assistant model failed: ${response.code ?? "response_failed"}: ${response.message ?? "Provider response failed"}`);
|
|
46997
|
+
}
|
|
46998
|
+
if (response.status === "incomplete") {
|
|
46999
|
+
return new Error(`OpenAI voice assistant model incomplete: ${response.incompleteReason ?? "unknown reason"}`);
|
|
47000
|
+
}
|
|
47001
|
+
return;
|
|
47002
|
+
};
|
|
46994
47003
|
var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) => {
|
|
46995
47004
|
let assistantText = "";
|
|
46996
47005
|
let usage;
|
|
@@ -46998,11 +47007,18 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
46998
47007
|
let incompleteReason;
|
|
46999
47008
|
let refusal = "";
|
|
47000
47009
|
let failureMessage;
|
|
47010
|
+
let streamError;
|
|
47001
47011
|
let outputItems = 0;
|
|
47002
47012
|
const calls = new Map;
|
|
47003
47013
|
await readServerSentEvents(response, (event) => {
|
|
47004
47014
|
const type = typeof event.type === "string" ? event.type : "";
|
|
47005
47015
|
const item = event.item;
|
|
47016
|
+
if (type === "error") {
|
|
47017
|
+
const nested = event.error && typeof event.error === "object" ? event.error : undefined;
|
|
47018
|
+
const code = typeof event.code === "string" ? event.code : typeof nested?.code === "string" ? nested.code : "stream_error";
|
|
47019
|
+
const message = typeof event.message === "string" ? event.message : typeof nested?.message === "string" ? nested.message : "Provider stream failed";
|
|
47020
|
+
streamError = new Error(`OpenAI voice assistant model failed: ${code}: ${message}`);
|
|
47021
|
+
}
|
|
47006
47022
|
if (type === "response.output_item.added") {
|
|
47007
47023
|
outputItems += 1;
|
|
47008
47024
|
}
|
|
@@ -47031,9 +47047,7 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
47031
47047
|
if (completed?.usage && typeof completed.usage === "object") {
|
|
47032
47048
|
usage = completed.usage;
|
|
47033
47049
|
}
|
|
47034
|
-
|
|
47035
|
-
status = completed.status;
|
|
47036
|
-
}
|
|
47050
|
+
status = typeof completed?.status === "string" ? completed.status : type.slice("response.".length);
|
|
47037
47051
|
const incomplete = completed?.incomplete_details;
|
|
47038
47052
|
if (typeof incomplete?.reason === "string") {
|
|
47039
47053
|
incompleteReason = incomplete.reason;
|
|
@@ -47042,6 +47056,12 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
47042
47056
|
if (typeof failure?.message === "string") {
|
|
47043
47057
|
failureMessage = failure.message;
|
|
47044
47058
|
}
|
|
47059
|
+
streamError ??= openAIResponseError({
|
|
47060
|
+
code: typeof failure?.code === "string" ? failure.code : undefined,
|
|
47061
|
+
incompleteReason,
|
|
47062
|
+
message: failureMessage,
|
|
47063
|
+
status: type === "response.completed" ? status : type.slice("response.".length)
|
|
47064
|
+
});
|
|
47045
47065
|
}
|
|
47046
47066
|
}, abortOptions);
|
|
47047
47067
|
return {
|
|
@@ -47051,6 +47071,7 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
47051
47071
|
outputItems,
|
|
47052
47072
|
refusal: refusal || undefined,
|
|
47053
47073
|
status,
|
|
47074
|
+
streamError,
|
|
47054
47075
|
toolCalls: finalizeToolCalls(calls),
|
|
47055
47076
|
usage
|
|
47056
47077
|
};
|
|
@@ -47119,6 +47140,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
47119
47140
|
let refusal;
|
|
47120
47141
|
let failureMessage;
|
|
47121
47142
|
let outputItems = 0;
|
|
47143
|
+
let streamError;
|
|
47122
47144
|
let firstDeltaSeen = false;
|
|
47123
47145
|
const onTextDelta = input.onTextDelta ? (delta) => {
|
|
47124
47146
|
if (!firstDeltaSeen) {
|
|
@@ -47136,7 +47158,8 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
47136
47158
|
incompleteReason,
|
|
47137
47159
|
refusal,
|
|
47138
47160
|
failureMessage,
|
|
47139
|
-
outputItems
|
|
47161
|
+
outputItems,
|
|
47162
|
+
streamError
|
|
47140
47163
|
} = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
47141
47164
|
signal: ac.signal,
|
|
47142
47165
|
inactivityMs: options.streamInactivityMs ?? 1e4
|
|
@@ -47160,6 +47183,8 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
47160
47183
|
if (usage) {
|
|
47161
47184
|
await options.onUsage?.(usage);
|
|
47162
47185
|
}
|
|
47186
|
+
if (streamError)
|
|
47187
|
+
throw streamError;
|
|
47163
47188
|
return {
|
|
47164
47189
|
...assistantText ? { assistantText } : {},
|
|
47165
47190
|
...toolCalls.length ? { toolCalls } : {},
|
package/dist/testing/index.js
CHANGED
|
@@ -5321,7 +5321,7 @@ var sleep = (ms) => new Promise((resolve2) => {
|
|
|
5321
5321
|
setTimeout(resolve2, ms);
|
|
5322
5322
|
});
|
|
5323
5323
|
var errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
5324
|
-
var defaultIsRateLimitError = (error) => /(\b429\b|rate limit|quota|too many requests)/i.test(errorMessage(error));
|
|
5324
|
+
var defaultIsRateLimitError = (error) => /(\b429\b|rate[ _-]limit|quota|too many requests)/i.test(errorMessage(error));
|
|
5325
5325
|
var normalizeRouteOutput = (output) => {
|
|
5326
5326
|
const result = {};
|
|
5327
5327
|
if (typeof output.assistantText === "string") {
|
|
@@ -5878,6 +5878,15 @@ var finalizeToolCalls = (calls) => [...calls.values()].filter((call) => call.nam
|
|
|
5878
5878
|
id: call.id,
|
|
5879
5879
|
name: call.name
|
|
5880
5880
|
}));
|
|
5881
|
+
var openAIResponseError = (response) => {
|
|
5882
|
+
if (response.status === "failed") {
|
|
5883
|
+
return new Error(`OpenAI voice assistant model failed: ${response.code ?? "response_failed"}: ${response.message ?? "Provider response failed"}`);
|
|
5884
|
+
}
|
|
5885
|
+
if (response.status === "incomplete") {
|
|
5886
|
+
return new Error(`OpenAI voice assistant model incomplete: ${response.incompleteReason ?? "unknown reason"}`);
|
|
5887
|
+
}
|
|
5888
|
+
return;
|
|
5889
|
+
};
|
|
5881
5890
|
var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) => {
|
|
5882
5891
|
let assistantText = "";
|
|
5883
5892
|
let usage;
|
|
@@ -5885,11 +5894,18 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
5885
5894
|
let incompleteReason;
|
|
5886
5895
|
let refusal = "";
|
|
5887
5896
|
let failureMessage;
|
|
5897
|
+
let streamError;
|
|
5888
5898
|
let outputItems = 0;
|
|
5889
5899
|
const calls = new Map;
|
|
5890
5900
|
await readServerSentEvents(response, (event) => {
|
|
5891
5901
|
const type = typeof event.type === "string" ? event.type : "";
|
|
5892
5902
|
const item = event.item;
|
|
5903
|
+
if (type === "error") {
|
|
5904
|
+
const nested = event.error && typeof event.error === "object" ? event.error : undefined;
|
|
5905
|
+
const code = typeof event.code === "string" ? event.code : typeof nested?.code === "string" ? nested.code : "stream_error";
|
|
5906
|
+
const message = typeof event.message === "string" ? event.message : typeof nested?.message === "string" ? nested.message : "Provider stream failed";
|
|
5907
|
+
streamError = new Error(`OpenAI voice assistant model failed: ${code}: ${message}`);
|
|
5908
|
+
}
|
|
5893
5909
|
if (type === "response.output_item.added") {
|
|
5894
5910
|
outputItems += 1;
|
|
5895
5911
|
}
|
|
@@ -5918,9 +5934,7 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
5918
5934
|
if (completed?.usage && typeof completed.usage === "object") {
|
|
5919
5935
|
usage = completed.usage;
|
|
5920
5936
|
}
|
|
5921
|
-
|
|
5922
|
-
status = completed.status;
|
|
5923
|
-
}
|
|
5937
|
+
status = typeof completed?.status === "string" ? completed.status : type.slice("response.".length);
|
|
5924
5938
|
const incomplete = completed?.incomplete_details;
|
|
5925
5939
|
if (typeof incomplete?.reason === "string") {
|
|
5926
5940
|
incompleteReason = incomplete.reason;
|
|
@@ -5929,6 +5943,12 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
5929
5943
|
if (typeof failure?.message === "string") {
|
|
5930
5944
|
failureMessage = failure.message;
|
|
5931
5945
|
}
|
|
5946
|
+
streamError ??= openAIResponseError({
|
|
5947
|
+
code: typeof failure?.code === "string" ? failure.code : undefined,
|
|
5948
|
+
incompleteReason,
|
|
5949
|
+
message: failureMessage,
|
|
5950
|
+
status: type === "response.completed" ? status : type.slice("response.".length)
|
|
5951
|
+
});
|
|
5932
5952
|
}
|
|
5933
5953
|
}, abortOptions);
|
|
5934
5954
|
return {
|
|
@@ -5938,6 +5958,7 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
5938
5958
|
outputItems,
|
|
5939
5959
|
refusal: refusal || undefined,
|
|
5940
5960
|
status,
|
|
5961
|
+
streamError,
|
|
5941
5962
|
toolCalls: finalizeToolCalls(calls),
|
|
5942
5963
|
usage
|
|
5943
5964
|
};
|
|
@@ -6006,6 +6027,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
6006
6027
|
let refusal;
|
|
6007
6028
|
let failureMessage;
|
|
6008
6029
|
let outputItems = 0;
|
|
6030
|
+
let streamError;
|
|
6009
6031
|
let firstDeltaSeen = false;
|
|
6010
6032
|
const onTextDelta = input.onTextDelta ? (delta) => {
|
|
6011
6033
|
if (!firstDeltaSeen) {
|
|
@@ -6023,7 +6045,8 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
6023
6045
|
incompleteReason,
|
|
6024
6046
|
refusal,
|
|
6025
6047
|
failureMessage,
|
|
6026
|
-
outputItems
|
|
6048
|
+
outputItems,
|
|
6049
|
+
streamError
|
|
6027
6050
|
} = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
6028
6051
|
signal: ac.signal,
|
|
6029
6052
|
inactivityMs: options.streamInactivityMs ?? 1e4
|
|
@@ -6047,6 +6070,8 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
6047
6070
|
if (usage) {
|
|
6048
6071
|
await options.onUsage?.(usage);
|
|
6049
6072
|
}
|
|
6073
|
+
if (streamError)
|
|
6074
|
+
throw streamError;
|
|
6050
6075
|
return {
|
|
6051
6076
|
...assistantText ? { assistantText } : {},
|
|
6052
6077
|
...toolCalls.length ? { toolCalls } : {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/voice",
|
|
3
|
-
"version": "0.0.22-beta.
|
|
3
|
+
"version": "0.0.22-beta.677",
|
|
4
4
|
"description": "Voice primitives and Elysia plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
"url": "https://github.com/absolutejs/voice/issues"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
|
-
"
|
|
15
|
-
"README.md"
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"README.md",
|
|
16
|
+
"changelog.json",
|
|
17
|
+
"dist"
|
|
16
18
|
],
|
|
17
19
|
"main": "./dist/index.js",
|
|
18
20
|
"types": "./dist/index.d.ts",
|
|
@@ -33,7 +35,8 @@
|
|
|
33
35
|
"test:openai": "bun test ./test/live/openai.live.test.ts",
|
|
34
36
|
"typecheck": "absolute typecheck",
|
|
35
37
|
"verify-package": "absolute-manifest verify-package",
|
|
36
|
-
"check:package": "bun run format && bun run typecheck && bun run test && bun run verify-package && bun run build"
|
|
38
|
+
"check:package": "bun run format && bun run typecheck && bun run test && bun run verify-package && bun run build && absolute-changelog check",
|
|
39
|
+
"prepublishOnly": "bun run check:package"
|
|
37
40
|
},
|
|
38
41
|
"exports": {
|
|
39
42
|
".": {
|
|
@@ -148,6 +151,7 @@
|
|
|
148
151
|
"devDependencies": {
|
|
149
152
|
"@absolutejs/absolute": "0.20.0-beta.0",
|
|
150
153
|
"@absolutejs/ai": "0.0.47",
|
|
154
|
+
"@absolutejs/changelog": "^0.6.0",
|
|
151
155
|
"@angular/core": "22.0.8",
|
|
152
156
|
"@electric-sql/pglite": "^0.4.5",
|
|
153
157
|
"@eslint/js": "^10.0.1",
|