@emilshirokikh/slyos-sdk 1.0.0 → 1.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 +303 -25
- package/dist/index.d.ts +14 -19
- package/dist/index.js +166 -117
- package/package.json +1 -1
- package/src/index.ts +197 -144
package/README.md
CHANGED
|
@@ -1,48 +1,326 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# 🔥 @emilshirokikh/slyos-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official SDK for SlyOS on-device AI platform. Run AI models locally in browsers and Node.js.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📦 Installation
|
|
6
8
|
```bash
|
|
7
|
-
npm install @
|
|
9
|
+
npm install @emilshirokikh/slyos-sdk
|
|
8
10
|
```
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
**npm:** https://www.npmjs.com/package/@emilshirokikh/slyos-sdk
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🚀 Quick Start
|
|
11
17
|
```javascript
|
|
12
|
-
import SlyOS from '@
|
|
18
|
+
import SlyOS from '@emilshirokikh/slyos-sdk';
|
|
13
19
|
|
|
14
|
-
// Initialize
|
|
20
|
+
// 1. Initialize
|
|
15
21
|
const sdk = new SlyOS({
|
|
16
|
-
apiKey: '
|
|
22
|
+
apiKey: 'sk_live_your_api_key'
|
|
17
23
|
});
|
|
18
24
|
await sdk.initialize();
|
|
19
25
|
|
|
20
|
-
// Load model (downloads
|
|
26
|
+
// 2. Load model (downloads ~200MB once)
|
|
21
27
|
await sdk.loadModel('quantum-360m');
|
|
22
28
|
|
|
23
|
-
// Generate
|
|
24
|
-
const response = await sdk.generate('quantum-360m',
|
|
29
|
+
// 3. Generate responses
|
|
30
|
+
const response = await sdk.generate('quantum-360m',
|
|
31
|
+
'What is artificial intelligence?',
|
|
32
|
+
{
|
|
33
|
+
temperature: 0.7,
|
|
34
|
+
maxTokens: 100,
|
|
35
|
+
topP: 0.9
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
25
39
|
console.log(response);
|
|
40
|
+
// AI runs locally - zero cost!
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 📚 API Reference
|
|
46
|
+
|
|
47
|
+
### Constructor
|
|
48
|
+
```typescript
|
|
49
|
+
new SlyOS(config: SlyOSConfig)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Config:**
|
|
53
|
+
```typescript
|
|
54
|
+
{
|
|
55
|
+
apiKey: string; // Get from dashboard
|
|
56
|
+
apiUrl?: string; // Optional, defaults to production
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### Methods
|
|
63
|
+
|
|
64
|
+
#### `initialize()`
|
|
65
|
+
Authenticates with SlyOS backend and registers device.
|
|
66
|
+
```javascript
|
|
67
|
+
await sdk.initialize();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Returns:** `Promise<void>`
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
#### `loadModel(modelId)`
|
|
75
|
+
Downloads and caches AI model locally.
|
|
76
|
+
```javascript
|
|
77
|
+
await sdk.loadModel('quantum-360m');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Parameters:**
|
|
81
|
+
- `modelId` (string): Model identifier
|
|
82
|
+
- `quantum-135m` - 80MB, fastest
|
|
83
|
+
- `quantum-360m` - 200MB, recommended
|
|
84
|
+
- `quantum-1.7b` - 1GB, high quality
|
|
85
|
+
- `quantum-3b` - 1.7GB, best quality
|
|
86
|
+
|
|
87
|
+
**Returns:** `Promise<void>`
|
|
88
|
+
|
|
89
|
+
**First call:** Downloads model (~1-2 min)
|
|
90
|
+
**Subsequent calls:** Uses cached model (<1 sec)
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
#### `generate(modelId, prompt, options?)`
|
|
95
|
+
Generates AI response locally.
|
|
96
|
+
```javascript
|
|
97
|
+
const response = await sdk.generate('quantum-360m',
|
|
98
|
+
'Tell me about your menu',
|
|
99
|
+
{
|
|
100
|
+
temperature: 0.7,
|
|
101
|
+
maxTokens: 150,
|
|
102
|
+
topP: 0.9
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Parameters:**
|
|
108
|
+
- `modelId` (string): Model to use
|
|
109
|
+
- `prompt` (string): Input text
|
|
110
|
+
- `options` (object, optional):
|
|
111
|
+
- `temperature` (0-2): Creativity (default: 0.7)
|
|
112
|
+
- `maxTokens` (10-2000): Max response length (default: 100)
|
|
113
|
+
- `topP` (0-1): Nucleus sampling (default: 0.9)
|
|
114
|
+
|
|
115
|
+
**Returns:** `Promise<string>` - Generated text
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🌐 Platform Support
|
|
120
|
+
|
|
121
|
+
| Platform | Status | Notes |
|
|
122
|
+
|----------|--------|-------|
|
|
123
|
+
| **Chrome** | ✅ Supported | Recommended |
|
|
124
|
+
| **Safari** | ✅ Supported | iOS 16+ |
|
|
125
|
+
| **Edge** | ✅ Supported | Chromium-based |
|
|
126
|
+
| **Firefox** | ⚠️ Limited | Some models work |
|
|
127
|
+
| **Node.js** | ✅ Supported | v18+ |
|
|
128
|
+
| **React Native** | 🚧 Coming Soon | Q2 2026 |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 💡 Usage Examples
|
|
133
|
+
|
|
134
|
+
### Basic Chatbot
|
|
135
|
+
```javascript
|
|
136
|
+
import SlyOS from '@emilshirokikh/slyos-sdk';
|
|
137
|
+
|
|
138
|
+
const sdk = new SlyOS({ apiKey: 'sk_live_...' });
|
|
139
|
+
await sdk.initialize();
|
|
140
|
+
await sdk.loadModel('quantum-360m');
|
|
141
|
+
|
|
142
|
+
async function chat(userMessage) {
|
|
143
|
+
return await sdk.generate('quantum-360m', userMessage);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const response = await chat('What are your hours?');
|
|
147
|
+
console.log(response);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### With System Prompt
|
|
153
|
+
```javascript
|
|
154
|
+
const systemPrompt = `You are a helpful assistant for McDonald's.
|
|
155
|
+
Help with menu, hours, and nutrition. Be friendly and concise.`;
|
|
156
|
+
|
|
157
|
+
const userMessage = 'What breakfast items do you have?';
|
|
158
|
+
const fullPrompt = `${systemPrompt}\n\nCustomer: ${userMessage}\nAssistant:`;
|
|
159
|
+
|
|
160
|
+
const response = await sdk.generate('quantum-360m', fullPrompt, {
|
|
161
|
+
temperature: 0.7,
|
|
162
|
+
maxTokens: 150
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### React Integration
|
|
169
|
+
```jsx
|
|
170
|
+
import { useState, useEffect } from 'react';
|
|
171
|
+
import SlyOS from '@emilshirokikh/slyos-sdk';
|
|
172
|
+
|
|
173
|
+
function Chatbot() {
|
|
174
|
+
const [sdk, setSdk] = useState(null);
|
|
175
|
+
const [loading, setLoading] = useState(true);
|
|
176
|
+
const [response, setResponse] = useState('');
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
async function init() {
|
|
180
|
+
const client = new SlyOS({ apiKey: 'sk_live_...' });
|
|
181
|
+
await client.initialize();
|
|
182
|
+
await client.loadModel('quantum-360m');
|
|
183
|
+
setSdk(client);
|
|
184
|
+
setLoading(false);
|
|
185
|
+
}
|
|
186
|
+
init();
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
async function handleChat(message) {
|
|
190
|
+
const reply = await sdk.generate('quantum-360m', message);
|
|
191
|
+
setResponse(reply);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (loading) return <div>Loading AI...</div>;
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div>
|
|
198
|
+
<button onClick={() => handleChat('Hello!')}>
|
|
199
|
+
Chat
|
|
200
|
+
</button>
|
|
201
|
+
<p>{response}</p>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 🔧 Advanced Configuration
|
|
210
|
+
|
|
211
|
+
### Custom Backend URL
|
|
212
|
+
```javascript
|
|
213
|
+
const sdk = new SlyOS({
|
|
214
|
+
apiKey: 'sk_live_...',
|
|
215
|
+
apiUrl: 'https://api.slyos.world'
|
|
216
|
+
});
|
|
26
217
|
```
|
|
27
218
|
|
|
28
|
-
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### Multiple Models
|
|
222
|
+
```javascript
|
|
223
|
+
await sdk.loadModel('quantum-360m');
|
|
224
|
+
await sdk.loadModel('quantum-1.7b');
|
|
225
|
+
|
|
226
|
+
// Use different models
|
|
227
|
+
const fast = await sdk.generate('quantum-360m', 'Quick question?');
|
|
228
|
+
const detailed = await sdk.generate('quantum-1.7b', 'Complex question?');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 📊 Performance
|
|
234
|
+
|
|
235
|
+
### Benchmarks (Quantum 360M)
|
|
236
|
+
|
|
237
|
+
| Metric | Browser | Node.js |
|
|
238
|
+
|--------|---------|---------|
|
|
239
|
+
| First load | 60-120s | 30-60s |
|
|
240
|
+
| Cached load | <1s | <0.5s |
|
|
241
|
+
| Inference | 35 tok/s | 50 tok/s |
|
|
242
|
+
| Memory | 500MB | 300MB |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 🐛 Troubleshooting
|
|
247
|
+
|
|
248
|
+
### Model won't load
|
|
249
|
+
```javascript
|
|
250
|
+
// Check browser console for errors
|
|
251
|
+
// Ensure 2GB+ RAM available
|
|
252
|
+
// Try smaller model (quantum-135m)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### CORS errors
|
|
256
|
+
```javascript
|
|
257
|
+
// Backend must allow your domain
|
|
258
|
+
// Check CORS_ORIGIN environment variable
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Slow inference
|
|
262
|
+
```javascript
|
|
263
|
+
// Use smaller model
|
|
264
|
+
// Reduce maxTokens
|
|
265
|
+
// Check CPU/RAM availability
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 🔒 Security
|
|
271
|
+
|
|
272
|
+
- API keys stored client-side (localStorage)
|
|
273
|
+
- All inference happens locally (private)
|
|
274
|
+
- Telemetry sent to SlyOS (anonymized)
|
|
275
|
+
- No user data sent to cloud
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 📦 Package Info
|
|
280
|
+
|
|
281
|
+
- **Package:** `@emilshirokikh/slyos-sdk`
|
|
282
|
+
- **Version:** 1.0.0
|
|
283
|
+
- **License:** MIT
|
|
284
|
+
- **Size:** 13.5 KB (unpacked)
|
|
285
|
+
- **Dependencies:** axios, @huggingface/transformers
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 🤝 Contributing
|
|
290
|
+
```bash
|
|
291
|
+
# Clone repo
|
|
292
|
+
git clone https://github.com/BeltoAI/sly.git
|
|
293
|
+
cd sly/sdk
|
|
294
|
+
|
|
295
|
+
# Install dependencies
|
|
296
|
+
npm install
|
|
297
|
+
|
|
298
|
+
# Make changes to src/index.ts
|
|
299
|
+
|
|
300
|
+
# Build
|
|
301
|
+
npm run build
|
|
302
|
+
|
|
303
|
+
# Test locally
|
|
304
|
+
npm link
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 📄 License
|
|
29
310
|
|
|
30
|
-
|
|
31
|
-
- ✅ **Privacy-first** - Data never leaves device
|
|
32
|
-
- ✅ **Works offline** - No internet required after download
|
|
33
|
-
- ✅ **Auto-scaling** - No server capacity planning
|
|
34
|
-
- ✅ **Real-time** - Sub-second response times
|
|
311
|
+
MIT - See LICENSE file
|
|
35
312
|
|
|
36
|
-
|
|
313
|
+
---
|
|
37
314
|
|
|
38
|
-
|
|
39
|
-
- Node.js (v18+)
|
|
40
|
-
- React Native (coming soon)
|
|
315
|
+
## 🙏 Credits
|
|
41
316
|
|
|
42
|
-
|
|
317
|
+
Built with Hugging Face Transformers.js
|
|
43
318
|
|
|
44
|
-
|
|
319
|
+
---
|
|
45
320
|
|
|
46
|
-
##
|
|
321
|
+
## 📞 Support
|
|
47
322
|
|
|
48
|
-
|
|
323
|
+
- **npm:** https://www.npmjs.com/package/@emilshirokikh/slyos-sdk
|
|
324
|
+
- **GitHub:** https://github.com/BeltoAI/sly
|
|
325
|
+
- **Docs:** See main README.md
|
|
326
|
+
- **Email:** support@slyos.world
|
package/dist/index.d.ts
CHANGED
|
@@ -2,33 +2,28 @@ interface SlyOSConfig {
|
|
|
2
2
|
apiKey: string;
|
|
3
3
|
apiUrl?: string;
|
|
4
4
|
}
|
|
5
|
-
interface
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
platforms: string[];
|
|
14
|
-
};
|
|
5
|
+
interface GenerateOptions {
|
|
6
|
+
temperature?: number;
|
|
7
|
+
maxTokens?: number;
|
|
8
|
+
topP?: number;
|
|
9
|
+
}
|
|
10
|
+
interface TranscribeOptions {
|
|
11
|
+
language?: string;
|
|
12
|
+
returnTimestamps?: boolean;
|
|
15
13
|
}
|
|
16
14
|
declare class SlyOS {
|
|
17
15
|
private apiKey;
|
|
18
16
|
private apiUrl;
|
|
19
|
-
private api;
|
|
20
17
|
private deviceId;
|
|
18
|
+
private token;
|
|
21
19
|
private models;
|
|
22
20
|
constructor(config: SlyOSConfig);
|
|
23
|
-
private generateDeviceId;
|
|
24
21
|
initialize(): Promise<void>;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
getAvailableModels(): Record<string, {
|
|
23
|
+
models: string[];
|
|
24
|
+
}>;
|
|
28
25
|
loadModel(modelId: string): Promise<void>;
|
|
29
|
-
generate(modelId: string, prompt: string, options?:
|
|
30
|
-
|
|
31
|
-
getDeviceId(): string;
|
|
26
|
+
generate(modelId: string, prompt: string, options?: GenerateOptions): Promise<string>;
|
|
27
|
+
transcribe(modelId: string, audioInput: any, options?: TranscribeOptions): Promise<string>;
|
|
32
28
|
}
|
|
33
29
|
export default SlyOS;
|
|
34
|
-
export { SlyOS, SlyOSConfig, ModelInfo };
|
package/dist/index.js
CHANGED
|
@@ -1,156 +1,205 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { pipeline } from '@huggingface/transformers';
|
|
2
|
+
import { pipeline, env } from '@huggingface/transformers';
|
|
3
|
+
// @ts-ignore - Force CPU in Node.js
|
|
4
|
+
if (env.backends?.onnx?.wasm) {
|
|
5
|
+
env.backends.onnx.wasm.proxy = false;
|
|
6
|
+
}
|
|
7
|
+
const modelMap = {
|
|
8
|
+
// LLM models (1B+)
|
|
9
|
+
'quantum-1.7b': {
|
|
10
|
+
hfModel: 'HuggingFaceTB/SmolLM2-1.7B-Instruct',
|
|
11
|
+
task: 'text-generation',
|
|
12
|
+
category: 'llm',
|
|
13
|
+
},
|
|
14
|
+
'quantum-3b': {
|
|
15
|
+
hfModel: 'meta-llama/Llama-3.2-3B-Instruct',
|
|
16
|
+
task: 'text-generation',
|
|
17
|
+
category: 'llm',
|
|
18
|
+
},
|
|
19
|
+
'quantum-code-3b': {
|
|
20
|
+
hfModel: 'Qwen/Qwen2.5-Coder-3B-Instruct',
|
|
21
|
+
task: 'text-generation',
|
|
22
|
+
category: 'llm',
|
|
23
|
+
},
|
|
24
|
+
'quantum-8b': {
|
|
25
|
+
hfModel: 'meta-llama/Llama-3.1-8B-Instruct',
|
|
26
|
+
task: 'text-generation',
|
|
27
|
+
category: 'llm',
|
|
28
|
+
},
|
|
29
|
+
// STT models
|
|
30
|
+
'voicecore-base': {
|
|
31
|
+
hfModel: 'onnx-community/whisper-base',
|
|
32
|
+
task: 'automatic-speech-recognition',
|
|
33
|
+
category: 'stt',
|
|
34
|
+
},
|
|
35
|
+
'voicecore-small': {
|
|
36
|
+
hfModel: 'onnx-community/whisper-small',
|
|
37
|
+
task: 'automatic-speech-recognition',
|
|
38
|
+
category: 'stt',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
3
41
|
class SlyOS {
|
|
4
42
|
constructor(config) {
|
|
43
|
+
this.token = null;
|
|
5
44
|
this.models = new Map();
|
|
6
45
|
this.apiKey = config.apiKey;
|
|
7
|
-
this.apiUrl = config.apiUrl || '
|
|
8
|
-
this.
|
|
9
|
-
baseURL: `${this.apiUrl}/api`,
|
|
10
|
-
headers: {
|
|
11
|
-
'Authorization': `Bearer ${this.apiKey}`,
|
|
12
|
-
'Content-Type': 'application/json'
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
this.deviceId = this.generateDeviceId();
|
|
16
|
-
}
|
|
17
|
-
generateDeviceId() {
|
|
18
|
-
return `device-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
46
|
+
this.apiUrl = config.apiUrl || 'https://api.slyos.world';
|
|
47
|
+
this.deviceId = `device-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
19
48
|
}
|
|
20
49
|
async initialize() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const ua = navigator.userAgent.toLowerCase();
|
|
38
|
-
if (ua.includes('iphone') || ua.includes('ipad'))
|
|
39
|
-
return 'ios';
|
|
40
|
-
if (ua.includes('android'))
|
|
41
|
-
return 'android';
|
|
42
|
-
return 'web';
|
|
43
|
-
}
|
|
44
|
-
getMemoryInfo() {
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
return (navigator.deviceMemory || 4) * 1024;
|
|
50
|
+
// Authenticate using API key
|
|
51
|
+
const authRes = await axios.post(`${this.apiUrl}/api/auth/sdk`, {
|
|
52
|
+
apiKey: this.apiKey,
|
|
53
|
+
});
|
|
54
|
+
this.token = authRes.data.token;
|
|
55
|
+
// Register this device
|
|
56
|
+
await axios.post(`${this.apiUrl}/api/devices/register`, {
|
|
57
|
+
device_id: this.deviceId,
|
|
58
|
+
platform: typeof window !== 'undefined' ? 'web' : 'nodejs',
|
|
59
|
+
os_version: typeof window !== 'undefined' ? navigator.userAgent : process.version,
|
|
60
|
+
total_memory_mb: 4096,
|
|
61
|
+
cpu_cores: 4,
|
|
62
|
+
has_gpu: false,
|
|
63
|
+
}, {
|
|
64
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
65
|
+
});
|
|
47
66
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
displayName: m.display_name,
|
|
55
|
-
size: m.size_q4,
|
|
56
|
-
requirements: {
|
|
57
|
-
minMemoryMB: parseInt(m.memory_required) || 512,
|
|
58
|
-
minStorageMB: m.size_q4 + 100,
|
|
59
|
-
platforms: ['ios', 'android', 'web']
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
console.error('Failed to fetch models:', error);
|
|
65
|
-
return [];
|
|
67
|
+
getAvailableModels() {
|
|
68
|
+
const grouped = { llm: [], stt: [] };
|
|
69
|
+
for (const [id, info] of Object.entries(modelMap)) {
|
|
70
|
+
if (!grouped[info.category])
|
|
71
|
+
grouped[info.category] = [];
|
|
72
|
+
grouped[info.category].push(id);
|
|
66
73
|
}
|
|
74
|
+
return Object.fromEntries(Object.entries(grouped).map(([cat, models]) => [cat, { models }]));
|
|
67
75
|
}
|
|
68
76
|
async loadModel(modelId) {
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
const info = modelMap[modelId];
|
|
78
|
+
if (!info) {
|
|
79
|
+
throw new Error(`Unknown model "${modelId}". Available: ${Object.keys(modelMap).join(', ')}`);
|
|
80
|
+
}
|
|
71
81
|
try {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'quantum-1.7b': 'HuggingFaceTB/SmolLM2-1.7B-Instruct'
|
|
76
|
-
};
|
|
77
|
-
const hfModel = modelMap[modelId] || modelMap['quantum-360m'];
|
|
78
|
-
const generator = await pipeline('text-generation', hfModel, {
|
|
79
|
-
device: 'webgpu',
|
|
80
|
-
dtype: 'q4'
|
|
81
|
-
});
|
|
82
|
-
this.models.set(modelId, generator);
|
|
83
|
-
const loadTime = Date.now() - startTime;
|
|
84
|
-
console.log(`✅ Model loaded in ${loadTime}ms`);
|
|
85
|
-
await this.sendTelemetry({
|
|
86
|
-
event_type: 'model_load',
|
|
87
|
-
model_id: modelId,
|
|
88
|
-
latency_ms: loadTime,
|
|
89
|
-
success: true
|
|
82
|
+
const pipe = await pipeline(info.task, info.hfModel, {
|
|
83
|
+
device: 'cpu',
|
|
84
|
+
dtype: 'fp32',
|
|
90
85
|
});
|
|
86
|
+
this.models.set(modelId, { pipe, info });
|
|
87
|
+
if (this.token) {
|
|
88
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
89
|
+
device_id: this.deviceId,
|
|
90
|
+
event_type: 'model_load',
|
|
91
|
+
model_id: modelId,
|
|
92
|
+
success: true,
|
|
93
|
+
}, {
|
|
94
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
95
|
+
}).catch(() => { });
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
98
|
catch (error) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
if (this.token) {
|
|
100
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
101
|
+
device_id: this.deviceId,
|
|
102
|
+
event_type: 'model_load',
|
|
103
|
+
model_id: modelId,
|
|
104
|
+
success: false,
|
|
105
|
+
error_message: error.message,
|
|
106
|
+
}, {
|
|
107
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
108
|
+
}).catch(() => { });
|
|
109
|
+
}
|
|
100
110
|
throw error;
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
|
-
async generate(modelId, prompt, options) {
|
|
113
|
+
async generate(modelId, prompt, options = {}) {
|
|
104
114
|
if (!this.models.has(modelId)) {
|
|
105
115
|
await this.loadModel(modelId);
|
|
106
116
|
}
|
|
107
|
-
const
|
|
117
|
+
const { pipe, info } = this.models.get(modelId);
|
|
118
|
+
if (info.category !== 'llm') {
|
|
119
|
+
throw new Error(`Model "${modelId}" is not an LLM. Use transcribe() for STT models.`);
|
|
120
|
+
}
|
|
108
121
|
const startTime = Date.now();
|
|
109
122
|
try {
|
|
110
|
-
const result = await
|
|
111
|
-
max_new_tokens: options
|
|
112
|
-
temperature: options
|
|
113
|
-
top_p: options
|
|
114
|
-
|
|
123
|
+
const result = await pipe(prompt, {
|
|
124
|
+
max_new_tokens: options.maxTokens || 100,
|
|
125
|
+
temperature: options.temperature || 0.7,
|
|
126
|
+
top_p: options.topP || 0.9,
|
|
127
|
+
do_sample: true,
|
|
115
128
|
});
|
|
116
|
-
const latency = Date.now() - startTime;
|
|
117
129
|
const response = result[0].generated_text;
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
const latency = Date.now() - startTime;
|
|
131
|
+
if (this.token) {
|
|
132
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
133
|
+
device_id: this.deviceId,
|
|
134
|
+
event_type: 'inference',
|
|
135
|
+
model_id: modelId,
|
|
136
|
+
latency_ms: latency,
|
|
137
|
+
tokens_generated: response.split(' ').length,
|
|
138
|
+
success: true,
|
|
139
|
+
}, {
|
|
140
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
141
|
+
}).catch(() => { });
|
|
142
|
+
}
|
|
127
143
|
return response;
|
|
128
144
|
}
|
|
129
145
|
catch (error) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
if (this.token) {
|
|
147
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
148
|
+
device_id: this.deviceId,
|
|
149
|
+
event_type: 'inference',
|
|
150
|
+
model_id: modelId,
|
|
151
|
+
success: false,
|
|
152
|
+
error_message: error.message,
|
|
153
|
+
}, {
|
|
154
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
155
|
+
}).catch(() => { });
|
|
156
|
+
}
|
|
137
157
|
throw error;
|
|
138
158
|
}
|
|
139
159
|
}
|
|
140
|
-
async
|
|
160
|
+
async transcribe(modelId, audioInput, options = {}) {
|
|
161
|
+
if (!this.models.has(modelId)) {
|
|
162
|
+
await this.loadModel(modelId);
|
|
163
|
+
}
|
|
164
|
+
const { pipe, info } = this.models.get(modelId);
|
|
165
|
+
if (info.category !== 'stt') {
|
|
166
|
+
throw new Error(`Model "${modelId}" is not an STT model. Use generate() for LLMs.`);
|
|
167
|
+
}
|
|
168
|
+
const startTime = Date.now();
|
|
141
169
|
try {
|
|
142
|
-
await
|
|
143
|
-
|
|
144
|
-
|
|
170
|
+
const result = await pipe(audioInput, {
|
|
171
|
+
language: options.language || 'en',
|
|
172
|
+
return_timestamps: options.returnTimestamps || false,
|
|
145
173
|
});
|
|
174
|
+
const text = result.text;
|
|
175
|
+
const latency = Date.now() - startTime;
|
|
176
|
+
if (this.token) {
|
|
177
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
178
|
+
device_id: this.deviceId,
|
|
179
|
+
event_type: 'inference',
|
|
180
|
+
model_id: modelId,
|
|
181
|
+
latency_ms: latency,
|
|
182
|
+
success: true,
|
|
183
|
+
}, {
|
|
184
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
185
|
+
}).catch(() => { });
|
|
186
|
+
}
|
|
187
|
+
return text;
|
|
146
188
|
}
|
|
147
189
|
catch (error) {
|
|
148
|
-
|
|
190
|
+
if (this.token) {
|
|
191
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
192
|
+
device_id: this.deviceId,
|
|
193
|
+
event_type: 'inference',
|
|
194
|
+
model_id: modelId,
|
|
195
|
+
success: false,
|
|
196
|
+
error_message: error.message,
|
|
197
|
+
}, {
|
|
198
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
199
|
+
}).catch(() => { });
|
|
200
|
+
}
|
|
201
|
+
throw error;
|
|
149
202
|
}
|
|
150
203
|
}
|
|
151
|
-
getDeviceId() {
|
|
152
|
-
return this.deviceId;
|
|
153
|
-
}
|
|
154
204
|
}
|
|
155
205
|
export default SlyOS;
|
|
156
|
-
export { SlyOS };
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,204 +1,257 @@
|
|
|
1
|
-
import axios
|
|
2
|
-
import { pipeline } from '@huggingface/transformers';
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { pipeline, env } from '@huggingface/transformers';
|
|
3
|
+
|
|
4
|
+
// @ts-ignore - Force CPU in Node.js
|
|
5
|
+
if (env.backends?.onnx?.wasm) {
|
|
6
|
+
env.backends.onnx.wasm.proxy = false;
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
interface SlyOSConfig {
|
|
5
10
|
apiKey: string;
|
|
6
11
|
apiUrl?: string;
|
|
7
12
|
}
|
|
8
13
|
|
|
14
|
+
interface GenerateOptions {
|
|
15
|
+
temperature?: number;
|
|
16
|
+
maxTokens?: number;
|
|
17
|
+
topP?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface TranscribeOptions {
|
|
21
|
+
language?: string;
|
|
22
|
+
returnTimestamps?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type ModelCategory = 'llm' | 'tts' | 'stt';
|
|
26
|
+
|
|
9
27
|
interface ModelInfo {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
size: number;
|
|
14
|
-
requirements: {
|
|
15
|
-
minMemoryMB: number;
|
|
16
|
-
minStorageMB: number;
|
|
17
|
-
platforms: string[];
|
|
18
|
-
};
|
|
28
|
+
hfModel: string;
|
|
29
|
+
task: string;
|
|
30
|
+
category: ModelCategory;
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
const modelMap: Record<string, ModelInfo> = {
|
|
34
|
+
// LLM models (1B+)
|
|
35
|
+
'quantum-1.7b': {
|
|
36
|
+
hfModel: 'HuggingFaceTB/SmolLM2-1.7B-Instruct',
|
|
37
|
+
task: 'text-generation',
|
|
38
|
+
category: 'llm',
|
|
39
|
+
},
|
|
40
|
+
'quantum-3b': {
|
|
41
|
+
hfModel: 'meta-llama/Llama-3.2-3B-Instruct',
|
|
42
|
+
task: 'text-generation',
|
|
43
|
+
category: 'llm',
|
|
44
|
+
},
|
|
45
|
+
'quantum-code-3b': {
|
|
46
|
+
hfModel: 'Qwen/Qwen2.5-Coder-3B-Instruct',
|
|
47
|
+
task: 'text-generation',
|
|
48
|
+
category: 'llm',
|
|
49
|
+
},
|
|
50
|
+
'quantum-8b': {
|
|
51
|
+
hfModel: 'meta-llama/Llama-3.1-8B-Instruct',
|
|
52
|
+
task: 'text-generation',
|
|
53
|
+
category: 'llm',
|
|
54
|
+
},
|
|
55
|
+
// STT models
|
|
56
|
+
'voicecore-base': {
|
|
57
|
+
hfModel: 'onnx-community/whisper-base',
|
|
58
|
+
task: 'automatic-speech-recognition',
|
|
59
|
+
category: 'stt',
|
|
60
|
+
},
|
|
61
|
+
'voicecore-small': {
|
|
62
|
+
hfModel: 'onnx-community/whisper-small',
|
|
63
|
+
task: 'automatic-speech-recognition',
|
|
64
|
+
category: 'stt',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
21
68
|
class SlyOS {
|
|
22
69
|
private apiKey: string;
|
|
23
70
|
private apiUrl: string;
|
|
24
|
-
private api: AxiosInstance;
|
|
25
71
|
private deviceId: string;
|
|
72
|
+
private token: string | null = null;
|
|
26
73
|
private models: Map<string, any> = new Map();
|
|
27
74
|
|
|
28
75
|
constructor(config: SlyOSConfig) {
|
|
29
76
|
this.apiKey = config.apiKey;
|
|
30
|
-
this.apiUrl = config.apiUrl || '
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
77
|
+
this.apiUrl = config.apiUrl || 'https://api.slyos.world';
|
|
78
|
+
this.deviceId = `device-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async initialize(): Promise<void> {
|
|
82
|
+
// Authenticate using API key
|
|
83
|
+
const authRes = await axios.post(`${this.apiUrl}/api/auth/sdk`, {
|
|
84
|
+
apiKey: this.apiKey,
|
|
38
85
|
});
|
|
86
|
+
this.token = authRes.data.token;
|
|
39
87
|
|
|
40
|
-
|
|
88
|
+
// Register this device
|
|
89
|
+
await axios.post(`${this.apiUrl}/api/devices/register`, {
|
|
90
|
+
device_id: this.deviceId,
|
|
91
|
+
platform: typeof window !== 'undefined' ? 'web' : 'nodejs',
|
|
92
|
+
os_version: typeof window !== 'undefined' ? navigator.userAgent : process.version,
|
|
93
|
+
total_memory_mb: 4096,
|
|
94
|
+
cpu_cores: 4,
|
|
95
|
+
has_gpu: false,
|
|
96
|
+
}, {
|
|
97
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
98
|
+
});
|
|
41
99
|
}
|
|
42
100
|
|
|
43
|
-
|
|
44
|
-
|
|
101
|
+
getAvailableModels(): Record<string, { models: string[] }> {
|
|
102
|
+
const grouped: Record<string, string[]> = { llm: [], stt: [] };
|
|
103
|
+
for (const [id, info] of Object.entries(modelMap)) {
|
|
104
|
+
if (!grouped[info.category]) grouped[info.category] = [];
|
|
105
|
+
grouped[info.category].push(id);
|
|
106
|
+
}
|
|
107
|
+
return Object.fromEntries(
|
|
108
|
+
Object.entries(grouped).map(([cat, models]) => [cat, { models }])
|
|
109
|
+
);
|
|
45
110
|
}
|
|
46
111
|
|
|
47
|
-
async
|
|
48
|
-
|
|
49
|
-
|
|
112
|
+
async loadModel(modelId: string): Promise<void> {
|
|
113
|
+
const info = modelMap[modelId];
|
|
114
|
+
if (!info) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Unknown model "${modelId}". Available: ${Object.keys(modelMap).join(', ')}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
50
120
|
try {
|
|
51
|
-
await
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
os_version: navigator.userAgent,
|
|
55
|
-
total_memory_mb: this.getMemoryInfo(),
|
|
56
|
-
cpu_cores: navigator.hardwareConcurrency || 4
|
|
121
|
+
const pipe = await pipeline(info.task as any, info.hfModel, {
|
|
122
|
+
device: 'cpu',
|
|
123
|
+
dtype: 'fp32',
|
|
57
124
|
});
|
|
58
|
-
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error('Failed to register device:', error);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
125
|
+
this.models.set(modelId, { pipe, info });
|
|
63
126
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
127
|
+
if (this.token) {
|
|
128
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
129
|
+
device_id: this.deviceId,
|
|
130
|
+
event_type: 'model_load',
|
|
131
|
+
model_id: modelId,
|
|
132
|
+
success: true,
|
|
133
|
+
}, {
|
|
134
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
135
|
+
}).catch(() => {});
|
|
136
|
+
}
|
|
137
|
+
} catch (error: any) {
|
|
138
|
+
if (this.token) {
|
|
139
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
140
|
+
device_id: this.deviceId,
|
|
141
|
+
event_type: 'model_load',
|
|
142
|
+
model_id: modelId,
|
|
143
|
+
success: false,
|
|
144
|
+
error_message: error.message,
|
|
145
|
+
}, {
|
|
146
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
147
|
+
}).catch(() => {});
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
69
151
|
}
|
|
70
152
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
153
|
+
async generate(modelId: string, prompt: string, options: GenerateOptions = {}): Promise<string> {
|
|
154
|
+
if (!this.models.has(modelId)) {
|
|
155
|
+
await this.loadModel(modelId);
|
|
156
|
+
}
|
|
75
157
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return res.data.map((m: any) => ({
|
|
80
|
-
id: m.model_id,
|
|
81
|
-
name: m.name,
|
|
82
|
-
displayName: m.display_name,
|
|
83
|
-
size: m.size_q4,
|
|
84
|
-
requirements: {
|
|
85
|
-
minMemoryMB: parseInt(m.memory_required) || 512,
|
|
86
|
-
minStorageMB: m.size_q4 + 100,
|
|
87
|
-
platforms: ['ios', 'android', 'web']
|
|
88
|
-
}
|
|
89
|
-
}));
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.error('Failed to fetch models:', error);
|
|
92
|
-
return [];
|
|
158
|
+
const { pipe, info } = this.models.get(modelId);
|
|
159
|
+
if (info.category !== 'llm') {
|
|
160
|
+
throw new Error(`Model "${modelId}" is not an LLM. Use transcribe() for STT models.`);
|
|
93
161
|
}
|
|
94
|
-
}
|
|
95
162
|
|
|
96
|
-
async loadModel(modelId: string): Promise<void> {
|
|
97
|
-
console.log(`📥 Loading model: ${modelId}`);
|
|
98
|
-
|
|
99
163
|
const startTime = Date.now();
|
|
100
164
|
|
|
101
165
|
try {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const hfModel = modelMap[modelId] || modelMap['quantum-360m'];
|
|
109
|
-
|
|
110
|
-
const generator = await pipeline('text-generation', hfModel, {
|
|
111
|
-
device: 'webgpu',
|
|
112
|
-
dtype: 'q4'
|
|
166
|
+
const result = await pipe(prompt, {
|
|
167
|
+
max_new_tokens: options.maxTokens || 100,
|
|
168
|
+
temperature: options.temperature || 0.7,
|
|
169
|
+
top_p: options.topP || 0.9,
|
|
170
|
+
do_sample: true,
|
|
113
171
|
});
|
|
114
172
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const loadTime = Date.now() - startTime;
|
|
118
|
-
console.log(`✅ Model loaded in ${loadTime}ms`);
|
|
119
|
-
|
|
120
|
-
await this.sendTelemetry({
|
|
121
|
-
event_type: 'model_load',
|
|
122
|
-
model_id: modelId,
|
|
123
|
-
latency_ms: loadTime,
|
|
124
|
-
success: true
|
|
125
|
-
});
|
|
173
|
+
const response = result[0].generated_text;
|
|
174
|
+
const latency = Date.now() - startTime;
|
|
126
175
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
176
|
+
if (this.token) {
|
|
177
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
178
|
+
device_id: this.deviceId,
|
|
179
|
+
event_type: 'inference',
|
|
180
|
+
model_id: modelId,
|
|
181
|
+
latency_ms: latency,
|
|
182
|
+
tokens_generated: response.split(' ').length,
|
|
183
|
+
success: true,
|
|
184
|
+
}, {
|
|
185
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
186
|
+
}).catch(() => {});
|
|
187
|
+
}
|
|
136
188
|
|
|
189
|
+
return response;
|
|
190
|
+
} catch (error: any) {
|
|
191
|
+
if (this.token) {
|
|
192
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
193
|
+
device_id: this.deviceId,
|
|
194
|
+
event_type: 'inference',
|
|
195
|
+
model_id: modelId,
|
|
196
|
+
success: false,
|
|
197
|
+
error_message: error.message,
|
|
198
|
+
}, {
|
|
199
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
200
|
+
}).catch(() => {});
|
|
201
|
+
}
|
|
137
202
|
throw error;
|
|
138
203
|
}
|
|
139
204
|
}
|
|
140
205
|
|
|
141
|
-
async
|
|
206
|
+
async transcribe(modelId: string, audioInput: any, options: TranscribeOptions = {}): Promise<string> {
|
|
142
207
|
if (!this.models.has(modelId)) {
|
|
143
208
|
await this.loadModel(modelId);
|
|
144
209
|
}
|
|
145
210
|
|
|
146
|
-
const
|
|
211
|
+
const { pipe, info } = this.models.get(modelId);
|
|
212
|
+
if (info.category !== 'stt') {
|
|
213
|
+
throw new Error(`Model "${modelId}" is not an STT model. Use generate() for LLMs.`);
|
|
214
|
+
}
|
|
215
|
+
|
|
147
216
|
const startTime = Date.now();
|
|
148
217
|
|
|
149
218
|
try {
|
|
150
|
-
const result = await
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
top_p: options?.topP || 0.9,
|
|
154
|
-
...options
|
|
219
|
+
const result = await pipe(audioInput, {
|
|
220
|
+
language: options.language || 'en',
|
|
221
|
+
return_timestamps: options.returnTimestamps || false,
|
|
155
222
|
});
|
|
156
223
|
|
|
224
|
+
const text = result.text;
|
|
157
225
|
const latency = Date.now() - startTime;
|
|
158
|
-
const response = result[0].generated_text;
|
|
159
|
-
const tokens = response.split(' ').length;
|
|
160
|
-
|
|
161
|
-
await this.sendTelemetry({
|
|
162
|
-
event_type: 'inference',
|
|
163
|
-
model_id: modelId,
|
|
164
|
-
latency_ms: latency,
|
|
165
|
-
tokens_generated: tokens,
|
|
166
|
-
success: true
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
console.log(`⚡ Generated ${tokens} tokens in ${latency}ms`);
|
|
170
|
-
|
|
171
|
-
return response;
|
|
172
226
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
227
|
+
if (this.token) {
|
|
228
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
229
|
+
device_id: this.deviceId,
|
|
230
|
+
event_type: 'inference',
|
|
231
|
+
model_id: modelId,
|
|
232
|
+
latency_ms: latency,
|
|
233
|
+
success: true,
|
|
234
|
+
}, {
|
|
235
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
236
|
+
}).catch(() => {});
|
|
237
|
+
}
|
|
182
238
|
|
|
239
|
+
return text;
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
if (this.token) {
|
|
242
|
+
await axios.post(`${this.apiUrl}/api/telemetry`, {
|
|
243
|
+
device_id: this.deviceId,
|
|
244
|
+
event_type: 'inference',
|
|
245
|
+
model_id: modelId,
|
|
246
|
+
success: false,
|
|
247
|
+
error_message: error.message,
|
|
248
|
+
}, {
|
|
249
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
250
|
+
}).catch(() => {});
|
|
251
|
+
}
|
|
183
252
|
throw error;
|
|
184
253
|
}
|
|
185
254
|
}
|
|
186
|
-
|
|
187
|
-
private async sendTelemetry(data: any): Promise<void> {
|
|
188
|
-
try {
|
|
189
|
-
await this.api.post('/telemetry', {
|
|
190
|
-
device_id: this.deviceId,
|
|
191
|
-
...data
|
|
192
|
-
});
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error('Failed to send telemetry:', error);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
getDeviceId(): string {
|
|
199
|
-
return this.deviceId;
|
|
200
|
-
}
|
|
201
255
|
}
|
|
202
256
|
|
|
203
257
|
export default SlyOS;
|
|
204
|
-
export { SlyOS, SlyOSConfig, ModelInfo };
|