@evalage/sdk-core 0.1.0

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 ADDED
@@ -0,0 +1,38 @@
1
+ # @evalage/sdk-core
2
+
3
+ Minimal core transport SDK for the ReplicaVAPi voice service.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @evalage/sdk-core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { EvalageCoreClient } from '@evalage/sdk-core';
15
+
16
+ const core = new EvalageCoreClient({
17
+ serverUrl: process.env.VITE_SERVER_URL!,
18
+ platformApiKey: process.env.VITE_PLATFORM_API_KEY!,
19
+ assistantId: process.env.VITE_ASSISTANT_ID!,
20
+ });
21
+
22
+ core.on('call-ended', (e) => {
23
+ console.log('call ended', e);
24
+ });
25
+
26
+ await core.connect();
27
+ core.startCall({});
28
+
29
+ // later
30
+ core.endCall();
31
+ ```
32
+
33
+ ## What it does
34
+
35
+ - Connects to the Socket.IO `/voice` namespace
36
+ - Authenticates with `{ platformApiKey, assistantId }`
37
+ - Emits typed events like `call-started`, `call-ended`, `session-var-updated`, `agent-ready/open/close`
38
+ - Lets you send audio via `emitAudioChunk(ArrayBuffer)` and receive with `onAgentAudioChunk(...)`
@@ -0,0 +1,80 @@
1
+ import { Socket } from 'socket.io-client';
2
+
3
+ type JsonValue = null | boolean | number | string | JsonValue[] | {
4
+ [key: string]: JsonValue;
5
+ };
6
+ type SessionVars = Record<string, JsonValue>;
7
+ type StartCallPayload = {
8
+ userName?: string;
9
+ vars?: SessionVars;
10
+ };
11
+ type CallStartedEvent = {
12
+ callId?: string;
13
+ [key: string]: unknown;
14
+ };
15
+ type CallEndedEvent = {
16
+ callId?: string | null;
17
+ reason?: string;
18
+ tool?: string;
19
+ [key: string]: unknown;
20
+ };
21
+ type SessionVarUpdatedEvent = {
22
+ callId?: string;
23
+ key: string;
24
+ value: JsonValue;
25
+ type?: string;
26
+ };
27
+ type SdkError = {
28
+ message: string;
29
+ code?: string;
30
+ raw?: unknown;
31
+ };
32
+ type CoreClientEvents = {
33
+ connected: {
34
+ socketId: string;
35
+ };
36
+ disconnected: {
37
+ reason: string;
38
+ };
39
+ 'call-started': CallStartedEvent;
40
+ 'call-ended': CallEndedEvent;
41
+ 'agent-ready': void;
42
+ 'agent-open': void;
43
+ 'agent-close': void;
44
+ 'agent-error': SdkError;
45
+ error: SdkError;
46
+ 'session-var-updated': SessionVarUpdatedEvent;
47
+ '_socket-connect-error': SdkError;
48
+ };
49
+
50
+ type CoreClientOptions = {
51
+ serverUrl: string;
52
+ platformApiKey: string;
53
+ assistantId: string;
54
+ transports?: Array<'websocket' | 'polling'>;
55
+ debug?: boolean;
56
+ };
57
+ type InterruptPayload = {
58
+ type: string;
59
+ [key: string]: unknown;
60
+ };
61
+ declare class EvalageCoreClient {
62
+ private readonly opts;
63
+ private socket;
64
+ private emitter;
65
+ private connected;
66
+ constructor(options: CoreClientOptions);
67
+ on<K extends keyof CoreClientEvents>(event: K, handler: (payload: CoreClientEvents[K]) => void): () => void;
68
+ off<K extends keyof CoreClientEvents>(event: K, handler: (payload: CoreClientEvents[K]) => void): void;
69
+ getSocket(): Socket | null;
70
+ isConnected(): boolean;
71
+ connect(): Promise<void>;
72
+ startCall(payload?: StartCallPayload): void;
73
+ endCall(): void;
74
+ interrupt(payload: InterruptPayload): void;
75
+ emitAudioChunk(pcm16Buffer: ArrayBufferLike): void;
76
+ onAgentAudioChunk(handler: (pcm16: ArrayBuffer) => void): () => void;
77
+ disconnect(): void;
78
+ }
79
+
80
+ export { type CallEndedEvent, type CallStartedEvent, type CoreClientEvents, type CoreClientOptions, EvalageCoreClient, type InterruptPayload, type SdkError, type SessionVarUpdatedEvent, type StartCallPayload };
package/dist/index.js ADDED
@@ -0,0 +1,196 @@
1
+ // src/index.ts
2
+ import { io } from "socket.io-client";
3
+
4
+ // src/tinyEmitter.ts
5
+ var TinyEmitter = class {
6
+ constructor() {
7
+ this.handlers = /* @__PURE__ */ new Map();
8
+ }
9
+ on(event, handler) {
10
+ const set = this.handlers.get(event) || /* @__PURE__ */ new Set();
11
+ set.add(handler);
12
+ this.handlers.set(event, set);
13
+ return () => this.off(event, handler);
14
+ }
15
+ off(event, handler) {
16
+ const set = this.handlers.get(event);
17
+ if (!set) return;
18
+ set.delete(handler);
19
+ if (set.size === 0) this.handlers.delete(event);
20
+ }
21
+ emit(event, payload) {
22
+ const set = this.handlers.get(event);
23
+ if (!set || set.size === 0) return;
24
+ for (const handler of Array.from(set)) {
25
+ try {
26
+ handler(payload);
27
+ } catch {
28
+ }
29
+ }
30
+ }
31
+ };
32
+
33
+ // src/index.ts
34
+ var toError = (e, fallbackMessage) => {
35
+ if (e && typeof e === "object") {
36
+ const anyE = e;
37
+ return {
38
+ message: String(anyE?.message || fallbackMessage),
39
+ code: anyE?.code ? String(anyE.code) : void 0,
40
+ raw: e
41
+ };
42
+ }
43
+ return { message: fallbackMessage, raw: e };
44
+ };
45
+ var EvalageCoreClient = class {
46
+ constructor(options) {
47
+ this.socket = null;
48
+ this.emitter = new TinyEmitter();
49
+ this.connected = false;
50
+ this.opts = {
51
+ transports: ["websocket"],
52
+ debug: false,
53
+ ...options
54
+ };
55
+ if (!this.opts.serverUrl || !this.opts.serverUrl.trim()) {
56
+ throw new Error("EvalageCoreClient: missing serverUrl");
57
+ }
58
+ if (!this.opts.platformApiKey || !this.opts.platformApiKey.trim()) {
59
+ throw new Error("EvalageCoreClient: missing platformApiKey");
60
+ }
61
+ if (!this.opts.assistantId || !this.opts.assistantId.trim()) {
62
+ throw new Error("EvalageCoreClient: missing assistantId");
63
+ }
64
+ }
65
+ on(event, handler) {
66
+ return this.emitter.on(event, handler);
67
+ }
68
+ off(event, handler) {
69
+ this.emitter.off(event, handler);
70
+ }
71
+ getSocket() {
72
+ return this.socket;
73
+ }
74
+ isConnected() {
75
+ return this.connected;
76
+ }
77
+ async connect() {
78
+ if (this.socket && this.connected) return;
79
+ const url = this.opts.serverUrl.replace(/\/$/, "");
80
+ const socket = io(`${url}/voice`, {
81
+ transports: this.opts.transports,
82
+ auth: {
83
+ platformApiKey: this.opts.platformApiKey,
84
+ assistantId: this.opts.assistantId
85
+ }
86
+ });
87
+ this.socket = socket;
88
+ socket.on("connect", () => {
89
+ this.connected = true;
90
+ const socketId = socket.id ?? "";
91
+ this.emitter.emit("connected", { socketId });
92
+ if (this.opts.debug) console.log("[evalage/sdk-core] connected", { socketId });
93
+ });
94
+ socket.on("disconnect", (reason) => {
95
+ this.connected = false;
96
+ this.emitter.emit("disconnected", { reason: String(reason || "") });
97
+ if (this.opts.debug) console.log("[evalage/sdk-core] disconnected", reason);
98
+ });
99
+ socket.on("connect_error", (err) => {
100
+ const e = toError(err, "socket connect_error");
101
+ this.emitter.emit("_socket-connect-error", e);
102
+ this.emitter.emit("error", e);
103
+ if (this.opts.debug) console.log("[evalage/sdk-core] connect_error", e);
104
+ });
105
+ socket.on("error", (err) => {
106
+ const e = toError(err, "server error");
107
+ this.emitter.emit("error", e);
108
+ if (this.opts.debug) console.log("[evalage/sdk-core] error", e);
109
+ });
110
+ socket.on("agent-error", (err) => {
111
+ const e = toError(err, "agent error");
112
+ this.emitter.emit("agent-error", e);
113
+ if (this.opts.debug) console.log("[evalage/sdk-core] agent-error", e);
114
+ });
115
+ socket.on("agent-ready", () => {
116
+ this.emitter.emit("agent-ready", void 0);
117
+ if (this.opts.debug) console.log("[evalage/sdk-core] agent-ready");
118
+ });
119
+ socket.on("agent-open", () => {
120
+ this.emitter.emit("agent-open", void 0);
121
+ if (this.opts.debug) console.log("[evalage/sdk-core] agent-open");
122
+ });
123
+ socket.on("agent-close", () => {
124
+ this.emitter.emit("agent-close", void 0);
125
+ if (this.opts.debug) console.log("[evalage/sdk-core] agent-close");
126
+ });
127
+ socket.on("call-started", (data) => {
128
+ this.emitter.emit("call-started", data);
129
+ if (this.opts.debug) console.log("[evalage/sdk-core] call-started", data);
130
+ });
131
+ socket.on("call-ended", (data) => {
132
+ this.emitter.emit("call-ended", data);
133
+ if (this.opts.debug) console.log("[evalage/sdk-core] call-ended", data);
134
+ });
135
+ socket.on("session-var-updated", (data) => {
136
+ this.emitter.emit("session-var-updated", data);
137
+ if (this.opts.debug) console.log("[evalage/sdk-core] session-var-updated", data);
138
+ });
139
+ await new Promise((resolve) => {
140
+ const done = () => resolve();
141
+ socket.once("connect", done);
142
+ socket.once("connect_error", done);
143
+ });
144
+ }
145
+ startCall(payload = {}) {
146
+ if (!this.socket) throw new Error("EvalageCoreClient: not connected");
147
+ this.socket.emit("agent-start", payload);
148
+ if (this.opts.debug) console.log("[evalage/sdk-core] agent-start emitted", payload);
149
+ }
150
+ endCall() {
151
+ if (!this.socket) return;
152
+ try {
153
+ this.socket.emit("agent-end");
154
+ } catch {
155
+ }
156
+ try {
157
+ this.socket.disconnect();
158
+ } catch {
159
+ }
160
+ }
161
+ interrupt(payload) {
162
+ if (!this.socket) throw new Error("EvalageCoreClient: not connected");
163
+ this.socket.emit("agent-interrupt", payload);
164
+ if (this.opts.debug) console.log("[evalage/sdk-core] agent-interrupt emitted", payload);
165
+ }
166
+ emitAudioChunk(pcm16Buffer) {
167
+ if (!this.socket) throw new Error("EvalageCoreClient: not connected");
168
+ this.socket.emit("agent-audio-chunk", pcm16Buffer);
169
+ }
170
+ onAgentAudioChunk(handler) {
171
+ if (!this.socket) throw new Error("EvalageCoreClient: not connected");
172
+ const h = (data) => {
173
+ const bytes = data instanceof ArrayBuffer ? data : data?.buffer instanceof ArrayBuffer ? data.buffer : null;
174
+ if (bytes) handler(bytes);
175
+ };
176
+ this.socket.on("agent-audio-chunk", h);
177
+ return () => {
178
+ try {
179
+ this.socket?.off("agent-audio-chunk", h);
180
+ } catch {
181
+ }
182
+ };
183
+ }
184
+ disconnect() {
185
+ if (!this.socket) return;
186
+ try {
187
+ this.socket.disconnect();
188
+ } catch {
189
+ }
190
+ this.socket = null;
191
+ this.connected = false;
192
+ }
193
+ };
194
+ export {
195
+ EvalageCoreClient
196
+ };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@evalage/sdk-core",
3
+ "version": "0.1.0",
4
+ "description": "Core client SDK for ReplicaVAPi voice service (transport + events).",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --format esm --dts --clean",
20
+ "typecheck": "tsc -p tsconfig.json --noEmit"
21
+ },
22
+ "dependencies": {
23
+ "socket.io-client": "^4.8.1"
24
+ },
25
+ "devDependencies": {
26
+ "tsup": "^8.0.2",
27
+ "typescript": "^5.6.3"
28
+ }
29
+ }