@shogo-ai/voice 1.4.1
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/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/chunk-D2EVSUZX.js +27 -0
- package/dist/chunk-Q3TJAJ54.js +165 -0
- package/dist/chunk-QHDIEE7O.js +453 -0
- package/dist/chunk-S3S2RNO6.js +671 -0
- package/dist/chunk-ZGYHDC5G.js +423 -0
- package/dist/index.cjs +1316 -0
- package/dist/index.d.cts +432 -0
- package/dist/index.d.ts +432 -0
- package/dist/index.js +735 -0
- package/dist/native/index.cjs +1667 -0
- package/dist/native/index.d.cts +285 -0
- package/dist/native/index.d.ts +285 -0
- package/dist/native/index.js +949 -0
- package/dist/organicParticlesShaders-DIva0vBO.d.cts +693 -0
- package/dist/organicParticlesShaders-DIva0vBO.d.ts +693 -0
- package/dist/react/index.cjs +1670 -0
- package/dist/react/index.d.cts +423 -0
- package/dist/react/index.d.ts +423 -0
- package/dist/react/index.js +952 -0
- package/dist/route/agent.cjs +981 -0
- package/dist/route/agent.d.cts +20 -0
- package/dist/route/agent.d.ts +20 -0
- package/dist/route/agent.js +17 -0
- package/dist/route/audio-tags.cjs +975 -0
- package/dist/route/audio-tags.d.cts +16 -0
- package/dist/route/audio-tags.d.ts +16 -0
- package/dist/route/audio-tags.js +13 -0
- package/dist/route/index.cjs +987 -0
- package/dist/route/index.d.cts +115 -0
- package/dist/route/index.d.ts +115 -0
- package/dist/route/index.js +12 -0
- package/dist/route/signed-url.cjs +975 -0
- package/dist/route/signed-url.d.cts +12 -0
- package/dist/route/signed-url.d.ts +12 -0
- package/dist/route/signed-url.js +13 -0
- package/dist/route/tts-preview.cjs +975 -0
- package/dist/route/tts-preview.d.cts +17 -0
- package/dist/route/tts-preview.d.ts +17 -0
- package/dist/route/tts-preview.js +13 -0
- package/dist/server.cjs +999 -0
- package/dist/server.d.cts +88 -0
- package/dist/server.d.ts +88 -0
- package/dist/server.js +11 -0
- package/dist/types-BKJtOUbE.d.cts +352 -0
- package/dist/types-BKJtOUbE.d.ts +352 -0
- package/package.json +148 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shogo Technologies, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: MIT -->
|
|
2
|
+
<!-- Copyright (C) 2026 Shogo Technologies, Inc. -->
|
|
3
|
+
|
|
4
|
+
# @shogo-ai/voice
|
|
5
|
+
|
|
6
|
+
Voice infrastructure for Shogo agents — ElevenLabs ConvAI integration,
|
|
7
|
+
Twilio inbound/outbound, optional 3D visualizers, and React + React
|
|
8
|
+
Native UI primitives.
|
|
9
|
+
|
|
10
|
+
## Subpaths
|
|
11
|
+
|
|
12
|
+
| Subpath | Use |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `@shogo-ai/voice` | Server-safe primitives: `ElevenLabsClient`, audio-tag helpers, types. |
|
|
15
|
+
| `@shogo-ai/voice/server` | Handler factories that turn the SDK into ready-to-mount HTTP routes. |
|
|
16
|
+
| `@shogo-ai/voice/react` | Web React hooks + visualizers (`useShogoVoice`, `OrganicParticles`). |
|
|
17
|
+
| `@shogo-ai/voice/native` | React Native equivalents (Expo). |
|
|
18
|
+
| `@shogo-ai/voice/route/*` | Standard `(Request) => Response` route handlers. |
|
|
19
|
+
|
|
20
|
+
## Peers (all optional)
|
|
21
|
+
|
|
22
|
+
Install only the peers you actually use:
|
|
23
|
+
|
|
24
|
+
- `@elevenlabs/react`, `@ai-sdk/react`, `ai`, `react`, `three` — web React
|
|
25
|
+
- `@elevenlabs/react-native`, `react-native`, `expo-gl`, `expo-three`,
|
|
26
|
+
`three` — React Native
|
|
27
|
+
|
|
28
|
+
## License
|
|
29
|
+
|
|
30
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createVoiceHandlers
|
|
3
|
+
} from "./chunk-QHDIEE7O.js";
|
|
4
|
+
|
|
5
|
+
// src/route/index.ts
|
|
6
|
+
function defaultHandlers() {
|
|
7
|
+
return createVoiceHandlers({});
|
|
8
|
+
}
|
|
9
|
+
function createVoiceRoute(config) {
|
|
10
|
+
const v = createVoiceHandlers(config);
|
|
11
|
+
return {
|
|
12
|
+
signedUrl: { GET: (req) => v.signedUrl(req) },
|
|
13
|
+
ttsPreview: { POST: (req) => v.tts(req) },
|
|
14
|
+
agent: {
|
|
15
|
+
POST: (req) => v.agent.create(req),
|
|
16
|
+
PATCH: (req) => v.agent.patch(req),
|
|
17
|
+
DELETE: (req) => v.agent.delete(req)
|
|
18
|
+
},
|
|
19
|
+
audioTags: { GET: (req) => v.audioTags(req) }
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
defaultHandlers,
|
|
25
|
+
createVoiceRoute
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-D2EVSUZX.js.map
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/audioTags.ts
|
|
2
|
+
var AUDIO_TAGS = [
|
|
3
|
+
// Emotions
|
|
4
|
+
{ tag: "happy", group: "emotion", label: "Happy", description: "Warm, upbeat delivery.", example: "[happy] Oh, I\u2019m so glad you stopped by." },
|
|
5
|
+
{ tag: "sad", group: "emotion", label: "Sad", description: "Subdued, heavy-hearted tone.", example: "[sad] I wish things had gone differently." },
|
|
6
|
+
{ tag: "excited", group: "emotion", label: "Excited", description: "Energized, eager delivery.", example: "[excited] Wait until you hear this!" },
|
|
7
|
+
{ tag: "curious", group: "emotion", label: "Curious", description: "Inquisitive, rising inflection.", example: "[curious] Hmm, what did they look like?" },
|
|
8
|
+
{ tag: "sarcastic", group: "emotion", label: "Sarcastic", description: "Dry, ironic tone.", example: "[sarcastic] Oh, fantastic. Another Monday." },
|
|
9
|
+
{ tag: "angry", group: "emotion", label: "Angry", description: "Sharp, irritated edge.", example: "[angry] I told you not to touch that." },
|
|
10
|
+
{ tag: "nervous", group: "emotion", label: "Nervous", description: "Uneasy, faltering delivery.", example: "[nervous] I\u2014 I didn\u2019t think you\u2019d actually come." },
|
|
11
|
+
{ tag: "confident", group: "emotion", label: "Confident", description: "Grounded, assured delivery.", example: "[confident] Trust me. I\u2019ve done this before." },
|
|
12
|
+
// Delivery
|
|
13
|
+
{ tag: "whispers", group: "delivery", label: "Whispers", description: "Hushed, close-mic delivery.", example: "[whispers] Don\u2019t say it too loud." },
|
|
14
|
+
{ tag: "shouts", group: "delivery", label: "Shouts", description: "Loud, projected delivery.", example: "[shouts] Look out behind you!" },
|
|
15
|
+
{ tag: "softly", group: "delivery", label: "Softly", description: "Gentle, quiet tone.", example: "[softly] It\u2019s okay. I\u2019m here." },
|
|
16
|
+
{ tag: "slowly", group: "delivery", label: "Slowly", description: "Deliberate pacing.", example: "[slowly] Tell me\u2026 exactly\u2026 what happened." },
|
|
17
|
+
{ tag: "quickly", group: "delivery", label: "Quickly", description: "Rapid, urgent pacing.", example: "[quickly] We need to go, right now." },
|
|
18
|
+
// Reactions (non-verbal sounds)
|
|
19
|
+
{ tag: "laughs", group: "reaction", label: "Laughs", description: "A genuine laugh.", example: "That\u2019s ridiculous [laughs]." },
|
|
20
|
+
{ tag: "chuckles", group: "reaction", label: "Chuckles", description: "A small, warm chuckle.", example: "[chuckles] You always say that." },
|
|
21
|
+
{ tag: "sighs", group: "reaction", label: "Sighs", description: "An audible sigh.", example: "[sighs] Alright, fine. Let\u2019s do it." },
|
|
22
|
+
{ tag: "gasps", group: "reaction", label: "Gasps", description: "A sharp inhale of surprise.", example: "[gasps] You didn\u2019t!" },
|
|
23
|
+
{ tag: "hesitates", group: "reaction", label: "Hesitates", description: "A pause with uncertainty.", example: "[hesitates] I\u2019m not sure I should say." },
|
|
24
|
+
{ tag: "clears throat", group: "reaction", label: "Clears throat", description: "Throat-clearing beat.", example: "[clears throat] As I was saying\u2026" }
|
|
25
|
+
];
|
|
26
|
+
var AUDIO_TAG_GROUPS = [
|
|
27
|
+
{ id: "emotion", label: "Emotions", description: "Overall feeling carried through the line." },
|
|
28
|
+
{ id: "delivery", label: "Delivery", description: "How the words are spoken (pace, volume)." },
|
|
29
|
+
{ id: "reaction", label: "Reactions", description: "Non-verbal sounds mixed into speech." }
|
|
30
|
+
];
|
|
31
|
+
var EXPRESSIVITY_OPTIONS = [
|
|
32
|
+
{ id: "off", label: "Off", description: "No audio tags. Plain, natural delivery." },
|
|
33
|
+
{ id: "subtle", label: "Subtle", description: "Occasional tag when it genuinely fits. Default." },
|
|
34
|
+
{ id: "full", label: "Full", description: "Freely expressive \u2014 tags sprinkled to carry emotion and reactions." }
|
|
35
|
+
];
|
|
36
|
+
var DEFAULT_ALLOWED_TAGS = [
|
|
37
|
+
"whispers",
|
|
38
|
+
"laughs",
|
|
39
|
+
"sighs",
|
|
40
|
+
"excited",
|
|
41
|
+
"curious",
|
|
42
|
+
"softly"
|
|
43
|
+
];
|
|
44
|
+
var DEFAULT_VOICE_SETTINGS = {
|
|
45
|
+
stability: 0.5,
|
|
46
|
+
similarity_boost: 0.8,
|
|
47
|
+
style: 0,
|
|
48
|
+
use_speaker_boost: true
|
|
49
|
+
};
|
|
50
|
+
var KNOWN_TAG_SET = new Set(AUDIO_TAGS.map((t) => t.tag.toLowerCase()));
|
|
51
|
+
function stripAudioTags(text) {
|
|
52
|
+
if (!text) return "";
|
|
53
|
+
return text.replace(/\[([a-z][a-z ]{0,24})\]/gi, (match, inner) => {
|
|
54
|
+
const key = inner.trim().toLowerCase();
|
|
55
|
+
return KNOWN_TAG_SET.has(key) ? "" : match;
|
|
56
|
+
}).replace(/[ \t]{2,}/g, " ").replace(/\s+([,.!?;:])/g, "$1").trim();
|
|
57
|
+
}
|
|
58
|
+
var EXPRESSIVITY_BLOCK_OPEN = "<!--expressivity-->";
|
|
59
|
+
var EXPRESSIVITY_BLOCK_CLOSE = "<!--/expressivity-->";
|
|
60
|
+
function stripExpressivityBlock(prompt) {
|
|
61
|
+
if (!prompt) return prompt;
|
|
62
|
+
const open = EXPRESSIVITY_BLOCK_OPEN;
|
|
63
|
+
const close = EXPRESSIVITY_BLOCK_CLOSE;
|
|
64
|
+
const openIdx = prompt.indexOf(open);
|
|
65
|
+
if (openIdx === -1) return prompt;
|
|
66
|
+
const closeIdx = prompt.indexOf(close, openIdx);
|
|
67
|
+
if (closeIdx === -1) return prompt;
|
|
68
|
+
const before = prompt.slice(0, openIdx).replace(/\s+$/, "");
|
|
69
|
+
const after = prompt.slice(closeIdx + close.length).replace(/^\s+/, "");
|
|
70
|
+
return [before, after].filter(Boolean).join("\n\n");
|
|
71
|
+
}
|
|
72
|
+
function composeExpressivityBlock(expressivity, allowedTags) {
|
|
73
|
+
if (expressivity === "off") return "";
|
|
74
|
+
const allowed = (allowedTags && allowedTags.length > 0 ? allowedTags : DEFAULT_ALLOWED_TAGS).filter((t) => AUDIO_TAGS.some((a) => a.tag === t));
|
|
75
|
+
if (allowed.length === 0) return "";
|
|
76
|
+
const intensity = expressivity === "full" ? "Use audio tags liberally to color your speech with emotion, delivery, and non-verbal reactions. Most substantive replies should include at least one tag. Never add so many tags that a line becomes unnatural." : "Use audio tags sparingly \u2014 only when one genuinely fits the emotion or reaction of the moment. Most lines will have no tag. At most one or two tags per reply.";
|
|
77
|
+
const tagList = allowed.map((t) => {
|
|
78
|
+
const entry = AUDIO_TAGS.find((a) => a.tag === t);
|
|
79
|
+
return ` - [${entry.tag}] \u2014 ${entry.description} Example: ${entry.example}`;
|
|
80
|
+
}).join("\n");
|
|
81
|
+
const body = [
|
|
82
|
+
"You speak through an expressive TTS engine that understands inline audio tags.",
|
|
83
|
+
'Audio tags are lowercase words in square brackets placed directly in your spoken text \u2014 e.g. "[whispers] don\u2019t tell anyone" or "that was wild [laughs]".',
|
|
84
|
+
'Tags are performance cues, not words. Never describe a tag (do not say "asterisk laughs asterisk" or "*laughs*"). Never read a tag aloud. Never put tags inside quoted speech from another character.',
|
|
85
|
+
intensity,
|
|
86
|
+
"You may only use tags from this allow-list:",
|
|
87
|
+
tagList,
|
|
88
|
+
"If no tag fits, use none. Tags are optional flavor, not filler."
|
|
89
|
+
].join("\n");
|
|
90
|
+
return `${EXPRESSIVITY_BLOCK_OPEN}
|
|
91
|
+
${body}
|
|
92
|
+
${EXPRESSIVITY_BLOCK_CLOSE}`;
|
|
93
|
+
}
|
|
94
|
+
function buildPreviewLine(allowedTags, characterName = "your companion") {
|
|
95
|
+
const tags = (allowedTags ?? []).filter((t) => AUDIO_TAGS.some((a) => a.tag === t));
|
|
96
|
+
if (tags.length === 0) {
|
|
97
|
+
return `Hi there. This is a quick preview of how ${characterName} sounds.`;
|
|
98
|
+
}
|
|
99
|
+
const pick = tags.slice(0, 3);
|
|
100
|
+
const lead = pick[0];
|
|
101
|
+
const mid = pick[1];
|
|
102
|
+
const tail = pick[2];
|
|
103
|
+
if (pick.length === 1) {
|
|
104
|
+
return `[${lead}] Hey \u2014 this is how ${characterName} sounds when the mood takes over.`;
|
|
105
|
+
}
|
|
106
|
+
if (pick.length === 2) {
|
|
107
|
+
return `[${lead}] Oh, this is fun. [${mid}] Let me know what you think.`;
|
|
108
|
+
}
|
|
109
|
+
return `[${lead}] Okay, listen. [${mid}] This is the new voice. [${tail}] How does it feel?`;
|
|
110
|
+
}
|
|
111
|
+
function normalizeAudioTags(raw) {
|
|
112
|
+
if (!Array.isArray(raw)) return null;
|
|
113
|
+
const cleaned = raw.filter((x) => typeof x === "string").map((x) => x.trim().toLowerCase()).filter((x) => AUDIO_TAGS.some((a) => a.tag === x));
|
|
114
|
+
return Array.from(new Set(cleaned));
|
|
115
|
+
}
|
|
116
|
+
function normalizeExpressivity(raw) {
|
|
117
|
+
if (raw === "off" || raw === "subtle" || raw === "full") return raw;
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
function normalizeVoiceSettings(raw) {
|
|
121
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
122
|
+
const src = raw;
|
|
123
|
+
const out = {};
|
|
124
|
+
const clamp = (v) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : void 0;
|
|
125
|
+
const stability = clamp(src.stability);
|
|
126
|
+
const similarity = clamp(src.similarity_boost ?? src.similarityBoost);
|
|
127
|
+
const style = clamp(src.style);
|
|
128
|
+
const boost = src.use_speaker_boost ?? src.useSpeakerBoost;
|
|
129
|
+
if (stability !== void 0) out.stability = stability;
|
|
130
|
+
if (similarity !== void 0) out.similarity_boost = similarity;
|
|
131
|
+
if (style !== void 0) out.style = style;
|
|
132
|
+
if (typeof boost === "boolean") out.use_speaker_boost = boost;
|
|
133
|
+
return Object.keys(out).length ? out : void 0;
|
|
134
|
+
}
|
|
135
|
+
function readAudioTags(raw) {
|
|
136
|
+
const norm = normalizeAudioTags(raw);
|
|
137
|
+
return norm && norm.length > 0 ? norm : DEFAULT_ALLOWED_TAGS;
|
|
138
|
+
}
|
|
139
|
+
function readExpressivity(raw) {
|
|
140
|
+
return normalizeExpressivity(raw) ?? "subtle";
|
|
141
|
+
}
|
|
142
|
+
function readVoiceSettings(raw) {
|
|
143
|
+
return normalizeVoiceSettings(raw) ?? { ...DEFAULT_VOICE_SETTINGS };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export {
|
|
147
|
+
AUDIO_TAGS,
|
|
148
|
+
AUDIO_TAG_GROUPS,
|
|
149
|
+
EXPRESSIVITY_OPTIONS,
|
|
150
|
+
DEFAULT_ALLOWED_TAGS,
|
|
151
|
+
DEFAULT_VOICE_SETTINGS,
|
|
152
|
+
stripAudioTags,
|
|
153
|
+
EXPRESSIVITY_BLOCK_OPEN,
|
|
154
|
+
EXPRESSIVITY_BLOCK_CLOSE,
|
|
155
|
+
stripExpressivityBlock,
|
|
156
|
+
composeExpressivityBlock,
|
|
157
|
+
buildPreviewLine,
|
|
158
|
+
normalizeAudioTags,
|
|
159
|
+
normalizeExpressivity,
|
|
160
|
+
normalizeVoiceSettings,
|
|
161
|
+
readAudioTags,
|
|
162
|
+
readExpressivity,
|
|
163
|
+
readVoiceSettings
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=chunk-Q3TJAJ54.js.map
|