omnimind-sdk 1.0.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,43 @@
1
+ # OmniMind NodeJS SDK
2
+
3
+ NodeJS SDK for the OmniMind Protocol, supporting real-time voice streaming, text messaging, and hardware capability registration (MCP).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install omnimind-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```javascript
14
+ const { OmniMindClient } = require('omnimind-sdk');
15
+
16
+ const client = new OmniMindClient(
17
+ "ws://127.0.0.1:8082/v1/ws",
18
+ "YOUR_DEVICE_SN",
19
+ "YOUR_TOKEN"
20
+ );
21
+
22
+ client.setTextCallback((text, isFinal) => {
23
+ console.log(`[AI] ${text}`);
24
+ });
25
+
26
+ client.setOnOpenCallback(() => {
27
+ console.log("Connected!");
28
+ client.sendText("Hello, what is the weather today?");
29
+ });
30
+
31
+ client.connect();
32
+ ```
33
+
34
+ ## Features
35
+
36
+ - **Protobuf-based**: Efficient binary communication.
37
+ - **Voice Streaming**: High-quality audio chunk transmission.
38
+ - **Tool Registration**: Easily register hardware functions for the AI to call.
39
+ - **Automatic Heartbeat**: Reliable long-term connection management.
40
+
41
+ ## License
42
+
43
+ MIT
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "omnimind-sdk",
3
+ "version": "1.0.0",
4
+ "description": "OmniMind Protocol NodeJS SDK for AIoT Devices",
5
+ "main": "src/omnimind/index.js",
6
+ "files": [
7
+ "src/omnimind"
8
+ ],
9
+ "author": "XiaoRGEEK",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "omnimind",
13
+ "aiot",
14
+ "protocol",
15
+ "sdk",
16
+ "voice-streaming",
17
+ "protobuf"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/xiaorgeek/omnimind-sdk"
22
+ },
23
+ "dependencies": {
24
+ "ws": "^8.16.0",
25
+ "protobufjs": "^7.2.5"
26
+ },
27
+ "devDependencies": {
28
+ "@types/ws": "^8.5.10",
29
+ "protobufjs-cli": "^1.1.1"
30
+ },
31
+ "scripts": {
32
+ "start": "node examples/client_example.js",
33
+ "test": "echo \"Error: no test specified\" && exit 1"
34
+ }
35
+ }
@@ -0,0 +1,258 @@
1
+ const WebSocket = require('ws');
2
+ const omnimind_pb = require('./generated/omnimind_pb');
3
+
4
+ // Using the root namespace generated by protobufjs
5
+ const omnimind = omnimind_pb.omnimind;
6
+
7
+ class OmniMindClient {
8
+ constructor(serverUrl, sn, token = "", config = {}) {
9
+ this.serverUrl = serverUrl;
10
+ this.sn = sn;
11
+ this.token = token;
12
+ this.inputAudioFormat = config.inputAudioFormat || null;
13
+ this.outputAudioFormat = config.outputAudioFormat || null;
14
+ this.enableTTS = config.enableTTS || false;
15
+
16
+ this.ws = null;
17
+ this.running = false;
18
+ this.heartbeatInterval = 30000;
19
+ this.heartbeatTimer = null;
20
+
21
+ // Callbacks
22
+ this.onVoiceCallback = null;
23
+ this.onTextCallback = null;
24
+ this.onToolCallCallback = null;
25
+ this.onOpenCallback = null;
26
+ this.onCloseCallback = null;
27
+ this.onErrorCallback = null;
28
+
29
+ this.toolHandlers = new Map();
30
+ }
31
+
32
+ setVoiceCallback(callback) {
33
+ this.onVoiceCallback = callback;
34
+ }
35
+
36
+ setTextCallback(callback) {
37
+ this.onTextCallback = callback;
38
+ }
39
+
40
+ setToolCallCallback(callback) {
41
+ this.onToolCallCallback = callback;
42
+ }
43
+
44
+ setOnOpenCallback(callback) {
45
+ this.onOpenCallback = callback;
46
+ }
47
+
48
+ setOnCloseCallback(callback) {
49
+ this.onCloseCallback = callback;
50
+ }
51
+
52
+ setOnErrorCallback(callback) {
53
+ this.onErrorCallback = callback;
54
+ }
55
+
56
+ connect() {
57
+ this.ws = new WebSocket(this.serverUrl);
58
+ this.ws.binaryType = 'arraybuffer';
59
+
60
+ this.ws.on('open', () => {
61
+ console.log(`[Client ${this.sn}] Connected to ${this.serverUrl}`);
62
+ this.running = true;
63
+ this._handleOpen();
64
+ });
65
+
66
+ this.ws.on('message', (data) => {
67
+ this._handleMessage(data);
68
+ });
69
+
70
+ this.ws.on('error', (err) => {
71
+ console.error(`[Client ${this.sn}] WebSocket Error:`, err);
72
+ if (this.onErrorCallback) this.onErrorCallback(err);
73
+ });
74
+
75
+ this.ws.on('close', (code, reason) => {
76
+ console.log(`[Client ${this.sn}] Connection closed. Code: ${code}, Reason: ${reason}`);
77
+ this.running = false;
78
+ this._stopHeartbeat();
79
+ if (this.onCloseCallback) this.onCloseCallback(code, reason);
80
+ });
81
+ }
82
+
83
+ close() {
84
+ this.running = false;
85
+ this._stopHeartbeat();
86
+ if (this.ws) {
87
+ this.ws.close();
88
+ }
89
+ }
90
+
91
+ _handleOpen() {
92
+ // Send AUTH
93
+ const auth = {
94
+ firmwareVersion: "1.0.0",
95
+ token: this.token,
96
+ enableTts: this.enableTTS
97
+ };
98
+
99
+ if (this.inputAudioFormat) {
100
+ auth.inputAudioFormat = this.inputAudioFormat;
101
+ }
102
+ if (this.outputAudioFormat) {
103
+ auth.outputAudioFormat = this.outputAudioFormat;
104
+ }
105
+
106
+ const msg = {
107
+ sn: this.sn,
108
+ type: omnimind.MessageType.AUTH,
109
+ payload: omnimind.AuthPayload.encode(auth).finish(),
110
+ timestamp: Date.now()
111
+ };
112
+
113
+ this.ws.send(omnimind.OmniMessage.encode(msg).finish());
114
+ this._startHeartbeat();
115
+ if (this.onOpenCallback) this.onOpenCallback();
116
+ }
117
+
118
+ _handleMessage(data) {
119
+ try {
120
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
121
+ const msg = omnimind.OmniMessage.decode(buffer);
122
+
123
+ switch (msg.type) {
124
+ case omnimind.MessageType.VOICE_STREAM:
125
+ if (this.onVoiceCallback) this.onVoiceCallback(msg.payload);
126
+ break;
127
+ case omnimind.MessageType.TEXT_MSG:
128
+ const textMsg = omnimind.TextMessage.decode(msg.payload);
129
+ if (this.onTextCallback) this.onTextCallback(textMsg.content, textMsg.role);
130
+ break;
131
+ case omnimind.MessageType.TOOL_CALL:
132
+ this._handleToolCall(msg.payload);
133
+ break;
134
+ case omnimind.MessageType.PONG:
135
+ // Heartbeat response
136
+ break;
137
+ default:
138
+ // console.log(`[Client ${this.sn}] Message type: ${msg.type}`);
139
+ }
140
+ } catch (err) {
141
+ console.error(`[Client ${this.sn}] Message decode error:`, err);
142
+ }
143
+ }
144
+
145
+ _handleToolCall(payload) {
146
+ try {
147
+ const toolCall = omnimind.ToolCall.decode(payload);
148
+ const handler = this.toolHandlers.get(toolCall.functionName);
149
+
150
+ if (handler) {
151
+ const args = JSON.parse(toolCall.argsJson);
152
+ const result = handler(args);
153
+
154
+ const response = {
155
+ callId: toolCall.callId,
156
+ resultJson: JSON.stringify(result)
157
+ };
158
+
159
+ const msg = {
160
+ sn: this.sn,
161
+ type: omnimind.MessageType.TOOL_RESP,
162
+ payload: omnimind.ToolResponse.encode(response).finish(),
163
+ timestamp: Date.now()
164
+ };
165
+
166
+ this.ws.send(omnimind.OmniMessage.encode(msg).finish());
167
+ } else if (this.onToolCallCallback) {
168
+ this.onToolCallCallback(toolCall.functionName, toolCall.argsJson, toolCall.callId);
169
+ }
170
+ } catch (err) {
171
+ console.error(`[Client ${this.sn}] Tool call error:`, err);
172
+ }
173
+ }
174
+
175
+ sendVoice(audioData) {
176
+ if (!this.running || !this.ws || this.ws.readyState !== WebSocket.OPEN) return;
177
+
178
+ const msg = {
179
+ sn: this.sn,
180
+ type: omnimind.MessageType.VOICE_STREAM,
181
+ payload: audioData,
182
+ timestamp: Date.now()
183
+ };
184
+
185
+ this.ws.send(omnimind.OmniMessage.encode(msg).finish());
186
+ }
187
+
188
+ sendText(text) {
189
+ if (!this.running || !this.ws || this.ws.readyState !== WebSocket.OPEN) return;
190
+
191
+ const textMsg = {
192
+ content: text,
193
+ role: "user"
194
+ };
195
+ const msg = {
196
+ sn: this.sn,
197
+ type: omnimind.MessageType.TEXT_MSG,
198
+ payload: omnimind.TextMessage.encode(textMsg).finish(),
199
+ timestamp: Date.now()
200
+ };
201
+
202
+ this.ws.send(omnimind.OmniMessage.encode(msg).finish());
203
+ }
204
+
205
+ registerTool(name, description, argsSchema, handler) {
206
+ this.toolHandlers.set(name, handler);
207
+ // Note: In current protocol, tool registration can be done via MCP_REG
208
+ }
209
+
210
+ sendToolManifest(tools, events = []) {
211
+ if (!this.running || !this.ws || this.ws.readyState !== WebSocket.OPEN) return;
212
+
213
+ const manifest = {
214
+ tools: tools.map(t => ({
215
+ name: t.name,
216
+ description: t.description,
217
+ argsSchema: JSON.stringify(t.argsSchema || {})
218
+ })),
219
+ events: events.map(e => ({
220
+ name: e.name,
221
+ description: e.description,
222
+ payloadSchema: JSON.stringify(e.payloadSchema || {})
223
+ }))
224
+ };
225
+
226
+ const msg = {
227
+ sn: this.sn,
228
+ type: omnimind.MessageType.MCP_REG,
229
+ payload: omnimind.ToolManifest.encode(manifest).finish(),
230
+ timestamp: Date.now()
231
+ };
232
+
233
+ this.ws.send(omnimind.OmniMessage.encode(msg).finish());
234
+ }
235
+
236
+ _startHeartbeat() {
237
+ this._stopHeartbeat();
238
+ this.heartbeatTimer = setInterval(() => {
239
+ if (this.running && this.ws && this.ws.readyState === WebSocket.OPEN) {
240
+ const msg = {
241
+ sn: this.sn,
242
+ type: omnimind.MessageType.PING,
243
+ timestamp: Date.now()
244
+ };
245
+ this.ws.send(omnimind.OmniMessage.encode(msg).finish());
246
+ }
247
+ }, this.heartbeatInterval);
248
+ }
249
+
250
+ _stopHeartbeat() {
251
+ if (this.heartbeatTimer) {
252
+ clearInterval(this.heartbeatTimer);
253
+ this.heartbeatTimer = null;
254
+ }
255
+ }
256
+ }
257
+
258
+ module.exports = OmniMindClient;