@absolutejs/voice 0.0.22-beta.167 → 0.0.22-beta.169
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/angular/index.d.ts +1 -0
- package/dist/angular/index.js +177 -58
- package/dist/angular/voice-provider-contracts.service.d.ts +12 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +234 -47
- package/dist/client/providerContracts.d.ts +19 -0
- package/dist/client/providerContractsWidget.d.ts +37 -0
- package/dist/index.js +38 -2
- package/dist/providerStackRecommendations.d.ts +8 -0
- package/dist/react/VoiceProviderContracts.d.ts +6 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +494 -195
- package/dist/react/useVoiceProviderContracts.d.ts +8 -0
- package/dist/svelte/createVoiceProviderContracts.d.ts +10 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +270 -78
- package/dist/vue/VoiceProviderContracts.d.ts +21 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.js +460 -170
- package/dist/vue/useVoiceProviderContracts.d.ts +9 -0
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -3156,6 +3156,80 @@ var createVoiceProviderCapabilitiesStore = (path = "/api/provider-capabilities",
|
|
|
3156
3156
|
}
|
|
3157
3157
|
};
|
|
3158
3158
|
};
|
|
3159
|
+
// src/client/providerContracts.ts
|
|
3160
|
+
var fetchVoiceProviderContracts = async (path = "/api/provider-contracts", options = {}) => {
|
|
3161
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
3162
|
+
const response = await fetchImpl(path);
|
|
3163
|
+
if (!response.ok) {
|
|
3164
|
+
throw new Error(`Voice provider contracts failed: HTTP ${response.status}`);
|
|
3165
|
+
}
|
|
3166
|
+
return await response.json();
|
|
3167
|
+
};
|
|
3168
|
+
var createVoiceProviderContractsStore = (path = "/api/provider-contracts", options = {}) => {
|
|
3169
|
+
const listeners = new Set;
|
|
3170
|
+
let closed = false;
|
|
3171
|
+
let timer;
|
|
3172
|
+
let snapshot = {
|
|
3173
|
+
error: null,
|
|
3174
|
+
isLoading: false
|
|
3175
|
+
};
|
|
3176
|
+
const emit = () => {
|
|
3177
|
+
for (const listener of listeners) {
|
|
3178
|
+
listener();
|
|
3179
|
+
}
|
|
3180
|
+
};
|
|
3181
|
+
const refresh = async () => {
|
|
3182
|
+
if (closed) {
|
|
3183
|
+
return snapshot.report;
|
|
3184
|
+
}
|
|
3185
|
+
snapshot = { ...snapshot, error: null, isLoading: true };
|
|
3186
|
+
emit();
|
|
3187
|
+
try {
|
|
3188
|
+
const report = await fetchVoiceProviderContracts(path, options);
|
|
3189
|
+
snapshot = {
|
|
3190
|
+
error: null,
|
|
3191
|
+
isLoading: false,
|
|
3192
|
+
report,
|
|
3193
|
+
updatedAt: Date.now()
|
|
3194
|
+
};
|
|
3195
|
+
emit();
|
|
3196
|
+
return report;
|
|
3197
|
+
} catch (error) {
|
|
3198
|
+
snapshot = {
|
|
3199
|
+
...snapshot,
|
|
3200
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3201
|
+
isLoading: false
|
|
3202
|
+
};
|
|
3203
|
+
emit();
|
|
3204
|
+
throw error;
|
|
3205
|
+
}
|
|
3206
|
+
};
|
|
3207
|
+
const close = () => {
|
|
3208
|
+
closed = true;
|
|
3209
|
+
if (timer) {
|
|
3210
|
+
clearInterval(timer);
|
|
3211
|
+
timer = undefined;
|
|
3212
|
+
}
|
|
3213
|
+
listeners.clear();
|
|
3214
|
+
};
|
|
3215
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
3216
|
+
timer = setInterval(() => {
|
|
3217
|
+
refresh().catch(() => {});
|
|
3218
|
+
}, options.intervalMs);
|
|
3219
|
+
}
|
|
3220
|
+
return {
|
|
3221
|
+
close,
|
|
3222
|
+
getServerSnapshot: () => snapshot,
|
|
3223
|
+
getSnapshot: () => snapshot,
|
|
3224
|
+
refresh,
|
|
3225
|
+
subscribe: (listener) => {
|
|
3226
|
+
listeners.add(listener);
|
|
3227
|
+
return () => {
|
|
3228
|
+
listeners.delete(listener);
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
};
|
|
3232
|
+
};
|
|
3159
3233
|
// src/client/turnQuality.ts
|
|
3160
3234
|
var fetchVoiceTurnQuality = async (path = "/api/turn-quality", options = {}) => {
|
|
3161
3235
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -3939,10 +4013,116 @@ var defineVoiceProviderCapabilitiesElement = (tagName = "absolute-voice-provider
|
|
|
3939
4013
|
}
|
|
3940
4014
|
});
|
|
3941
4015
|
};
|
|
3942
|
-
// src/client/
|
|
3943
|
-
var DEFAULT_TITLE7 = "
|
|
3944
|
-
var DEFAULT_DESCRIPTION7 = "
|
|
4016
|
+
// src/client/providerContractsWidget.ts
|
|
4017
|
+
var DEFAULT_TITLE7 = "Provider Contracts";
|
|
4018
|
+
var DEFAULT_DESCRIPTION7 = "Production contract coverage for provider env, latency, fallback, streaming, and capabilities.";
|
|
3945
4019
|
var escapeHtml9 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4020
|
+
var formatProvider3 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
4021
|
+
var formatStatus3 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
4022
|
+
var contractDetail = (row) => {
|
|
4023
|
+
const failing = row.checks.filter((check) => check.status !== "pass");
|
|
4024
|
+
if (failing.length === 0) {
|
|
4025
|
+
return "Provider contract is production-ready.";
|
|
4026
|
+
}
|
|
4027
|
+
return failing.map((check) => `${check.label}: ${check.detail ?? check.status}`).join(" ");
|
|
4028
|
+
};
|
|
4029
|
+
var createVoiceProviderContractsViewModel = (snapshot, options = {}) => {
|
|
4030
|
+
const rows = (snapshot.report?.rows ?? []).map((row) => ({
|
|
4031
|
+
...row,
|
|
4032
|
+
detail: contractDetail(row),
|
|
4033
|
+
label: `${formatProvider3(row.provider)} ${row.kind.toUpperCase()}`,
|
|
4034
|
+
remediations: row.checks.filter((check) => check.status !== "pass" && check.remediation).map((check) => ({
|
|
4035
|
+
detail: check.remediation?.detail ?? "",
|
|
4036
|
+
href: check.remediation?.href,
|
|
4037
|
+
label: check.remediation?.label ?? check.label
|
|
4038
|
+
})),
|
|
4039
|
+
rows: [
|
|
4040
|
+
{ label: "Status", value: formatStatus3(row.status) },
|
|
4041
|
+
{ label: "Selected", value: row.selected ? "Yes" : "No" },
|
|
4042
|
+
{ label: "Configured", value: row.configured ? "Yes" : "No" },
|
|
4043
|
+
{
|
|
4044
|
+
label: "Checks",
|
|
4045
|
+
value: row.checks.map((check) => `${check.label}: ${formatStatus3(check.status)}`).join(", ")
|
|
4046
|
+
}
|
|
4047
|
+
]
|
|
4048
|
+
}));
|
|
4049
|
+
const warningCount = snapshot.report ? snapshot.report.failed + snapshot.report.warned : rows.filter((row) => row.status !== "pass").length;
|
|
4050
|
+
return {
|
|
4051
|
+
description: options.description ?? DEFAULT_DESCRIPTION7,
|
|
4052
|
+
error: snapshot.error,
|
|
4053
|
+
isLoading: snapshot.isLoading,
|
|
4054
|
+
label: snapshot.error ? "Unavailable" : rows.length ? warningCount > 0 ? `${warningCount} needs attention` : `${rows.length} passing` : snapshot.isLoading ? "Checking" : "No contracts",
|
|
4055
|
+
rows,
|
|
4056
|
+
status: snapshot.error ? "error" : rows.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
4057
|
+
title: options.title ?? DEFAULT_TITLE7,
|
|
4058
|
+
updatedAt: snapshot.updatedAt
|
|
4059
|
+
};
|
|
4060
|
+
};
|
|
4061
|
+
var renderVoiceProviderContractsHTML = (snapshot, options = {}) => {
|
|
4062
|
+
const model = createVoiceProviderContractsViewModel(snapshot, options);
|
|
4063
|
+
const rows = model.rows.length ? `<div class="absolute-voice-provider-contracts__rows">${model.rows.map((row) => `<article class="absolute-voice-provider-contracts__row absolute-voice-provider-contracts__row--${escapeHtml9(row.status)}">
|
|
4064
|
+
<header>
|
|
4065
|
+
<strong>${escapeHtml9(row.label)}</strong>
|
|
4066
|
+
<span>${escapeHtml9(formatStatus3(row.status))}</span>
|
|
4067
|
+
</header>
|
|
4068
|
+
<p>${escapeHtml9(row.detail)}</p>
|
|
4069
|
+
${row.remediations.length ? `<ul class="absolute-voice-provider-contracts__remediations">${row.remediations.map((remediation) => `<li>${remediation.href ? `<a href="${escapeHtml9(remediation.href)}">${escapeHtml9(remediation.label)}</a>` : `<strong>${escapeHtml9(remediation.label)}</strong>`}<span>${escapeHtml9(remediation.detail)}</span></li>`).join("")}</ul>` : ""}
|
|
4070
|
+
<dl>${row.rows.map((item) => `<div>
|
|
4071
|
+
<dt>${escapeHtml9(item.label)}</dt>
|
|
4072
|
+
<dd>${escapeHtml9(item.value)}</dd>
|
|
4073
|
+
</div>`).join("")}</dl>
|
|
4074
|
+
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-contracts__empty">Configure provider contracts to see production coverage.</p>';
|
|
4075
|
+
return `<section class="absolute-voice-provider-contracts absolute-voice-provider-contracts--${escapeHtml9(model.status)}">
|
|
4076
|
+
<header class="absolute-voice-provider-contracts__header">
|
|
4077
|
+
<span class="absolute-voice-provider-contracts__eyebrow">${escapeHtml9(model.title)}</span>
|
|
4078
|
+
<strong class="absolute-voice-provider-contracts__label">${escapeHtml9(model.label)}</strong>
|
|
4079
|
+
</header>
|
|
4080
|
+
<p class="absolute-voice-provider-contracts__description">${escapeHtml9(model.description)}</p>
|
|
4081
|
+
${rows}
|
|
4082
|
+
${model.error ? `<p class="absolute-voice-provider-contracts__error">${escapeHtml9(model.error)}</p>` : ""}
|
|
4083
|
+
</section>`;
|
|
4084
|
+
};
|
|
4085
|
+
var getVoiceProviderContractsCSS = () => `.absolute-voice-provider-contracts{border:1px solid #b8dcc7;border-radius:20px;background:#f7fff9;color:#09140d;padding:18px;box-shadow:0 18px 40px rgba(21,83,45,.12);font-family:inherit}.absolute-voice-provider-contracts--error,.absolute-voice-provider-contracts--warning{border-color:#f2a7a7;background:#fff7f4}.absolute-voice-provider-contracts__header,.absolute-voice-provider-contracts__row header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-contracts__eyebrow{color:#166534;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-contracts__label{font-size:24px;line-height:1}.absolute-voice-provider-contracts__description,.absolute-voice-provider-contracts__row p,.absolute-voice-provider-contracts__row dt,.absolute-voice-provider-contracts__empty{color:#405448}.absolute-voice-provider-contracts__rows{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-contracts__row{background:#fff;border:1px solid #d6eadb;border-radius:16px;padding:14px}.absolute-voice-provider-contracts__row--pass{border-color:#86efac}.absolute-voice-provider-contracts__row--warn,.absolute-voice-provider-contracts__row--fail{border-color:#f2a7a7}.absolute-voice-provider-contracts__row p{margin:10px 0}.absolute-voice-provider-contracts__remediations{display:grid;gap:8px;list-style:none;margin:0 0 10px;padding:0}.absolute-voice-provider-contracts__remediations li{background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;display:grid;gap:3px;padding:8px}.absolute-voice-provider-contracts__remediations a,.absolute-voice-provider-contracts__remediations strong{color:#9a3412}.absolute-voice-provider-contracts__remediations span{color:#7c2d12}.absolute-voice-provider-contracts__row dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-contracts__row div{background:#f7fff9;border:1px solid #d6eadb;border-radius:12px;padding:8px}.absolute-voice-provider-contracts__row dt{font-size:12px}.absolute-voice-provider-contracts__row dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-contracts__error{color:#9f1239;font-weight:700}`;
|
|
4086
|
+
var mountVoiceProviderContracts = (element, path = "/api/provider-contracts", options = {}) => {
|
|
4087
|
+
const store = createVoiceProviderContractsStore(path, options);
|
|
4088
|
+
const render = () => {
|
|
4089
|
+
element.innerHTML = renderVoiceProviderContractsHTML(store.getSnapshot(), options);
|
|
4090
|
+
};
|
|
4091
|
+
const unsubscribe = store.subscribe(render);
|
|
4092
|
+
render();
|
|
4093
|
+
store.refresh().catch(() => {});
|
|
4094
|
+
return {
|
|
4095
|
+
close: () => {
|
|
4096
|
+
unsubscribe();
|
|
4097
|
+
store.close();
|
|
4098
|
+
},
|
|
4099
|
+
refresh: store.refresh
|
|
4100
|
+
};
|
|
4101
|
+
};
|
|
4102
|
+
var defineVoiceProviderContractsElement = (tagName = "absolute-voice-provider-contracts") => {
|
|
4103
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
4104
|
+
return;
|
|
4105
|
+
}
|
|
4106
|
+
customElements.define(tagName, class AbsoluteVoiceProviderContractsElement extends HTMLElement {
|
|
4107
|
+
mounted;
|
|
4108
|
+
connectedCallback() {
|
|
4109
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
4110
|
+
this.mounted = mountVoiceProviderContracts(this, this.getAttribute("path") ?? "/api/provider-contracts", {
|
|
4111
|
+
description: this.getAttribute("description") ?? undefined,
|
|
4112
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
4113
|
+
title: this.getAttribute("title") ?? undefined
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
4116
|
+
disconnectedCallback() {
|
|
4117
|
+
this.mounted?.close();
|
|
4118
|
+
this.mounted = undefined;
|
|
4119
|
+
}
|
|
4120
|
+
});
|
|
4121
|
+
};
|
|
4122
|
+
// src/client/turnQualityWidget.ts
|
|
4123
|
+
var DEFAULT_TITLE8 = "Turn Quality";
|
|
4124
|
+
var DEFAULT_DESCRIPTION8 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
|
|
4125
|
+
var escapeHtml10 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
3946
4126
|
var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
|
|
3947
4127
|
var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
|
|
3948
4128
|
var getTurnDetail = (turn) => {
|
|
@@ -3980,37 +4160,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
|
|
|
3980
4160
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
3981
4161
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
3982
4162
|
return {
|
|
3983
|
-
description: options.description ??
|
|
4163
|
+
description: options.description ?? DEFAULT_DESCRIPTION8,
|
|
3984
4164
|
error: snapshot.error,
|
|
3985
4165
|
isLoading: snapshot.isLoading,
|
|
3986
4166
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
|
|
3987
4167
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
3988
|
-
title: options.title ??
|
|
4168
|
+
title: options.title ?? DEFAULT_TITLE8,
|
|
3989
4169
|
turns,
|
|
3990
4170
|
updatedAt: snapshot.updatedAt
|
|
3991
4171
|
};
|
|
3992
4172
|
};
|
|
3993
4173
|
var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
|
|
3994
4174
|
const model = createVoiceTurnQualityViewModel(snapshot, options);
|
|
3995
|
-
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${
|
|
4175
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml10(turn.status)}">
|
|
3996
4176
|
<header>
|
|
3997
|
-
<strong>${
|
|
3998
|
-
<span>${
|
|
4177
|
+
<strong>${escapeHtml10(turn.label)}</strong>
|
|
4178
|
+
<span>${escapeHtml10(turn.status)}</span>
|
|
3999
4179
|
</header>
|
|
4000
|
-
<p>${
|
|
4180
|
+
<p>${escapeHtml10(turn.detail)}</p>
|
|
4001
4181
|
<dl>${turn.rows.map((row) => `<div>
|
|
4002
|
-
<dt>${
|
|
4003
|
-
<dd>${
|
|
4182
|
+
<dt>${escapeHtml10(row.label)}</dt>
|
|
4183
|
+
<dd>${escapeHtml10(row.value)}</dd>
|
|
4004
4184
|
</div>`).join("")}</dl>
|
|
4005
4185
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
|
|
4006
|
-
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${
|
|
4186
|
+
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml10(model.status)}">
|
|
4007
4187
|
<header class="absolute-voice-turn-quality__header">
|
|
4008
|
-
<span class="absolute-voice-turn-quality__eyebrow">${
|
|
4009
|
-
<strong class="absolute-voice-turn-quality__label">${
|
|
4188
|
+
<span class="absolute-voice-turn-quality__eyebrow">${escapeHtml10(model.title)}</span>
|
|
4189
|
+
<strong class="absolute-voice-turn-quality__label">${escapeHtml10(model.label)}</strong>
|
|
4010
4190
|
</header>
|
|
4011
|
-
<p class="absolute-voice-turn-quality__description">${
|
|
4191
|
+
<p class="absolute-voice-turn-quality__description">${escapeHtml10(model.description)}</p>
|
|
4012
4192
|
${turns}
|
|
4013
|
-
${model.error ? `<p class="absolute-voice-turn-quality__error">${
|
|
4193
|
+
${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml10(model.error)}</p>` : ""}
|
|
4014
4194
|
</section>`;
|
|
4015
4195
|
};
|
|
4016
4196
|
var getVoiceTurnQualityCSS = () => `.absolute-voice-turn-quality{border:1px solid #e4d1a3;border-radius:20px;background:#fff9eb;color:#17120a;padding:18px;box-shadow:0 18px 40px rgba(73,48,14,.12);font-family:inherit}.absolute-voice-turn-quality--error,.absolute-voice-turn-quality--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-turn-quality__header,.absolute-voice-turn-quality__turn header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-turn-quality__eyebrow{color:#8a5a0a;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-turn-quality__label{font-size:24px;line-height:1}.absolute-voice-turn-quality__description,.absolute-voice-turn-quality__turn p,.absolute-voice-turn-quality__turn dt,.absolute-voice-turn-quality__empty{color:#5a4930}.absolute-voice-turn-quality__turns{display:grid;gap:12px;margin-top:14px}.absolute-voice-turn-quality__turn{background:#fff;border:1px solid #f0dfba;border-radius:16px;padding:14px}.absolute-voice-turn-quality__turn--pass{border-color:#86efac}.absolute-voice-turn-quality__turn--warn,.absolute-voice-turn-quality__turn--unknown{border-color:#fbbf24}.absolute-voice-turn-quality__turn--fail{border-color:#f2a7a7}.absolute-voice-turn-quality__turn p{margin:10px 0}.absolute-voice-turn-quality__turn dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-turn-quality__turn div{background:#fff9eb;border:1px solid #f0dfba;border-radius:12px;padding:8px}.absolute-voice-turn-quality__turn dt{font-size:12px}.absolute-voice-turn-quality__turn dd{font-weight:800;margin:4px 0 0}.absolute-voice-turn-quality__empty{margin:14px 0 0}.absolute-voice-turn-quality__error{color:#9f1239;font-weight:700}`;
|
|
@@ -4051,10 +4231,10 @@ var defineVoiceTurnQualityElement = (tagName = "absolute-voice-turn-quality") =>
|
|
|
4051
4231
|
});
|
|
4052
4232
|
};
|
|
4053
4233
|
// src/client/turnLatencyWidget.ts
|
|
4054
|
-
var
|
|
4055
|
-
var
|
|
4234
|
+
var DEFAULT_TITLE9 = "Turn Latency";
|
|
4235
|
+
var DEFAULT_DESCRIPTION9 = "Per-turn timing from first transcript to commit and assistant response start.";
|
|
4056
4236
|
var DEFAULT_PROOF_LABEL = "Run latency proof";
|
|
4057
|
-
var
|
|
4237
|
+
var escapeHtml11 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4058
4238
|
var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
4059
4239
|
var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
4060
4240
|
const turns = (snapshot.report?.turns ?? []).map((turn) => ({
|
|
@@ -4068,39 +4248,39 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
|
4068
4248
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
4069
4249
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
4070
4250
|
return {
|
|
4071
|
-
description: options.description ??
|
|
4251
|
+
description: options.description ?? DEFAULT_DESCRIPTION9,
|
|
4072
4252
|
error: snapshot.error,
|
|
4073
4253
|
isLoading: snapshot.isLoading,
|
|
4074
4254
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
|
|
4075
4255
|
proofLabel: options.proofPath ? options.proofLabel ?? DEFAULT_PROOF_LABEL : undefined,
|
|
4076
4256
|
showProofAction: Boolean(options.proofPath),
|
|
4077
4257
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
4078
|
-
title: options.title ??
|
|
4258
|
+
title: options.title ?? DEFAULT_TITLE9,
|
|
4079
4259
|
turns,
|
|
4080
4260
|
updatedAt: snapshot.updatedAt
|
|
4081
4261
|
};
|
|
4082
4262
|
};
|
|
4083
4263
|
var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
|
|
4084
4264
|
const model = createVoiceTurnLatencyViewModel(snapshot, options);
|
|
4085
|
-
const turns = model.turns.length ? `<div class="absolute-voice-turn-latency__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-latency__turn absolute-voice-turn-latency__turn--${
|
|
4265
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-latency__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-latency__turn absolute-voice-turn-latency__turn--${escapeHtml11(turn.status)}">
|
|
4086
4266
|
<header>
|
|
4087
|
-
<strong>${
|
|
4088
|
-
<span>${
|
|
4267
|
+
<strong>${escapeHtml11(turn.label)}</strong>
|
|
4268
|
+
<span>${escapeHtml11(turn.status)}</span>
|
|
4089
4269
|
</header>
|
|
4090
4270
|
<dl>${turn.rows.map((row) => `<div>
|
|
4091
|
-
<dt>${
|
|
4092
|
-
<dd>${
|
|
4271
|
+
<dt>${escapeHtml11(row.label)}</dt>
|
|
4272
|
+
<dd>${escapeHtml11(row.value)}</dd>
|
|
4093
4273
|
</div>`).join("")}</dl>
|
|
4094
4274
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
|
|
4095
|
-
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${
|
|
4275
|
+
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml11(model.status)}">
|
|
4096
4276
|
<header class="absolute-voice-turn-latency__header">
|
|
4097
|
-
<span class="absolute-voice-turn-latency__eyebrow">${
|
|
4098
|
-
<strong class="absolute-voice-turn-latency__label">${
|
|
4277
|
+
<span class="absolute-voice-turn-latency__eyebrow">${escapeHtml11(model.title)}</span>
|
|
4278
|
+
<strong class="absolute-voice-turn-latency__label">${escapeHtml11(model.label)}</strong>
|
|
4099
4279
|
</header>
|
|
4100
|
-
<p class="absolute-voice-turn-latency__description">${
|
|
4101
|
-
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${
|
|
4280
|
+
<p class="absolute-voice-turn-latency__description">${escapeHtml11(model.description)}</p>
|
|
4281
|
+
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml11(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
|
|
4102
4282
|
${turns}
|
|
4103
|
-
${model.error ? `<p class="absolute-voice-turn-latency__error">${
|
|
4283
|
+
${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml11(model.error)}</p>` : ""}
|
|
4104
4284
|
</section>`;
|
|
4105
4285
|
};
|
|
4106
4286
|
var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
|
|
@@ -4150,9 +4330,9 @@ var defineVoiceTurnLatencyElement = (tagName = "absolute-voice-turn-latency") =>
|
|
|
4150
4330
|
});
|
|
4151
4331
|
};
|
|
4152
4332
|
// src/client/traceTimelineWidget.ts
|
|
4153
|
-
var
|
|
4154
|
-
var
|
|
4155
|
-
var
|
|
4333
|
+
var DEFAULT_TITLE10 = "Voice Traces";
|
|
4334
|
+
var DEFAULT_DESCRIPTION10 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
|
|
4335
|
+
var escapeHtml12 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4156
4336
|
var formatMs2 = (value) => typeof value === "number" ? `${value}ms` : "n/a";
|
|
4157
4337
|
var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
|
|
4158
4338
|
var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
@@ -4166,34 +4346,34 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
|
4166
4346
|
const failed = sessions.filter((session) => session.status === "failed").length;
|
|
4167
4347
|
const warnings = sessions.filter((session) => session.status === "warning").length;
|
|
4168
4348
|
return {
|
|
4169
|
-
description: options.description ??
|
|
4349
|
+
description: options.description ?? DEFAULT_DESCRIPTION10,
|
|
4170
4350
|
error: snapshot.error,
|
|
4171
4351
|
isLoading: snapshot.isLoading,
|
|
4172
4352
|
label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
|
|
4173
4353
|
sessions,
|
|
4174
4354
|
status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
4175
|
-
title: options.title ??
|
|
4355
|
+
title: options.title ?? DEFAULT_TITLE10,
|
|
4176
4356
|
updatedAt: snapshot.updatedAt
|
|
4177
4357
|
};
|
|
4178
4358
|
};
|
|
4179
4359
|
var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
|
|
4180
4360
|
const model = createVoiceTraceTimelineViewModel(snapshot, options);
|
|
4181
|
-
const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${
|
|
4361
|
+
const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${escapeHtml12(session.status)}">
|
|
4182
4362
|
<header>
|
|
4183
|
-
<strong>${
|
|
4184
|
-
<span>${
|
|
4363
|
+
<strong>${escapeHtml12(session.sessionId)}</strong>
|
|
4364
|
+
<span>${escapeHtml12(session.status)}</span>
|
|
4185
4365
|
</header>
|
|
4186
|
-
<p>${
|
|
4187
|
-
<a href="${
|
|
4366
|
+
<p>${escapeHtml12(session.label)} \xB7 ${escapeHtml12(session.durationLabel)} \xB7 ${escapeHtml12(session.providerLabel)}</p>
|
|
4367
|
+
<a href="${escapeHtml12(session.detailHref)}">Open timeline</a>
|
|
4188
4368
|
</article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
|
|
4189
|
-
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${
|
|
4369
|
+
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml12(model.status)}">
|
|
4190
4370
|
<header class="absolute-voice-trace-timeline__header">
|
|
4191
|
-
<span class="absolute-voice-trace-timeline__eyebrow">${
|
|
4192
|
-
<strong class="absolute-voice-trace-timeline__label">${
|
|
4371
|
+
<span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml12(model.title)}</span>
|
|
4372
|
+
<strong class="absolute-voice-trace-timeline__label">${escapeHtml12(model.label)}</strong>
|
|
4193
4373
|
</header>
|
|
4194
|
-
<p class="absolute-voice-trace-timeline__description">${
|
|
4374
|
+
<p class="absolute-voice-trace-timeline__description">${escapeHtml12(model.description)}</p>
|
|
4195
4375
|
${sessions}
|
|
4196
|
-
${model.error ? `<p class="absolute-voice-trace-timeline__error">${
|
|
4376
|
+
${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml12(model.error)}</p>` : ""}
|
|
4197
4377
|
</section>`;
|
|
4198
4378
|
};
|
|
4199
4379
|
var getVoiceTraceTimelineCSS = () => `.absolute-voice-trace-timeline{border:1px solid #bad7d3;border-radius:20px;background:#f3fffb;color:#09201c;padding:18px;box-shadow:0 18px 40px rgba(9,32,28,.12);font-family:inherit}.absolute-voice-trace-timeline--error,.absolute-voice-trace-timeline--failed{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-trace-timeline--warning{border-color:#fbbf24;background:#fffaf0}.absolute-voice-trace-timeline__header,.absolute-voice-trace-timeline__session header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-trace-timeline__eyebrow{color:#17665b;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-trace-timeline__label{font-size:24px;line-height:1}.absolute-voice-trace-timeline__description,.absolute-voice-trace-timeline__session p,.absolute-voice-trace-timeline__empty{color:#35544f}.absolute-voice-trace-timeline__sessions{display:grid;gap:12px;margin-top:14px}.absolute-voice-trace-timeline__session{background:#fff;border:1px solid #cfe7e2;border-radius:16px;padding:14px}.absolute-voice-trace-timeline__session--failed{border-color:#f2a7a7}.absolute-voice-trace-timeline__session--warning{border-color:#fbbf24}.absolute-voice-trace-timeline__session p{margin:10px 0}.absolute-voice-trace-timeline__session a{color:#0f766e;font-weight:800}.absolute-voice-trace-timeline__empty{margin:14px 0 0}.absolute-voice-trace-timeline__error{color:#9f1239;font-weight:700}`;
|
|
@@ -4325,6 +4505,7 @@ export {
|
|
|
4325
4505
|
renderVoiceRoutingStatusHTML,
|
|
4326
4506
|
renderVoiceProviderStatusHTML,
|
|
4327
4507
|
renderVoiceProviderSimulationControlsHTML,
|
|
4508
|
+
renderVoiceProviderContractsHTML,
|
|
4328
4509
|
renderVoiceProviderCapabilitiesHTML,
|
|
4329
4510
|
renderVoiceOpsStatusHTML,
|
|
4330
4511
|
renderVoiceOpsActionHistoryWidgetHTML,
|
|
@@ -4337,6 +4518,7 @@ export {
|
|
|
4337
4518
|
mountVoiceRoutingStatus,
|
|
4338
4519
|
mountVoiceProviderStatus,
|
|
4339
4520
|
mountVoiceProviderSimulationControls,
|
|
4521
|
+
mountVoiceProviderContracts,
|
|
4340
4522
|
mountVoiceProviderCapabilities,
|
|
4341
4523
|
mountVoiceOpsStatus,
|
|
4342
4524
|
mountVoiceOpsActionHistory,
|
|
@@ -4346,6 +4528,7 @@ export {
|
|
|
4346
4528
|
getVoiceTraceTimelineCSS,
|
|
4347
4529
|
getVoiceRoutingStatusCSS,
|
|
4348
4530
|
getVoiceProviderStatusCSS,
|
|
4531
|
+
getVoiceProviderContractsCSS,
|
|
4349
4532
|
getVoiceProviderCapabilitiesCSS,
|
|
4350
4533
|
getVoiceOpsStatusLabel,
|
|
4351
4534
|
getVoiceOpsStatusCSS,
|
|
@@ -4358,6 +4541,7 @@ export {
|
|
|
4358
4541
|
fetchVoiceTraceTimeline,
|
|
4359
4542
|
fetchVoiceRoutingStatus,
|
|
4360
4543
|
fetchVoiceProviderStatus,
|
|
4544
|
+
fetchVoiceProviderContracts,
|
|
4361
4545
|
fetchVoiceProviderCapabilities,
|
|
4362
4546
|
fetchVoiceOpsStatus,
|
|
4363
4547
|
fetchVoiceOpsActionHistory,
|
|
@@ -4369,6 +4553,7 @@ export {
|
|
|
4369
4553
|
defineVoiceRoutingStatusElement,
|
|
4370
4554
|
defineVoiceProviderStatusElement,
|
|
4371
4555
|
defineVoiceProviderSimulationControlsElement,
|
|
4556
|
+
defineVoiceProviderContractsElement,
|
|
4372
4557
|
defineVoiceProviderCapabilitiesElement,
|
|
4373
4558
|
defineVoiceOpsStatusElement,
|
|
4374
4559
|
defineVoiceOpsActionCenterElement,
|
|
@@ -4388,6 +4573,8 @@ export {
|
|
|
4388
4573
|
createVoiceProviderStatusStore,
|
|
4389
4574
|
createVoiceProviderSimulationControlsViewModel,
|
|
4390
4575
|
createVoiceProviderSimulationControlsStore,
|
|
4576
|
+
createVoiceProviderContractsViewModel,
|
|
4577
|
+
createVoiceProviderContractsStore,
|
|
4391
4578
|
createVoiceProviderCapabilitiesViewModel,
|
|
4392
4579
|
createVoiceProviderCapabilitiesStore,
|
|
4393
4580
|
createVoiceOpsStatusViewModel,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VoiceProviderContractMatrixReport } from '../providerStackRecommendations';
|
|
2
|
+
export type VoiceProviderContractsClientOptions = {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
intervalMs?: number;
|
|
5
|
+
};
|
|
6
|
+
export type VoiceProviderContractsSnapshot<TProvider extends string = string> = {
|
|
7
|
+
error: string | null;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
report?: VoiceProviderContractMatrixReport<TProvider>;
|
|
10
|
+
updatedAt?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const fetchVoiceProviderContracts: <TProvider extends string = string>(path?: string, options?: Pick<VoiceProviderContractsClientOptions, "fetch">) => Promise<VoiceProviderContractMatrixReport<TProvider>>;
|
|
13
|
+
export declare const createVoiceProviderContractsStore: <TProvider extends string = string>(path?: string, options?: VoiceProviderContractsClientOptions) => {
|
|
14
|
+
close: () => void;
|
|
15
|
+
getServerSnapshot: () => VoiceProviderContractsSnapshot<TProvider>;
|
|
16
|
+
getSnapshot: () => VoiceProviderContractsSnapshot<TProvider>;
|
|
17
|
+
refresh: () => Promise<VoiceProviderContractMatrixReport<TProvider> | undefined>;
|
|
18
|
+
subscribe: (listener: () => void) => () => void;
|
|
19
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { VoiceProviderContractMatrixRow } from '../providerStackRecommendations';
|
|
2
|
+
import { type VoiceProviderContractsClientOptions, type VoiceProviderContractsSnapshot } from './providerContracts';
|
|
3
|
+
export type VoiceProviderContractRowView<TProvider extends string = string> = VoiceProviderContractMatrixRow<TProvider> & {
|
|
4
|
+
detail: string;
|
|
5
|
+
label: string;
|
|
6
|
+
remediations: Array<{
|
|
7
|
+
detail: string;
|
|
8
|
+
href?: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}>;
|
|
11
|
+
rows: Array<{
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
export type VoiceProviderContractsViewModel<TProvider extends string = string> = {
|
|
17
|
+
description: string;
|
|
18
|
+
error: string | null;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
label: string;
|
|
21
|
+
rows: VoiceProviderContractRowView<TProvider>[];
|
|
22
|
+
status: 'empty' | 'error' | 'loading' | 'ready' | 'warning';
|
|
23
|
+
title: string;
|
|
24
|
+
updatedAt?: number;
|
|
25
|
+
};
|
|
26
|
+
export type VoiceProviderContractsWidgetOptions = VoiceProviderContractsClientOptions & {
|
|
27
|
+
description?: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
};
|
|
30
|
+
export declare const createVoiceProviderContractsViewModel: <TProvider extends string = string>(snapshot: VoiceProviderContractsSnapshot<TProvider>, options?: VoiceProviderContractsWidgetOptions) => VoiceProviderContractsViewModel<TProvider>;
|
|
31
|
+
export declare const renderVoiceProviderContractsHTML: <TProvider extends string = string>(snapshot: VoiceProviderContractsSnapshot<TProvider>, options?: VoiceProviderContractsWidgetOptions) => string;
|
|
32
|
+
export declare const getVoiceProviderContractsCSS: () => string;
|
|
33
|
+
export declare const mountVoiceProviderContracts: <TProvider extends string = string>(element: Element, path?: string, options?: VoiceProviderContractsWidgetOptions) => {
|
|
34
|
+
close: () => void;
|
|
35
|
+
refresh: () => Promise<import("..").VoiceProviderContractMatrixReport<TProvider> | undefined>;
|
|
36
|
+
};
|
|
37
|
+
export declare const defineVoiceProviderContractsElement: (tagName?: string) => void;
|
package/dist/index.js
CHANGED
|
@@ -21437,36 +21437,72 @@ var buildVoiceProviderContractMatrix = (input) => {
|
|
|
21437
21437
|
detail: configured ? "Provider is configured for this deployment." : "Provider is declared but not configured.",
|
|
21438
21438
|
key: "configured",
|
|
21439
21439
|
label: "Configured",
|
|
21440
|
+
remediation: configured ? undefined : {
|
|
21441
|
+
code: "provider.configure",
|
|
21442
|
+
detail: "Enable this provider or remove it from the contract matrix for this deployment.",
|
|
21443
|
+
href: contract.remediationHref,
|
|
21444
|
+
label: "Configure provider"
|
|
21445
|
+
},
|
|
21440
21446
|
status: configured ? "pass" : "fail"
|
|
21441
21447
|
},
|
|
21442
21448
|
{
|
|
21443
21449
|
detail: missingEnv.length === 0 ? "Required environment is present." : `Missing env: ${missingEnv.join(", ")}.`,
|
|
21444
21450
|
key: "env",
|
|
21445
21451
|
label: "Required env",
|
|
21452
|
+
remediation: missingEnv.length === 0 ? undefined : {
|
|
21453
|
+
code: "provider.env",
|
|
21454
|
+
detail: `Set ${missingEnv.join(", ")} before deploying this provider.`,
|
|
21455
|
+
href: contract.remediationHref,
|
|
21456
|
+
label: "Add missing env"
|
|
21457
|
+
},
|
|
21446
21458
|
status: missingEnv.length === 0 ? "pass" : "fail"
|
|
21447
21459
|
},
|
|
21448
21460
|
{
|
|
21449
21461
|
detail: contract.latencyBudgetMs !== undefined ? `Latency budget is ${contract.latencyBudgetMs}ms.` : "No latency budget declared.",
|
|
21450
21462
|
key: "latencyBudget",
|
|
21451
21463
|
label: "Latency budget",
|
|
21464
|
+
remediation: contract.latencyBudgetMs !== undefined ? undefined : {
|
|
21465
|
+
code: "provider.latency_budget",
|
|
21466
|
+
detail: "Declare latencyBudgetMs so readiness can distinguish expected latency from regressions.",
|
|
21467
|
+
href: contract.remediationHref,
|
|
21468
|
+
label: "Declare latency budget"
|
|
21469
|
+
},
|
|
21452
21470
|
status: contract.latencyBudgetMs !== undefined ? "pass" : "warn"
|
|
21453
21471
|
},
|
|
21454
21472
|
{
|
|
21455
21473
|
detail: (contract.fallbackProviders ?? []).length > 0 ? `Fallback providers: ${contract.fallbackProviders?.join(", ")}.` : "No fallback provider declared.",
|
|
21456
21474
|
key: "fallback",
|
|
21457
21475
|
label: "Fallback",
|
|
21476
|
+
remediation: (contract.fallbackProviders ?? []).length > 0 ? undefined : {
|
|
21477
|
+
code: "provider.fallback",
|
|
21478
|
+
detail: "Declare at least one fallback provider for this lane or mark this provider as intentionally single-provider.",
|
|
21479
|
+
href: contract.remediationHref,
|
|
21480
|
+
label: "Add fallback provider"
|
|
21481
|
+
},
|
|
21458
21482
|
status: (contract.fallbackProviders ?? []).length > 0 ? "pass" : "warn"
|
|
21459
21483
|
},
|
|
21460
21484
|
{
|
|
21461
21485
|
detail: contract.streaming ? "Streaming is supported." : "Streaming support is not declared.",
|
|
21462
21486
|
key: "streaming",
|
|
21463
21487
|
label: "Streaming",
|
|
21488
|
+
remediation: contract.streaming ? undefined : {
|
|
21489
|
+
code: "provider.streaming",
|
|
21490
|
+
detail: "Use a streaming-capable adapter for realtime voice, or route this provider only to non-realtime workflows.",
|
|
21491
|
+
href: contract.remediationHref,
|
|
21492
|
+
label: "Add streaming support"
|
|
21493
|
+
},
|
|
21464
21494
|
status: contract.streaming ? "pass" : "warn"
|
|
21465
21495
|
},
|
|
21466
21496
|
{
|
|
21467
21497
|
detail: missingCapabilities.length === 0 ? "Required capabilities are declared." : `Missing capabilities: ${missingCapabilities.join(", ")}.`,
|
|
21468
21498
|
key: "capabilities",
|
|
21469
21499
|
label: "Capabilities",
|
|
21500
|
+
remediation: missingCapabilities.length === 0 ? undefined : {
|
|
21501
|
+
code: "provider.capabilities",
|
|
21502
|
+
detail: `Declare or implement capabilities: ${missingCapabilities.join(", ")}.`,
|
|
21503
|
+
href: contract.remediationHref,
|
|
21504
|
+
label: "Add capability coverage"
|
|
21505
|
+
},
|
|
21470
21506
|
status: missingCapabilities.length === 0 ? "pass" : "warn"
|
|
21471
21507
|
}
|
|
21472
21508
|
];
|
|
@@ -21495,7 +21531,7 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
21495
21531
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
21496
21532
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
21497
21533
|
const rows = report.rows.map((row) => {
|
|
21498
|
-
const checks = row.checks.map((check) => `<li class="${escapeHtml36(check.status)}"><strong>${escapeHtml36(check.label)}</strong><span>${escapeHtml36(check.detail ?? check.status)}</span
|
|
21534
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml36(check.status)}"><strong>${escapeHtml36(check.label)}</strong><span>${escapeHtml36(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml36(check.remediation.href)}">${escapeHtml36(check.remediation.label)}</a>` : escapeHtml36(check.remediation.label)}: ${escapeHtml36(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
21499
21535
|
return `<article class="row ${escapeHtml36(row.status)}">
|
|
21500
21536
|
<div>
|
|
21501
21537
|
<p class="eyebrow">${escapeHtml36(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
@@ -21505,7 +21541,7 @@ var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
|
21505
21541
|
<ul>${checks}</ul>
|
|
21506
21542
|
</article>`;
|
|
21507
21543
|
}).join("");
|
|
21508
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml36(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
21544
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.row li em{color:#f9d77e;font-style:normal}.row li a{color:#86efac}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml36(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
21509
21545
|
};
|
|
21510
21546
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
21511
21547
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -37,10 +37,17 @@ export type VoiceProviderStackCapabilityGapInput<TProvider extends string = stri
|
|
|
37
37
|
required?: Partial<Record<VoiceProviderStackKind, readonly string[]>>;
|
|
38
38
|
};
|
|
39
39
|
export type VoiceProviderContractCheckStatus = 'fail' | 'pass' | 'warn';
|
|
40
|
+
export type VoiceProviderContractRemediation = {
|
|
41
|
+
code: string;
|
|
42
|
+
detail: string;
|
|
43
|
+
href?: string;
|
|
44
|
+
label: string;
|
|
45
|
+
};
|
|
40
46
|
export type VoiceProviderContractCheck = {
|
|
41
47
|
detail?: string;
|
|
42
48
|
key: string;
|
|
43
49
|
label: string;
|
|
50
|
+
remediation?: VoiceProviderContractRemediation;
|
|
44
51
|
status: VoiceProviderContractCheckStatus;
|
|
45
52
|
};
|
|
46
53
|
export type VoiceProviderContractDefinition<TProvider extends string = string> = {
|
|
@@ -53,6 +60,7 @@ export type VoiceProviderContractDefinition<TProvider extends string = string> =
|
|
|
53
60
|
provider: TProvider;
|
|
54
61
|
requiredCapabilities?: readonly string[];
|
|
55
62
|
requiredEnv?: readonly string[];
|
|
63
|
+
remediationHref?: string;
|
|
56
64
|
selected?: boolean;
|
|
57
65
|
streaming?: boolean;
|
|
58
66
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type VoiceProviderContractsWidgetOptions } from '../client/providerContractsWidget';
|
|
2
|
+
export type VoiceProviderContractsProps = VoiceProviderContractsWidgetOptions & {
|
|
3
|
+
className?: string;
|
|
4
|
+
path?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const VoiceProviderContracts: ({ className, path, ...options }: VoiceProviderContractsProps) => import("react/jsx-runtime").JSX.Element;
|