@voctiv/agent-sdk 0.3.0 → 0.3.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/README.md +202 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/types/media-providers.d.ts +83 -0
- package/dist/types/media-providers.d.ts.map +1 -0
- package/dist/types/media-providers.js +19 -0
- package/dist/types/media-providers.js.map +1 -0
- package/examples/README.md +1 -0
- package/examples/custom-media-providers.ts +128 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ The [`examples/`](./examples/) folder contains copy-paste-ready scripts:
|
|
|
81
81
|
| [schedule-call-with-defaults.ts](./examples/schedule-call-with-defaults.ts) | `platform.call()` without explicit recall — CMS defaults from `context` |
|
|
82
82
|
| [after-call-continuation.ts](./examples/after-call-continuation.ts) | `onSuccessCall` / `onFailedCall` vs recall (mutually exclusive) |
|
|
83
83
|
| [read-recall-from-params.ts](./examples/read-recall-from-params.ts) | `parseRecallDelaySeconds()` / `parseRecallCount()` on legacy params |
|
|
84
|
+
| [custom-media-providers.ts](./examples/custom-media-providers.ts) | `defineMediaProviders` + custom ASR/TTS via `createAsr` / `createTts` |
|
|
84
85
|
|
|
85
86
|
### Quick recall example
|
|
86
87
|
|
|
@@ -116,6 +117,8 @@ const maxAttempts = parseRecallCount(context.dialogParams?.recall_count);
|
|
|
116
117
|
|
|
117
118
|
`defineScript(fn)` marks the default export as the script entry point. It returns the same function and exists to give TypeScript the correct `ScriptContext` shape.
|
|
118
119
|
|
|
120
|
+
`defineMediaProviders(def)` marks a named `mediaProviders` export for custom ASR/TTS factories (trusted packages only). See [Custom ASR / TTS Providers](#custom-asr--tts-providers).
|
|
121
|
+
|
|
119
122
|
**Three different “context” names:**
|
|
120
123
|
|
|
121
124
|
| Name | Meaning |
|
|
@@ -554,6 +557,9 @@ All audio playback goes through **`channel.audio`** (`ChannelAudio`). There are
|
|
|
554
557
|
Create a reusable TTS session with `channel.createTts(config?)` — same pattern as `createAsr`.
|
|
555
558
|
Call `tts.say` / `tts.say$` / `tts.presay` on the handle so synthesis reuses the warmed SSL / streaming WebSocket.
|
|
556
559
|
|
|
560
|
+
Custom engines from a logic package are documented in
|
|
561
|
+
[Custom ASR / TTS Providers](#custom-asr--tts-providers).
|
|
562
|
+
|
|
557
563
|
```ts
|
|
558
564
|
const tts = await channel.createTts({
|
|
559
565
|
vendor: 'elevenlabs',
|
|
@@ -774,6 +780,202 @@ Shared by `say()`, `play()`, and (where noted) `presay()`:
|
|
|
774
780
|
|
|
775
781
|
`play()` ignores `tts*` and `cache` for raw audio. `preload()` only accepts `cache` overrides.
|
|
776
782
|
|
|
783
|
+
## Custom ASR / TTS Providers
|
|
784
|
+
|
|
785
|
+
Trusted logic packages can ship their own ASR/TTS engines next to `defineScript`. The host
|
|
786
|
+
`require`s a static **`export const mediaProviders`** **outside** the script sandbox (same
|
|
787
|
+
process privileges as the API). Use this only for packages you trust.
|
|
788
|
+
|
|
789
|
+
Mid-script `registerAsr` / `registerTts` is **not** supported — declare vendors at module load.
|
|
790
|
+
|
|
791
|
+
### Export shape
|
|
792
|
+
|
|
793
|
+
```ts
|
|
794
|
+
import {
|
|
795
|
+
defineScript,
|
|
796
|
+
defineMediaProviders,
|
|
797
|
+
type MediaConnectorContext,
|
|
798
|
+
type MediaProviderShared,
|
|
799
|
+
type ScriptAsrConnector,
|
|
800
|
+
type ScriptTtsConnector,
|
|
801
|
+
} from '@voctiv/agent-sdk';
|
|
802
|
+
import { Subject } from 'rxjs';
|
|
803
|
+
import { Readable } from 'stream';
|
|
804
|
+
|
|
805
|
+
export const mediaProviders = defineMediaProviders({
|
|
806
|
+
// Optional: one object per dialog, shared by ASR + TTS factories
|
|
807
|
+
createShared: (ctx) => ({
|
|
808
|
+
dialogUuid: ctx.dialogUuid,
|
|
809
|
+
dispose() { /* release app state */ },
|
|
810
|
+
}),
|
|
811
|
+
asr: {
|
|
812
|
+
'my-asr': (ctx, shared) => new MyAsr(ctx, shared),
|
|
813
|
+
},
|
|
814
|
+
tts: {
|
|
815
|
+
'my-tts': (ctx, shared) => new MyTts(ctx, shared),
|
|
816
|
+
},
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
export default defineScript(async ({ channel }) => {
|
|
820
|
+
const asr = await channel.createAsr({
|
|
821
|
+
vendor: 'my-asr',
|
|
822
|
+
language: 'ru-RU',
|
|
823
|
+
data: { api_key: process.env.MY_ASR_KEY! },
|
|
824
|
+
});
|
|
825
|
+
const tts = await channel.createTts({
|
|
826
|
+
vendor: 'my-tts',
|
|
827
|
+
data: {
|
|
828
|
+
api_key: process.env.MY_TTS_KEY!,
|
|
829
|
+
voice_id: '…',
|
|
830
|
+
output_format: 'pcm_16000', // preferred for telephony
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
await tts.say('Hello from a custom connector.', {
|
|
835
|
+
alias: 'greet',
|
|
836
|
+
ttsStrategy: 'streaming', // used when supportsStreaming() === true
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
tts.say$('One. Two.', { alias: 'reply' }).subscribe((ev) => {
|
|
840
|
+
// queued | speaking | done | cancelled — same events as builtin TTS
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
// Reuse the warm handle:
|
|
844
|
+
await channel.audio.say('Again', { tts });
|
|
845
|
+
|
|
846
|
+
asr.destroy();
|
|
847
|
+
tts.destroy();
|
|
848
|
+
});
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
`defineMediaProviders` is an identity helper for typing. The host looks up
|
|
852
|
+
`module.exports.mediaProviders` (or `default.mediaProviders`) on the script entry file.
|
|
853
|
+
|
|
854
|
+
Full stub: [`examples/custom-media-providers.ts`](./examples/custom-media-providers.ts).
|
|
855
|
+
|
|
856
|
+
### Implement ASR (`ScriptAsrConnector`)
|
|
857
|
+
|
|
858
|
+
The host pushes **PCM S16LE mono 16 kHz** frames into `send()`. Emit partials on
|
|
859
|
+
`transcription$` and finals on both `transcription$` (`isFinal: true`) and `result$`.
|
|
860
|
+
|
|
861
|
+
| Member | Role |
|
|
862
|
+
| --- | --- |
|
|
863
|
+
| `transcription$` | `{ text, isFinal }` partials and finals |
|
|
864
|
+
| `result$` | Final utterance strings (drives `AsrHandle.result$`) |
|
|
865
|
+
| `error$` | Vendor / transport failures |
|
|
866
|
+
| `send(audio)` | Accept inbound PCM (`ArrayBuffer` / `Buffer`) |
|
|
867
|
+
| `speech(active)` | Optional VAD gate from the host (`true` = utterance open) |
|
|
868
|
+
| `finalize()` | End-of-utterance nudge (flush / commit) |
|
|
869
|
+
| `isOpen()` | Whether the vendor socket is ready |
|
|
870
|
+
| `close()` | Tear down; complete Subjects |
|
|
871
|
+
|
|
872
|
+
```ts
|
|
873
|
+
class MyAsr implements ScriptAsrConnector {
|
|
874
|
+
readonly transcription$ = new Subject<{ text: string; isFinal: boolean }>();
|
|
875
|
+
readonly result$ = new Subject<string>();
|
|
876
|
+
readonly error$ = new Subject<{ message: string; code?: number }>();
|
|
877
|
+
|
|
878
|
+
constructor(
|
|
879
|
+
private readonly ctx: MediaConnectorContext,
|
|
880
|
+
private readonly shared: MediaProviderShared | undefined,
|
|
881
|
+
) {
|
|
882
|
+
// ctx.config — flattened createAsr data + channel asrConfig
|
|
883
|
+
// ctx.dialogUuid, ctx.role, ctx.debug$
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
isOpen() { return true; }
|
|
887
|
+
send(audio: ArrayBufferLike) { /* forward PCM to vendor */ }
|
|
888
|
+
speech(_active: boolean) {}
|
|
889
|
+
finalize() {
|
|
890
|
+
const text = '…';
|
|
891
|
+
this.transcription$.next({ text, isFinal: true });
|
|
892
|
+
this.result$.next(text);
|
|
893
|
+
}
|
|
894
|
+
close() {
|
|
895
|
+
this.transcription$.complete();
|
|
896
|
+
this.result$.complete();
|
|
897
|
+
this.error$.complete();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
Factory `throw` during create → host degraded ASR handle (same as builtin). Prefer connecting
|
|
903
|
+
lazily in `send()`; the host does not call a separate `connect()` on custom connectors.
|
|
904
|
+
|
|
905
|
+
### Implement TTS (`ScriptTtsConnector`)
|
|
906
|
+
|
|
907
|
+
**One class per vendor** covers batch HTTP and optional streaming WebSocket. Do **not**
|
|
908
|
+
register a separate streaming map.
|
|
909
|
+
|
|
910
|
+
| Member | Role |
|
|
911
|
+
| --- | --- |
|
|
912
|
+
| `supportsStreaming()` | `true` → host may use WS path for `ttsStrategy: 'streaming'` |
|
|
913
|
+
| `textToSpeechStream(text, ctx?)` | Batch/HTTP synthesis → `Readable` of audio bytes |
|
|
914
|
+
| `audio$` / `done$` | Streaming audio chunks and end-of-generation |
|
|
915
|
+
| `open` / `startGeneration` / `sendText` / `flush` | Streaming lifecycle |
|
|
916
|
+
| `sendSeparatorFlush?()` | Optional sentence separator flush |
|
|
917
|
+
| `close()` | Soft-close sockets for **reuse** (do not complete Subjects if `open()` may run again) |
|
|
918
|
+
|
|
919
|
+
```ts
|
|
920
|
+
class MyTts implements ScriptTtsConnector {
|
|
921
|
+
readonly audio$ = new Subject<Buffer>();
|
|
922
|
+
readonly done$ = new Subject<void>();
|
|
923
|
+
|
|
924
|
+
supportsStreaming() {
|
|
925
|
+
return true; // or false for HTTP-only
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
async textToSpeechStream(rawtext: string, _ctx?: { previousText?: string; nextText?: string }) {
|
|
929
|
+
// Return PCM (preferred) or MP3/OGG. Hint format via createTts data:
|
|
930
|
+
// output_format: 'pcm_16000' | 'mp3_…' or audioFormat: 'pcm' | 'mp3'
|
|
931
|
+
return Readable.from([/* bytes */]);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async open() { /* open long-lived WS */ }
|
|
935
|
+
async startGeneration() { /* BOS / new utterance */ }
|
|
936
|
+
sendText(chunk: string) { /* stream tokens */ }
|
|
937
|
+
flush() { /* EOS; later emit done$ */ }
|
|
938
|
+
close() { /* close WS; keep Subjects alive for fingerprint reuse */ }
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
- `supportsStreaming() === false` → streaming strategy falls back to sentence/full HTTP path.
|
|
943
|
+
- Default audio assumption for custom vendors is **PCM** unless `output_format` / `audioFormat`
|
|
944
|
+
indicates a compressed format (`mp3`, `ogg`, …).
|
|
945
|
+
|
|
946
|
+
### Using custom vendors in the script
|
|
947
|
+
|
|
948
|
+
```ts
|
|
949
|
+
const asr = await channel.createAsr({ vendor: 'my-asr', data: { … } });
|
|
950
|
+
const tts = await channel.createTts({ vendor: 'my-tts', data: { … } });
|
|
951
|
+
|
|
952
|
+
await tts.say('Hi', { alias: 'greet' });
|
|
953
|
+
await tts.presay('Warm cache');
|
|
954
|
+
tts.say$('Next', { alias: 'reply', ttsStrategy: 'streaming' }).subscribe(…);
|
|
955
|
+
|
|
956
|
+
await channel.audio.say('Reuse', { tts }); // same warm session
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
Vendor keys must be **multi-character** names. Builtin single-letter codes
|
|
960
|
+
(`A`, `D`, `E`, `ES`, `G`, `V`, `W`, `W2`, …) **cannot** be overridden.
|
|
961
|
+
|
|
962
|
+
### Warm reuse vs `createShared` vs PCM cache
|
|
963
|
+
|
|
964
|
+
| Layer | What it caches | Scope |
|
|
965
|
+
| --- | --- | --- |
|
|
966
|
+
| Factory + session registries | One connector instance per `dialog + vendor + config fingerprint` (TCP/WS) | Host automatic |
|
|
967
|
+
| `createShared` | Your app/vendor state shared by ASR↔TTS factories | One object per dialog |
|
|
968
|
+
| `PlayOptions.cache` / `presay` | Synthesized PCM files | Host TTS file cache |
|
|
969
|
+
|
|
970
|
+
Call `createAsr` / `createTts` early to warm sockets. Repeated `say` with the same handle (or
|
|
971
|
+
matching fingerprint) must **not** open a new TCP/WS per utterance.
|
|
972
|
+
|
|
973
|
+
### Security
|
|
974
|
+
|
|
975
|
+
- Host `require` of `mediaProviders` runs with API privileges — **trusted packages only**.
|
|
976
|
+
- Entry path is restricted to the script root (path traversal denied).
|
|
977
|
+
- Optional host allowlists may further restrict which packages may export providers.
|
|
978
|
+
|
|
777
979
|
## TTS Credentials And Vendor Parameters
|
|
778
980
|
|
|
779
981
|
### Direct TTS Vendor Parameters
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
*/
|
|
31
31
|
export { defineScript } from './define-script';
|
|
32
32
|
export type { ScriptContext, ScriptFn } from './define-script';
|
|
33
|
+
export { defineMediaProviders } from './types/media-providers';
|
|
34
|
+
export type { MediaProvidersDefinition, MediaConnectorContext, MediaProviderShared, AsrProviderFactory, TtsProviderFactory, ScriptAsrConnector, ScriptAsrConnectorError, ScriptTtsConnector, TtsSynthesisContext as ScriptTtsSynthesisContext, } from './types/media-providers';
|
|
33
35
|
export type { ScriptDialogContext, ScriptPhase, ScriptRunTime, ScriptResult, PersistedScriptResult, ScriptError, AgentContext, AgentEnvSetOptions, StorageContextApi, } from './types/script-context';
|
|
34
36
|
export { getScriptPhase } from './types/script-context';
|
|
35
37
|
export type { NluScriptApi, PlatformApi, DialogApi, ScheduleCallOptions, SendMessageOptions, InboundMessage, MessagingApi, } from './types/platform';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE/D,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjG,YAAY,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3H,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EACV,SAAS,EACT,OAAO,EACP,SAAS,EACT,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,IAAI,yBAAyB,GACjD,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjG,YAAY,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3H,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EACV,SAAS,EACT,OAAO,EACP,SAAS,EACT,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -30,9 +30,11 @@
|
|
|
30
30
|
* and live SIP INFO ({@link import('./types/events').SipInfo} on `sipInfo$`). See **`README.md`** → SIP Signalling Metadata.
|
|
31
31
|
*/
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.parseRecallDelaySeconds = exports.parseRecallCount = exports.isLegacyPhraseRecord = exports.LEGACY_PHRASE_RECORD_BRAND = exports.TranscriptionRole = exports.getScriptPhase = exports.defineScript = void 0;
|
|
33
|
+
exports.parseRecallDelaySeconds = exports.parseRecallCount = exports.isLegacyPhraseRecord = exports.LEGACY_PHRASE_RECORD_BRAND = exports.TranscriptionRole = exports.getScriptPhase = exports.defineMediaProviders = exports.defineScript = void 0;
|
|
34
34
|
var define_script_1 = require("./define-script");
|
|
35
35
|
Object.defineProperty(exports, "defineScript", { enumerable: true, get: function () { return define_script_1.defineScript; } });
|
|
36
|
+
var media_providers_1 = require("./types/media-providers");
|
|
37
|
+
Object.defineProperty(exports, "defineMediaProviders", { enumerable: true, get: function () { return media_providers_1.defineMediaProviders; } });
|
|
36
38
|
var script_context_1 = require("./types/script-context");
|
|
37
39
|
Object.defineProperty(exports, "getScriptPhase", { enumerable: true, get: function () { return script_context_1.getScriptPhase; } });
|
|
38
40
|
var logger_1 = require("./types/logger");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAEH,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAEH,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAGrB,2DAA+D;AAAtD,uHAAA,oBAAoB,OAAA;AAyB7B,yDAAwD;AAA/C,gHAAA,cAAc,OAAA;AAavB,yCAAmD;AAA1C,2GAAA,iBAAiB,OAAA;AAwC1B,uDAG+B;AAF7B,2HAAA,0BAA0B,OAAA;AAC1B,qHAAA,oBAAoB,OAAA;AAUtB,+CAA2E;AAAlE,gHAAA,gBAAgB,OAAA;AAAE,uHAAA,uBAAuB,OAAA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Observable, Subject } from 'rxjs';
|
|
2
|
+
import type { Readable } from 'stream';
|
|
3
|
+
/**
|
|
4
|
+
* Context passed to custom ASR/TTS factories from {@link defineMediaProviders}.
|
|
5
|
+
* Populated by the host outside the script sandbox.
|
|
6
|
+
*/
|
|
7
|
+
export interface MediaConnectorContext {
|
|
8
|
+
id: string;
|
|
9
|
+
config: Record<string, string>;
|
|
10
|
+
dialogUuid: string;
|
|
11
|
+
role?: string;
|
|
12
|
+
debug$: Subject<string>;
|
|
13
|
+
}
|
|
14
|
+
export interface ScriptAsrConnectorError {
|
|
15
|
+
message: string;
|
|
16
|
+
code?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Host ASR connector contract for custom providers (same surface as runtime AsrConnector).
|
|
20
|
+
*/
|
|
21
|
+
export interface ScriptAsrConnector {
|
|
22
|
+
transcription$: Subject<{
|
|
23
|
+
text: string;
|
|
24
|
+
isFinal: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
result$: Subject<string>;
|
|
27
|
+
error$: Subject<ScriptAsrConnectorError>;
|
|
28
|
+
isOpen(): boolean;
|
|
29
|
+
send(audio: ArrayBufferLike): void;
|
|
30
|
+
speech(active: boolean): void;
|
|
31
|
+
finalize(): void;
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
|
34
|
+
export type TtsSynthesisContext = {
|
|
35
|
+
previousText?: string;
|
|
36
|
+
nextText?: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Unified TTS connector: batch HTTP + optional streaming WebSocket methods.
|
|
40
|
+
*/
|
|
41
|
+
export interface ScriptTtsConnector {
|
|
42
|
+
supportsStreaming(): boolean;
|
|
43
|
+
textToSpeechStream(rawtext: string, ctx?: TtsSynthesisContext): Promise<Readable>;
|
|
44
|
+
readonly audio$: Subject<Buffer>;
|
|
45
|
+
readonly done$: Subject<void>;
|
|
46
|
+
open(): Promise<void>;
|
|
47
|
+
startGeneration(): Promise<void>;
|
|
48
|
+
sendText(chunk: string): void;
|
|
49
|
+
flush(): void;
|
|
50
|
+
sendSeparatorFlush?(): void;
|
|
51
|
+
close(): void;
|
|
52
|
+
}
|
|
53
|
+
/** Optional session object shared by ASR and TTS factories for one dialog. */
|
|
54
|
+
export type MediaProviderShared = {
|
|
55
|
+
dispose?: () => void;
|
|
56
|
+
close?: () => void;
|
|
57
|
+
} & Record<string, unknown>;
|
|
58
|
+
export type AsrProviderFactory = (ctx: MediaConnectorContext, shared: MediaProviderShared | undefined) => ScriptAsrConnector;
|
|
59
|
+
export type TtsProviderFactory = (ctx: MediaConnectorContext, shared: MediaProviderShared | undefined) => ScriptTtsConnector;
|
|
60
|
+
export interface MediaProvidersDefinition {
|
|
61
|
+
createShared?: (ctx: {
|
|
62
|
+
dialogUuid: string;
|
|
63
|
+
debug$: Subject<string>;
|
|
64
|
+
}) => MediaProviderShared;
|
|
65
|
+
asr?: Record<string, AsrProviderFactory>;
|
|
66
|
+
tts?: Record<string, TtsProviderFactory>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Mark a named export as custom media providers (identity helper).
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* export const mediaProviders = defineMediaProviders({
|
|
74
|
+
* createShared: (ctx) => new MySession(ctx),
|
|
75
|
+
* asr: { 'my-asr': (ctx, shared) => new MyAsr(ctx, shared) },
|
|
76
|
+
* tts: { 'my-tts': (ctx, shared) => new MyTts(ctx, shared) },
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function defineMediaProviders(def: MediaProvidersDefinition): MediaProvidersDefinition;
|
|
81
|
+
/** @internal helper type for hosts that mirror connector subjects */
|
|
82
|
+
export type SubjectLike<T> = Subject<T> | Observable<T>;
|
|
83
|
+
//# sourceMappingURL=media-providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-providers.d.ts","sourceRoot":"","sources":["../../src/types/media-providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineMediaProviders = defineMediaProviders;
|
|
4
|
+
/**
|
|
5
|
+
* Mark a named export as custom media providers (identity helper).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* export const mediaProviders = defineMediaProviders({
|
|
10
|
+
* createShared: (ctx) => new MySession(ctx),
|
|
11
|
+
* asr: { 'my-asr': (ctx, shared) => new MyAsr(ctx, shared) },
|
|
12
|
+
* tts: { 'my-tts': (ctx, shared) => new MyTts(ctx, shared) },
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
function defineMediaProviders(def) {
|
|
17
|
+
return def;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=media-providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-providers.js","sourceRoot":"","sources":["../../src/types/media-providers.ts"],"names":[],"mappings":";;AAmGA,oDAIC;AAhBD;;;;;;;;;;;GAWG;AACH,SAAgB,oBAAoB,CAClC,GAA6B;IAE7B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/examples/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Each file exports one `defineScript` handler. The host **always** invokes that s
|
|
|
11
11
|
| [schedule-call-with-defaults.ts](./schedule-call-with-defaults.ts) | `platform.call()` using CMS defaults from `context` |
|
|
12
12
|
| [after-call-continuation.ts](./after-call-continuation.ts) | `onSuccessCall` / `onFailedCall` + `getScriptPhase()` |
|
|
13
13
|
| [read-recall-from-params.ts](./read-recall-from-params.ts) | `parseRecallDelaySeconds()` / `parseRecallCount()` |
|
|
14
|
+
| [custom-media-providers.ts](./custom-media-providers.ts) | `defineMediaProviders` + `createAsr` / `createTts` / `say$` |
|
|
14
15
|
|
|
15
16
|
## Recall flow
|
|
16
17
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ASR / TTS from a trusted logic package.
|
|
3
|
+
*
|
|
4
|
+
* Export `mediaProviders` next to `defineScript`. The host loads that export
|
|
5
|
+
* outside the sandbox and wires vendors into `createAsr` / `createTts` with the
|
|
6
|
+
* same warm TCP/WS reuse as built-in engines.
|
|
7
|
+
*
|
|
8
|
+
* Batch and streaming TTS share one class (`supportsStreaming()` + streaming
|
|
9
|
+
* lifecycle methods). Do not register a separate streaming map.
|
|
10
|
+
*/
|
|
11
|
+
import { Readable } from 'stream';
|
|
12
|
+
import { Subject } from 'rxjs';
|
|
13
|
+
import {
|
|
14
|
+
defineScript,
|
|
15
|
+
defineMediaProviders,
|
|
16
|
+
type MediaConnectorContext,
|
|
17
|
+
type MediaProviderShared,
|
|
18
|
+
type ScriptAsrConnector,
|
|
19
|
+
type ScriptTtsConnector,
|
|
20
|
+
} from '@voctiv/agent-sdk';
|
|
21
|
+
|
|
22
|
+
type SharedSession = MediaProviderShared & {
|
|
23
|
+
turns: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class EchoAsr implements ScriptAsrConnector {
|
|
27
|
+
readonly transcription$ = new Subject<{ text: string; isFinal: boolean }>();
|
|
28
|
+
readonly result$ = new Subject<string>();
|
|
29
|
+
readonly error$ = new Subject<{ message: string; code?: number }>();
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly ctx: MediaConnectorContext,
|
|
33
|
+
private readonly shared: SharedSession | undefined,
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
isOpen(): boolean {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
send(_audio: ArrayBufferLike): void {
|
|
41
|
+
// Forward PCM to your vendor here.
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
speech(_active: boolean): void {}
|
|
45
|
+
|
|
46
|
+
finalize(): void {
|
|
47
|
+
const text = 'heard';
|
|
48
|
+
this.transcription$.next({ text, isFinal: true });
|
|
49
|
+
this.result$.next(text);
|
|
50
|
+
if (this.shared) this.shared.turns += 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
close(): void {
|
|
54
|
+
this.transcription$.complete();
|
|
55
|
+
this.result$.complete();
|
|
56
|
+
this.error$.complete();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class BatchOrStreamTts implements ScriptTtsConnector {
|
|
61
|
+
readonly audio$ = new Subject<Buffer>();
|
|
62
|
+
readonly done$ = new Subject<void>();
|
|
63
|
+
|
|
64
|
+
constructor(
|
|
65
|
+
private readonly ctx: MediaConnectorContext,
|
|
66
|
+
private readonly shared: SharedSession | undefined,
|
|
67
|
+
) {}
|
|
68
|
+
|
|
69
|
+
/** Return true when this connector also owns a long-lived streaming socket. */
|
|
70
|
+
supportsStreaming(): boolean {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async textToSpeechStream(rawtext: string): Promise<Readable> {
|
|
75
|
+
void this.ctx;
|
|
76
|
+
void this.shared;
|
|
77
|
+
// Replace with HTTP keep-alive synthesis that returns a PCM stream.
|
|
78
|
+
return Readable.from([Buffer.from(rawtext, 'utf8')]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async open(): Promise<void> {}
|
|
82
|
+
async startGeneration(): Promise<void> {}
|
|
83
|
+
sendText(_chunk: string): void {}
|
|
84
|
+
flush(): void {}
|
|
85
|
+
close(): void {
|
|
86
|
+
// Soft-close streaming sockets for reuse; do not complete Subjects if the
|
|
87
|
+
// host may call open() again on the same fingerprint.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const mediaProviders = defineMediaProviders({
|
|
92
|
+
createShared: (): SharedSession => ({
|
|
93
|
+
turns: 0,
|
|
94
|
+
dispose() {
|
|
95
|
+
/* release vendor app state for this dialog */
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
asr: {
|
|
99
|
+
'my-asr': (ctx, shared) => new EchoAsr(ctx, shared as SharedSession),
|
|
100
|
+
},
|
|
101
|
+
tts: {
|
|
102
|
+
'my-tts': (ctx, shared) =>
|
|
103
|
+
new BatchOrStreamTts(ctx, shared as SharedSession),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export default defineScript(async ({ channel, logger }) => {
|
|
108
|
+
channel.sip.answer();
|
|
109
|
+
|
|
110
|
+
const asr = await channel.createAsr({ vendor: 'my-asr' });
|
|
111
|
+
const tts = await channel.createTts({ vendor: 'my-tts' });
|
|
112
|
+
|
|
113
|
+
await tts.say('Hello from a custom TTS connector.');
|
|
114
|
+
|
|
115
|
+
tts.say$('One. Two.', { alias: 'reply' }).subscribe({
|
|
116
|
+
next: (ev) => {
|
|
117
|
+
logger.log('tts utterance', { state: ev.state, alias: ev.itemAlias });
|
|
118
|
+
},
|
|
119
|
+
error: (err) => logger.error('tts failed', err),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Same warm handle via audio.say:
|
|
123
|
+
await channel.audio.say('Reuse handle', { tts });
|
|
124
|
+
|
|
125
|
+
asr.destroy();
|
|
126
|
+
tts.destroy();
|
|
127
|
+
channel.sip.hangup();
|
|
128
|
+
});
|
package/package.json
CHANGED