@emilshirokikh/slyos-sdk 1.2.2 → 1.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/create-chatbot.sh +506 -0
- package/dist/index.d.ts +86 -3
- package/dist/index.js +279 -1
- package/package.json +1 -1
- package/src/index.ts +421 -4
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
#################################################################################
|
|
4
|
+
# Slyos Chatbot Setup Script
|
|
5
|
+
#
|
|
6
|
+
# This script creates a fully functional interactive chatbot using the Slyos SDK.
|
|
7
|
+
# Supports both Mac and Windows (via bash/powershell).
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ./create-chatbot.sh [--api-key YOUR_KEY] [--model MODEL_NAME]
|
|
11
|
+
#
|
|
12
|
+
# Examples:
|
|
13
|
+
# ./create-chatbot.sh
|
|
14
|
+
# ./create-chatbot.sh --api-key sk_123456789 --model quantum-1.7b
|
|
15
|
+
#################################################################################
|
|
16
|
+
|
|
17
|
+
set -e
|
|
18
|
+
|
|
19
|
+
# Color codes for terminal output
|
|
20
|
+
RED='\033[0;31m'
|
|
21
|
+
GREEN='\033[0;32m'
|
|
22
|
+
YELLOW='\033[1;33m'
|
|
23
|
+
BLUE='\033[0;34m'
|
|
24
|
+
CYAN='\033[0;36m'
|
|
25
|
+
NC='\033[0m' # No Color
|
|
26
|
+
|
|
27
|
+
# Default values
|
|
28
|
+
API_KEY=""
|
|
29
|
+
MODEL="quantum-1.7b"
|
|
30
|
+
SLYOS_SERVER="https://slyos-prod.eba-qjz3cmgq.us-east-2.elasticbeanstalk.com"
|
|
31
|
+
PROJECT_NAME="slyos-chatbot"
|
|
32
|
+
|
|
33
|
+
#################################################################################
|
|
34
|
+
# Helper Functions
|
|
35
|
+
#################################################################################
|
|
36
|
+
|
|
37
|
+
print_header() {
|
|
38
|
+
echo -e "\n${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
39
|
+
echo -e "${BLUE}║${NC} ${CYAN}Slyos Interactive Chatbot Setup${NC} ${BLUE}║${NC}"
|
|
40
|
+
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}\n"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
print_step() {
|
|
44
|
+
echo -e "${CYAN}▶${NC} $1"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
print_success() {
|
|
48
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
print_error() {
|
|
52
|
+
echo -e "${RED}✗${NC} $1" >&2
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
print_info() {
|
|
56
|
+
echo -e "${YELLOW}ℹ${NC} $1"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#################################################################################
|
|
60
|
+
# Argument Parsing
|
|
61
|
+
#################################################################################
|
|
62
|
+
|
|
63
|
+
while [[ $# -gt 0 ]]; do
|
|
64
|
+
case $1 in
|
|
65
|
+
--api-key)
|
|
66
|
+
API_KEY="$2"
|
|
67
|
+
shift 2
|
|
68
|
+
;;
|
|
69
|
+
--model)
|
|
70
|
+
MODEL="$2"
|
|
71
|
+
shift 2
|
|
72
|
+
;;
|
|
73
|
+
-h|--help)
|
|
74
|
+
echo "Usage: $0 [OPTIONS]"
|
|
75
|
+
echo ""
|
|
76
|
+
echo "Options:"
|
|
77
|
+
echo " --api-key KEY Slyos API key (prompted if not provided)"
|
|
78
|
+
echo " --model MODEL AI model to use (default: quantum-1.7b)"
|
|
79
|
+
echo " -h, --help Show this help message"
|
|
80
|
+
exit 0
|
|
81
|
+
;;
|
|
82
|
+
*)
|
|
83
|
+
print_error "Unknown option: $1"
|
|
84
|
+
exit 1
|
|
85
|
+
;;
|
|
86
|
+
esac
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
#################################################################################
|
|
90
|
+
# Main Setup
|
|
91
|
+
#################################################################################
|
|
92
|
+
|
|
93
|
+
print_header
|
|
94
|
+
|
|
95
|
+
# Prompt for API key if not provided
|
|
96
|
+
if [ -z "$API_KEY" ]; then
|
|
97
|
+
if [ -t 0 ]; then
|
|
98
|
+
print_step "Enter your Slyos API key (or press Enter for placeholder)"
|
|
99
|
+
read -r -p " API Key: " API_KEY
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [ -z "$API_KEY" ]; then
|
|
103
|
+
API_KEY="YOUR_API_KEY"
|
|
104
|
+
print_info "Using placeholder API key — set SLYOS_API_KEY in .env later"
|
|
105
|
+
else
|
|
106
|
+
print_success "API key configured"
|
|
107
|
+
fi
|
|
108
|
+
else
|
|
109
|
+
print_success "API key provided via arguments"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# Confirm model selection
|
|
113
|
+
print_step "AI Model Configuration"
|
|
114
|
+
echo -e " Current model: ${YELLOW}${MODEL}${NC}"
|
|
115
|
+
|
|
116
|
+
# Only prompt interactively if stdin is a terminal (not piped)
|
|
117
|
+
if [ -t 0 ]; then
|
|
118
|
+
read -p " Use this model? (y/n, default: y): " -r -n 1
|
|
119
|
+
echo
|
|
120
|
+
if [[ ! $REPLY =~ ^[Yy]?$ ]]; then
|
|
121
|
+
read -p " Enter model name: " -r MODEL
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
print_success "Model configured: ${YELLOW}${MODEL}${NC}"
|
|
125
|
+
|
|
126
|
+
# Check if project already exists
|
|
127
|
+
if [ -d "$PROJECT_NAME" ]; then
|
|
128
|
+
if [ -t 0 ]; then
|
|
129
|
+
print_error "Project folder '$PROJECT_NAME' already exists!"
|
|
130
|
+
read -p " Remove existing folder and continue? (y/n): " -r -n 1
|
|
131
|
+
echo
|
|
132
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
133
|
+
rm -rf "$PROJECT_NAME"
|
|
134
|
+
print_success "Existing folder removed"
|
|
135
|
+
else
|
|
136
|
+
print_error "Setup cancelled"
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
else
|
|
140
|
+
# Non-interactive: auto-remove
|
|
141
|
+
rm -rf "$PROJECT_NAME"
|
|
142
|
+
print_success "Existing folder removed"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Create project directory
|
|
147
|
+
print_step "Creating project directory: ${CYAN}$PROJECT_NAME${NC}"
|
|
148
|
+
mkdir -p "$PROJECT_NAME"
|
|
149
|
+
cd "$PROJECT_NAME"
|
|
150
|
+
print_success "Project directory created"
|
|
151
|
+
|
|
152
|
+
# Initialize npm
|
|
153
|
+
print_step "Initializing npm package"
|
|
154
|
+
npm init -y > /dev/null 2>&1
|
|
155
|
+
print_success "npm initialized"
|
|
156
|
+
|
|
157
|
+
# Update package.json to use ES modules
|
|
158
|
+
print_step "Configuring ES module support"
|
|
159
|
+
cat > package.json << 'EOF'
|
|
160
|
+
{
|
|
161
|
+
"name": "slyos-chatbot",
|
|
162
|
+
"version": "1.0.0",
|
|
163
|
+
"description": "Interactive chatbot powered by Slyos SDK",
|
|
164
|
+
"main": "app.mjs",
|
|
165
|
+
"type": "module",
|
|
166
|
+
"scripts": {
|
|
167
|
+
"start": "node app.mjs",
|
|
168
|
+
"chat": "node app.mjs"
|
|
169
|
+
},
|
|
170
|
+
"keywords": ["chatbot", "slyos", "ai"],
|
|
171
|
+
"author": "",
|
|
172
|
+
"license": "MIT"
|
|
173
|
+
}
|
|
174
|
+
EOF
|
|
175
|
+
print_success "Package configuration updated"
|
|
176
|
+
|
|
177
|
+
# Install Slyos SDK + dotenv
|
|
178
|
+
print_step "Installing dependencies"
|
|
179
|
+
print_info "This may take a moment..."
|
|
180
|
+
npm install @emilshirokikh/slyos-sdk dotenv > /dev/null 2>&1
|
|
181
|
+
print_success "Dependencies installed"
|
|
182
|
+
|
|
183
|
+
# Create the chatbot application
|
|
184
|
+
print_step "Creating interactive chatbot application: ${CYAN}app.mjs${NC}"
|
|
185
|
+
|
|
186
|
+
cat > app.mjs << 'CHATBOT_EOF'
|
|
187
|
+
#!/usr/bin/env node
|
|
188
|
+
|
|
189
|
+
import 'dotenv/config';
|
|
190
|
+
import readline from 'readline';
|
|
191
|
+
import SlyOS from '@emilshirokikh/slyos-sdk';
|
|
192
|
+
|
|
193
|
+
// Color codes for terminal output
|
|
194
|
+
const colors = {
|
|
195
|
+
reset: '\x1b[0m',
|
|
196
|
+
bright: '\x1b[1m',
|
|
197
|
+
dim: '\x1b[2m',
|
|
198
|
+
cyan: '\x1b[36m',
|
|
199
|
+
green: '\x1b[32m',
|
|
200
|
+
yellow: '\x1b[33m',
|
|
201
|
+
blue: '\x1b[34m',
|
|
202
|
+
red: '\x1b[31m',
|
|
203
|
+
magenta: '\x1b[35m'
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Configuration
|
|
207
|
+
const config = {
|
|
208
|
+
apiKey: process.env.SLYOS_API_KEY || 'YOUR_API_KEY',
|
|
209
|
+
model: process.env.SLYOS_MODEL || 'quantum-1.7b',
|
|
210
|
+
server: process.env.SLYOS_SERVER || 'https://slyos-prod.eba-qjz3cmgq.us-east-2.elasticbeanstalk.com'
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Initialize SlyOS SDK
|
|
214
|
+
let sdk;
|
|
215
|
+
try {
|
|
216
|
+
sdk = new SlyOS({
|
|
217
|
+
apiKey: config.apiKey,
|
|
218
|
+
onProgress: (e) => console.log(`${colors.dim}[${e.progress}%] ${e.message}${colors.reset}`)
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error(`${colors.red}Error initializing SDK:${colors.reset}`, error.message);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Create readline interface
|
|
226
|
+
const rl = readline.createInterface({
|
|
227
|
+
input: process.stdin,
|
|
228
|
+
output: process.stdout,
|
|
229
|
+
terminal: true
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Note: conversation history is not used for generation with small models
|
|
233
|
+
// They work better with single prompts
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Print welcome banner
|
|
237
|
+
*/
|
|
238
|
+
function printWelcome() {
|
|
239
|
+
console.clear();
|
|
240
|
+
console.log(`${colors.bright}${colors.cyan}╔════════════════════════════════════════════════════════════╗${colors.reset}`);
|
|
241
|
+
console.log(`${colors.bright}${colors.cyan}║${colors.reset} ${colors.bright}${colors.cyan}║${colors.reset}`);
|
|
242
|
+
console.log(`${colors.bright}${colors.cyan}║${colors.reset} ${colors.bright}Welcome to the Slyos Interactive Chatbot${colors.reset} ${colors.bright}${colors.cyan}║${colors.reset}`);
|
|
243
|
+
console.log(`${colors.bright}${colors.cyan}║${colors.reset} ${colors.bright}${colors.cyan}║${colors.reset}`);
|
|
244
|
+
console.log(`${colors.bright}${colors.cyan}╚════════════════════════════════════════════════════════════╝${colors.reset}\n`);
|
|
245
|
+
|
|
246
|
+
console.log(`${colors.blue}Model:${colors.reset} ${colors.yellow}${config.model}${colors.reset}`);
|
|
247
|
+
console.log(`${colors.blue}Server:${colors.reset} ${colors.yellow}${config.server}${colors.reset}`);
|
|
248
|
+
if (config.apiKey === 'YOUR_API_KEY') {
|
|
249
|
+
console.log(`${colors.red}⚠ Using placeholder API key - set SLYOS_API_KEY environment variable${colors.reset}`);
|
|
250
|
+
}
|
|
251
|
+
console.log(`\n${colors.bright}Commands:${colors.reset}`);
|
|
252
|
+
console.log(` ${colors.green}Type your message and press Enter to chat${colors.reset}`);
|
|
253
|
+
console.log(` ${colors.green}Type 'clear' to clear conversation history${colors.reset}`);
|
|
254
|
+
console.log(` ${colors.green}Type 'exit' or 'quit' to end the session${colors.reset}`);
|
|
255
|
+
console.log(`\n${colors.bright}${colors.cyan}─────────────────────────────────────────────────────────────${colors.reset}\n`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Send message to AI and get response
|
|
260
|
+
*/
|
|
261
|
+
async function sendMessage(userMessage) {
|
|
262
|
+
try {
|
|
263
|
+
console.log(`${colors.dim}Thinking...${colors.reset}`);
|
|
264
|
+
|
|
265
|
+
// Use chatCompletion (OpenAI-compatible) — handles prompt formatting for any model
|
|
266
|
+
const response = await sdk.chatCompletion(config.model, {
|
|
267
|
+
messages: [
|
|
268
|
+
{ role: 'system', content: 'You are a helpful AI assistant. Give short, direct answers.' },
|
|
269
|
+
{ role: 'user', content: userMessage }
|
|
270
|
+
],
|
|
271
|
+
max_tokens: 200,
|
|
272
|
+
temperature: 0.7
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
let assistantMessage = response?.choices?.[0]?.message?.content || '';
|
|
276
|
+
|
|
277
|
+
// Light cleanup — stop at any hallucinated role prefixes
|
|
278
|
+
assistantMessage = assistantMessage
|
|
279
|
+
.split(/\n\s*(User|Human|System):/i)[0]
|
|
280
|
+
.trim();
|
|
281
|
+
|
|
282
|
+
if (!assistantMessage) {
|
|
283
|
+
assistantMessage = '(No response generated — try rephrasing your question)';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log(`\n${colors.bright}${colors.magenta}AI:${colors.reset} ${assistantMessage}\n`);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error(`\n${colors.red}Error:${colors.reset} ${error.message}\n`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Prompt user for input
|
|
294
|
+
*/
|
|
295
|
+
function promptUser() {
|
|
296
|
+
rl.question(`${colors.bright}${colors.green}You:${colors.reset} `, async (input) => {
|
|
297
|
+
const message = input.trim();
|
|
298
|
+
|
|
299
|
+
if (!message) {
|
|
300
|
+
promptUser();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Handle commands
|
|
305
|
+
if (message.toLowerCase() === 'exit' || message.toLowerCase() === 'quit') {
|
|
306
|
+
console.log(`\n${colors.bright}${colors.cyan}Thank you for chatting! Goodbye.${colors.reset}\n`);
|
|
307
|
+
rl.close();
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (message.toLowerCase() === 'clear') {
|
|
312
|
+
console.clear();
|
|
313
|
+
printWelcome();
|
|
314
|
+
console.log(`${colors.green}✓ Screen cleared${colors.reset}\n`);
|
|
315
|
+
promptUser();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Send message to AI
|
|
320
|
+
await sendMessage(message);
|
|
321
|
+
promptUser();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Main entry point
|
|
327
|
+
*/
|
|
328
|
+
async function main() {
|
|
329
|
+
printWelcome();
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
console.log(`${colors.cyan}Initializing SlyOS...${colors.reset}`);
|
|
333
|
+
await sdk.initialize();
|
|
334
|
+
|
|
335
|
+
console.log(`${colors.cyan}Loading model: ${config.model}...${colors.reset}`);
|
|
336
|
+
await sdk.loadModel(config.model);
|
|
337
|
+
|
|
338
|
+
console.log(`${colors.green}Ready! Start chatting below.${colors.reset}\n`);
|
|
339
|
+
console.log(`${colors.bright}${colors.cyan}─────────────────────────────────────────────────────────────${colors.reset}\n`);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error(`${colors.red}Failed to initialize: ${error.message}${colors.reset}`);
|
|
342
|
+
console.error(`${colors.dim}Make sure your API key is correct and you have internet access.${colors.reset}`);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
promptUser();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Handle process termination gracefully
|
|
350
|
+
process.on('SIGINT', () => {
|
|
351
|
+
console.log(`\n${colors.bright}${colors.cyan}Session ended. Goodbye!${colors.reset}\n`);
|
|
352
|
+
rl.close();
|
|
353
|
+
process.exit(0);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
process.on('SIGTERM', () => {
|
|
357
|
+
rl.close();
|
|
358
|
+
process.exit(0);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Start the chatbot
|
|
362
|
+
main();
|
|
363
|
+
CHATBOT_EOF
|
|
364
|
+
|
|
365
|
+
chmod +x app.mjs
|
|
366
|
+
print_success "Chatbot application created"
|
|
367
|
+
|
|
368
|
+
# Create .env.example file
|
|
369
|
+
print_step "Creating environment configuration example"
|
|
370
|
+
cat > .env.example << 'ENV_EOF'
|
|
371
|
+
# Slyos SDK Configuration
|
|
372
|
+
SLYOS_API_KEY=your_api_key_here
|
|
373
|
+
SLYOS_MODEL=quantum-1.7b
|
|
374
|
+
SLYOS_SERVER=https://slyos-prod.eba-qjz3cmgq.us-east-2.elasticbeanstalk.com
|
|
375
|
+
ENV_EOF
|
|
376
|
+
print_success "Environment configuration template created"
|
|
377
|
+
|
|
378
|
+
# Create README
|
|
379
|
+
print_step "Creating README documentation"
|
|
380
|
+
cat > README.md << 'README_EOF'
|
|
381
|
+
# Slyos Interactive Chatbot
|
|
382
|
+
|
|
383
|
+
A simple yet powerful interactive chatbot powered by the Slyos SDK.
|
|
384
|
+
|
|
385
|
+
## Features
|
|
386
|
+
|
|
387
|
+
- Interactive command-line interface with colored output
|
|
388
|
+
- Conversation history management
|
|
389
|
+
- Easy API configuration
|
|
390
|
+
- Cross-platform support (Mac, Windows, Linux)
|
|
391
|
+
|
|
392
|
+
## Installation
|
|
393
|
+
|
|
394
|
+
1. Clone or download this project
|
|
395
|
+
2. Install dependencies: `npm install`
|
|
396
|
+
3. Configure your API key (see Configuration)
|
|
397
|
+
|
|
398
|
+
## Configuration
|
|
399
|
+
|
|
400
|
+
### Environment Variables
|
|
401
|
+
|
|
402
|
+
Set these environment variables before running:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
export SLYOS_API_KEY=your_api_key_here
|
|
406
|
+
export SLYOS_MODEL=quantum-1.7b
|
|
407
|
+
export SLYOS_SERVER=https://slyos-prod.eba-qjz3cmgq.us-east-2.elasticbeanstalk.com
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Or create a `.env` file based on `.env.example`.
|
|
411
|
+
|
|
412
|
+
## Running the Chatbot
|
|
413
|
+
|
|
414
|
+
### Direct Method
|
|
415
|
+
```bash
|
|
416
|
+
npm start
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### With Environment Variables
|
|
420
|
+
```bash
|
|
421
|
+
SLYOS_API_KEY=your_key npm start
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Manual
|
|
425
|
+
```bash
|
|
426
|
+
node app.mjs
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Usage
|
|
430
|
+
|
|
431
|
+
Once the chatbot starts:
|
|
432
|
+
|
|
433
|
+
- **Chat**: Type your message and press Enter
|
|
434
|
+
- **Clear History**: Type `clear` to reset conversation
|
|
435
|
+
- **Exit**: Type `exit` or `quit` to end session
|
|
436
|
+
- **Interrupt**: Press Ctrl+C to exit anytime
|
|
437
|
+
|
|
438
|
+
## API Response Format
|
|
439
|
+
|
|
440
|
+
The chatbot supports multiple response formats from the SDK:
|
|
441
|
+
|
|
442
|
+
- `response.content` - Primary response text
|
|
443
|
+
- `response.text` - Alternative response field
|
|
444
|
+
- Direct string response - Fallback format
|
|
445
|
+
|
|
446
|
+
## Troubleshooting
|
|
447
|
+
|
|
448
|
+
### "Error initializing SDK"
|
|
449
|
+
- Check that your API key is valid
|
|
450
|
+
- Verify the Slyos server is accessible
|
|
451
|
+
- Ensure internet connection is active
|
|
452
|
+
|
|
453
|
+
### "Cannot find module '@emilshirokikh/slyos-sdk'"
|
|
454
|
+
- Run `npm install` to install dependencies
|
|
455
|
+
- Check npm log: `npm list`
|
|
456
|
+
|
|
457
|
+
### Placeholder API Key Warning
|
|
458
|
+
- Set the `SLYOS_API_KEY` environment variable with your actual key
|
|
459
|
+
- Or update `config.apiKey` in `app.mjs`
|
|
460
|
+
|
|
461
|
+
## System Requirements
|
|
462
|
+
|
|
463
|
+
- Node.js 14+ (14.17.0 or higher recommended)
|
|
464
|
+
- npm 6+
|
|
465
|
+
- Internet connection for API access
|
|
466
|
+
|
|
467
|
+
## License
|
|
468
|
+
|
|
469
|
+
MIT
|
|
470
|
+
README_EOF
|
|
471
|
+
print_success "README created"
|
|
472
|
+
|
|
473
|
+
# Set up environment with provided values
|
|
474
|
+
print_step "Configuring environment variables"
|
|
475
|
+
cat > .env << ENV_SETUP_EOF
|
|
476
|
+
SLYOS_API_KEY=${API_KEY}
|
|
477
|
+
SLYOS_MODEL=${MODEL}
|
|
478
|
+
SLYOS_SERVER=${SLYOS_SERVER}
|
|
479
|
+
ENV_SETUP_EOF
|
|
480
|
+
print_success "Environment configured"
|
|
481
|
+
|
|
482
|
+
# Final summary
|
|
483
|
+
echo ""
|
|
484
|
+
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
485
|
+
echo -e "${BLUE}║${NC} ${GREEN}✓ Setup Complete!${NC} ${BLUE}║${NC}"
|
|
486
|
+
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
487
|
+
echo ""
|
|
488
|
+
echo -e "${CYAN}Project Details:${NC}"
|
|
489
|
+
echo " Location: ${YELLOW}$(pwd)${NC}"
|
|
490
|
+
echo " API Key: ${YELLOW}${API_KEY}${NC}"
|
|
491
|
+
echo " Model: ${YELLOW}${MODEL}${NC}"
|
|
492
|
+
echo ""
|
|
493
|
+
echo -e "${CYAN}Next Steps:${NC}"
|
|
494
|
+
echo " 1. Review the .env file and update your API key if needed"
|
|
495
|
+
echo " 2. Run the chatbot: ${YELLOW}npm start${NC}"
|
|
496
|
+
echo " 3. Type messages to chat with the AI"
|
|
497
|
+
echo " 4. Type 'exit' to quit"
|
|
498
|
+
echo ""
|
|
499
|
+
echo -e "${GREEN}Ready to chat! 🚀${NC}"
|
|
500
|
+
echo ""
|
|
501
|
+
|
|
502
|
+
# Tell user how to start (can't auto-run when piped because stdin is closed)
|
|
503
|
+
echo -e "${CYAN}To start chatting, run:${NC}"
|
|
504
|
+
echo ""
|
|
505
|
+
echo -e " ${YELLOW}cd ${PROJECT_NAME} && npm start${NC}"
|
|
506
|
+
echo ""
|
package/dist/index.d.ts
CHANGED
|
@@ -31,12 +31,81 @@ interface ProgressEvent {
|
|
|
31
31
|
detail?: any;
|
|
32
32
|
}
|
|
33
33
|
interface SlyEvent {
|
|
34
|
-
type: 'auth' | 'device_registered' | 'device_profiled' | 'model_download_start' | 'model_download_progress' | 'model_loaded' | 'inference_start' | 'inference_complete' | 'error';
|
|
34
|
+
type: 'auth' | 'device_registered' | 'device_profiled' | 'model_download_start' | 'model_download_progress' | 'model_loaded' | 'inference_start' | 'inference_complete' | 'error' | 'fallback_success' | 'fallback_error';
|
|
35
35
|
data?: any;
|
|
36
36
|
timestamp: number;
|
|
37
37
|
}
|
|
38
38
|
type ProgressCallback = (event: ProgressEvent) => void;
|
|
39
39
|
type EventCallback = (event: SlyEvent) => void;
|
|
40
|
+
interface OpenAIMessage {
|
|
41
|
+
role: 'system' | 'user' | 'assistant';
|
|
42
|
+
content: string;
|
|
43
|
+
}
|
|
44
|
+
interface OpenAIChatCompletionRequest {
|
|
45
|
+
messages: OpenAIMessage[];
|
|
46
|
+
temperature?: number;
|
|
47
|
+
top_p?: number;
|
|
48
|
+
max_tokens?: number;
|
|
49
|
+
frequency_penalty?: number;
|
|
50
|
+
presence_penalty?: number;
|
|
51
|
+
stop?: string | string[];
|
|
52
|
+
}
|
|
53
|
+
interface OpenAIChoice {
|
|
54
|
+
index: number;
|
|
55
|
+
message: OpenAIMessage;
|
|
56
|
+
finish_reason: string;
|
|
57
|
+
}
|
|
58
|
+
interface OpenAIUsage {
|
|
59
|
+
prompt_tokens: number;
|
|
60
|
+
completion_tokens: number;
|
|
61
|
+
total_tokens: number;
|
|
62
|
+
}
|
|
63
|
+
interface OpenAIChatCompletionResponse {
|
|
64
|
+
id: string;
|
|
65
|
+
object: 'chat.completion';
|
|
66
|
+
created: number;
|
|
67
|
+
model: string;
|
|
68
|
+
choices: OpenAIChoice[];
|
|
69
|
+
usage: OpenAIUsage;
|
|
70
|
+
}
|
|
71
|
+
interface BedrockTextGenerationConfig {
|
|
72
|
+
maxTokenCount?: number;
|
|
73
|
+
temperature?: number;
|
|
74
|
+
topP?: number;
|
|
75
|
+
topK?: number;
|
|
76
|
+
stopSequences?: string[];
|
|
77
|
+
}
|
|
78
|
+
interface BedrockInvokeRequest {
|
|
79
|
+
inputText: string;
|
|
80
|
+
textGenerationConfig?: BedrockTextGenerationConfig;
|
|
81
|
+
}
|
|
82
|
+
interface BedrockResult {
|
|
83
|
+
outputText: string;
|
|
84
|
+
tokenCount: number;
|
|
85
|
+
}
|
|
86
|
+
interface BedrockInvokeResponse {
|
|
87
|
+
results: BedrockResult[];
|
|
88
|
+
input_text_token_count?: number;
|
|
89
|
+
}
|
|
90
|
+
type FallbackProvider = 'openai' | 'bedrock';
|
|
91
|
+
interface FallbackConfig {
|
|
92
|
+
provider: FallbackProvider;
|
|
93
|
+
apiKey: string;
|
|
94
|
+
model: string;
|
|
95
|
+
region?: string;
|
|
96
|
+
}
|
|
97
|
+
interface SlyOSConfigWithFallback extends SlyOSConfig {
|
|
98
|
+
fallback?: FallbackConfig;
|
|
99
|
+
}
|
|
100
|
+
interface OpenAICompatibleClient {
|
|
101
|
+
chat: {
|
|
102
|
+
completions: {
|
|
103
|
+
create(request: OpenAIChatCompletionRequest & {
|
|
104
|
+
model: string;
|
|
105
|
+
}): Promise<OpenAIChatCompletionResponse>;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
}
|
|
40
109
|
declare class SlyOS {
|
|
41
110
|
private apiKey;
|
|
42
111
|
private apiUrl;
|
|
@@ -46,7 +115,8 @@ declare class SlyOS {
|
|
|
46
115
|
private deviceProfile;
|
|
47
116
|
private onProgress;
|
|
48
117
|
private onEvent;
|
|
49
|
-
|
|
118
|
+
private fallbackConfig;
|
|
119
|
+
constructor(config: SlyOSConfigWithFallback);
|
|
50
120
|
private emitProgress;
|
|
51
121
|
private emitEvent;
|
|
52
122
|
analyzeDevice(): Promise<DeviceProfile>;
|
|
@@ -75,6 +145,19 @@ declare class SlyOS {
|
|
|
75
145
|
}): Promise<void>;
|
|
76
146
|
generate(modelId: string, prompt: string, options?: GenerateOptions): Promise<string>;
|
|
77
147
|
transcribe(modelId: string, audioInput: any, options?: TranscribeOptions): Promise<string>;
|
|
148
|
+
chatCompletion(modelId: string, request: OpenAIChatCompletionRequest): Promise<OpenAIChatCompletionResponse>;
|
|
149
|
+
bedrockInvoke(modelId: string, request: BedrockInvokeRequest): Promise<BedrockInvokeResponse>;
|
|
150
|
+
private fallbackToOpenAI;
|
|
151
|
+
private fallbackToBedrock;
|
|
152
|
+
private fallbackToOpenAICloud;
|
|
153
|
+
private fallbackToBedrockCloud;
|
|
154
|
+
private invokeBedrockCloud;
|
|
155
|
+
private mapModelToOpenAI;
|
|
156
|
+
static openaiCompatible(config: {
|
|
157
|
+
apiKey: string;
|
|
158
|
+
apiUrl?: string;
|
|
159
|
+
fallback?: FallbackConfig;
|
|
160
|
+
}): OpenAICompatibleClient;
|
|
78
161
|
}
|
|
79
162
|
export default SlyOS;
|
|
80
|
-
export type { SlyOSConfig, GenerateOptions, TranscribeOptions, DeviceProfile, ProgressEvent, SlyEvent, QuantizationLevel, ModelCategory };
|
|
163
|
+
export type { SlyOSConfig, SlyOSConfigWithFallback, GenerateOptions, TranscribeOptions, DeviceProfile, ProgressEvent, SlyEvent, QuantizationLevel, ModelCategory, OpenAIMessage, OpenAIChatCompletionRequest, OpenAIChatCompletionResponse, OpenAIChoice, OpenAIUsage, BedrockTextGenerationConfig, BedrockInvokeRequest, BedrockInvokeResponse, BedrockResult, FallbackConfig, FallbackProvider, OpenAICompatibleClient, };
|
package/dist/index.js
CHANGED
|
@@ -153,6 +153,7 @@ class SlyOS {
|
|
|
153
153
|
this.deviceId = `device-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
154
154
|
this.onProgress = config.onProgress || null;
|
|
155
155
|
this.onEvent = config.onEvent || null;
|
|
156
|
+
this.fallbackConfig = config.fallback || null;
|
|
156
157
|
}
|
|
157
158
|
// ── Progress & Event Helpers ────────────────────────────────────
|
|
158
159
|
emitProgress(stage, progress, message, detail) {
|
|
@@ -412,7 +413,12 @@ class SlyOS {
|
|
|
412
413
|
top_p: options.topP || 0.9,
|
|
413
414
|
do_sample: true,
|
|
414
415
|
});
|
|
415
|
-
const
|
|
416
|
+
const rawOutput = result[0].generated_text;
|
|
417
|
+
// HuggingFace transformers returns the prompt + generated text concatenated.
|
|
418
|
+
// Strip the original prompt so we only return the NEW tokens.
|
|
419
|
+
const response = rawOutput.startsWith(prompt)
|
|
420
|
+
? rawOutput.slice(prompt.length).trim()
|
|
421
|
+
: rawOutput.trim();
|
|
416
422
|
const latency = Date.now() - startTime;
|
|
417
423
|
const tokensGenerated = response.split(/\s+/).length;
|
|
418
424
|
const tokensPerSec = (tokensGenerated / (latency / 1000)).toFixed(1);
|
|
@@ -500,5 +506,277 @@ class SlyOS {
|
|
|
500
506
|
throw error;
|
|
501
507
|
}
|
|
502
508
|
}
|
|
509
|
+
// ── OpenAI Compatibility ────────────────────────────────────────────
|
|
510
|
+
async chatCompletion(modelId, request) {
|
|
511
|
+
try {
|
|
512
|
+
// Convert OpenAI message format to a prompt string
|
|
513
|
+
const prompt = request.messages
|
|
514
|
+
.map(msg => {
|
|
515
|
+
if (msg.role === 'system') {
|
|
516
|
+
return `System: ${msg.content}`;
|
|
517
|
+
}
|
|
518
|
+
else if (msg.role === 'user') {
|
|
519
|
+
return `User: ${msg.content}`;
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
return `Assistant: ${msg.content}`;
|
|
523
|
+
}
|
|
524
|
+
})
|
|
525
|
+
.join('\n\n');
|
|
526
|
+
const response = await this.generate(modelId, prompt, {
|
|
527
|
+
temperature: request.temperature,
|
|
528
|
+
maxTokens: request.max_tokens,
|
|
529
|
+
topP: request.top_p,
|
|
530
|
+
});
|
|
531
|
+
// Estimate token counts (rough approximation: ~4 chars per token)
|
|
532
|
+
const promptTokens = Math.ceil(prompt.length / 4);
|
|
533
|
+
const completionTokens = Math.ceil(response.length / 4);
|
|
534
|
+
return {
|
|
535
|
+
id: `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
536
|
+
object: 'chat.completion',
|
|
537
|
+
created: Math.floor(Date.now() / 1000),
|
|
538
|
+
model: modelId,
|
|
539
|
+
choices: [
|
|
540
|
+
{
|
|
541
|
+
index: 0,
|
|
542
|
+
message: {
|
|
543
|
+
role: 'assistant',
|
|
544
|
+
content: response,
|
|
545
|
+
},
|
|
546
|
+
finish_reason: 'stop',
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
usage: {
|
|
550
|
+
prompt_tokens: promptTokens,
|
|
551
|
+
completion_tokens: completionTokens,
|
|
552
|
+
total_tokens: promptTokens + completionTokens,
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
// Fallback to cloud provider if configured
|
|
558
|
+
if (this.fallbackConfig?.provider === 'openai') {
|
|
559
|
+
return this.fallbackToOpenAI(modelId, request);
|
|
560
|
+
}
|
|
561
|
+
else if (this.fallbackConfig?.provider === 'bedrock') {
|
|
562
|
+
return this.fallbackToBedrock(modelId, request);
|
|
563
|
+
}
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// ── AWS Bedrock Compatibility ──────────────────────────────────────
|
|
568
|
+
async bedrockInvoke(modelId, request) {
|
|
569
|
+
try {
|
|
570
|
+
const response = await this.generate(modelId, request.inputText, {
|
|
571
|
+
temperature: request.textGenerationConfig?.temperature,
|
|
572
|
+
maxTokens: request.textGenerationConfig?.maxTokenCount,
|
|
573
|
+
topP: request.textGenerationConfig?.topP,
|
|
574
|
+
});
|
|
575
|
+
// Estimate token counts
|
|
576
|
+
const inputTokens = Math.ceil(request.inputText.length / 4);
|
|
577
|
+
const outputTokens = Math.ceil(response.length / 4);
|
|
578
|
+
return {
|
|
579
|
+
results: [
|
|
580
|
+
{
|
|
581
|
+
outputText: response,
|
|
582
|
+
tokenCount: outputTokens,
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
input_text_token_count: inputTokens,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
// Fallback to cloud provider if configured
|
|
590
|
+
if (this.fallbackConfig?.provider === 'bedrock') {
|
|
591
|
+
return this.fallbackToBedrockCloud(modelId, request);
|
|
592
|
+
}
|
|
593
|
+
else if (this.fallbackConfig?.provider === 'openai') {
|
|
594
|
+
return this.fallbackToOpenAICloud(modelId, request);
|
|
595
|
+
}
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// ── Fallback: OpenAI Cloud ────────────────────────────────────────
|
|
600
|
+
async fallbackToOpenAI(modelId, request) {
|
|
601
|
+
if (!this.fallbackConfig) {
|
|
602
|
+
throw new Error('OpenAI fallback not configured');
|
|
603
|
+
}
|
|
604
|
+
const mappedModel = this.mapModelToOpenAI(modelId);
|
|
605
|
+
const payload = {
|
|
606
|
+
model: this.fallbackConfig.model || mappedModel,
|
|
607
|
+
messages: request.messages,
|
|
608
|
+
temperature: request.temperature,
|
|
609
|
+
max_tokens: request.max_tokens,
|
|
610
|
+
top_p: request.top_p,
|
|
611
|
+
frequency_penalty: request.frequency_penalty,
|
|
612
|
+
presence_penalty: request.presence_penalty,
|
|
613
|
+
stop: request.stop,
|
|
614
|
+
};
|
|
615
|
+
try {
|
|
616
|
+
const response = await axios.post('https://api.openai.com/v1/chat/completions', payload, {
|
|
617
|
+
headers: {
|
|
618
|
+
Authorization: `Bearer ${this.fallbackConfig.apiKey}`,
|
|
619
|
+
'Content-Type': 'application/json',
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
this.emitEvent('fallback_success', { provider: 'openai', originalModel: modelId, mappedModel: this.fallbackConfig.model });
|
|
623
|
+
return response.data;
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
this.emitProgress('error', 0, `OpenAI fallback failed: ${error.message}`);
|
|
627
|
+
this.emitEvent('fallback_error', { provider: 'openai', error: error.message });
|
|
628
|
+
throw error;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
async fallbackToBedrock(modelId, request) {
|
|
632
|
+
if (!this.fallbackConfig) {
|
|
633
|
+
throw new Error('Bedrock fallback not configured');
|
|
634
|
+
}
|
|
635
|
+
// Convert OpenAI format to Bedrock's expected format (simplified)
|
|
636
|
+
const lastMessage = request.messages[request.messages.length - 1];
|
|
637
|
+
const inputText = lastMessage.content;
|
|
638
|
+
const bedrockResponse = await this.invokeBedrockCloud(inputText, {
|
|
639
|
+
temperature: request.temperature,
|
|
640
|
+
maxTokenCount: request.max_tokens,
|
|
641
|
+
topP: request.top_p,
|
|
642
|
+
});
|
|
643
|
+
// Convert Bedrock response back to OpenAI format
|
|
644
|
+
const promptTokens = Math.ceil(inputText.length / 4);
|
|
645
|
+
const completionTokens = bedrockResponse.results[0].tokenCount;
|
|
646
|
+
this.emitEvent('fallback_success', { provider: 'bedrock', originalModel: modelId, mappedModel: this.fallbackConfig.model });
|
|
647
|
+
return {
|
|
648
|
+
id: `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
649
|
+
object: 'chat.completion',
|
|
650
|
+
created: Math.floor(Date.now() / 1000),
|
|
651
|
+
model: modelId,
|
|
652
|
+
choices: [
|
|
653
|
+
{
|
|
654
|
+
index: 0,
|
|
655
|
+
message: {
|
|
656
|
+
role: 'assistant',
|
|
657
|
+
content: bedrockResponse.results[0].outputText,
|
|
658
|
+
},
|
|
659
|
+
finish_reason: 'stop',
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
usage: {
|
|
663
|
+
prompt_tokens: promptTokens,
|
|
664
|
+
completion_tokens: completionTokens,
|
|
665
|
+
total_tokens: promptTokens + completionTokens,
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
async fallbackToOpenAICloud(modelId, request) {
|
|
670
|
+
if (!this.fallbackConfig) {
|
|
671
|
+
throw new Error('OpenAI fallback not configured');
|
|
672
|
+
}
|
|
673
|
+
const mappedModel = this.mapModelToOpenAI(modelId);
|
|
674
|
+
const payload = {
|
|
675
|
+
model: this.fallbackConfig.model || mappedModel,
|
|
676
|
+
messages: [{ role: 'user', content: request.inputText }],
|
|
677
|
+
temperature: request.textGenerationConfig?.temperature,
|
|
678
|
+
max_tokens: request.textGenerationConfig?.maxTokenCount,
|
|
679
|
+
top_p: request.textGenerationConfig?.topP,
|
|
680
|
+
};
|
|
681
|
+
try {
|
|
682
|
+
const response = await axios.post('https://api.openai.com/v1/chat/completions', payload, {
|
|
683
|
+
headers: {
|
|
684
|
+
Authorization: `Bearer ${this.fallbackConfig.apiKey}`,
|
|
685
|
+
'Content-Type': 'application/json',
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
const outputText = response.data.choices[0].message.content;
|
|
689
|
+
const inputTokens = Math.ceil(request.inputText.length / 4);
|
|
690
|
+
const outputTokens = response.data.usage.completion_tokens;
|
|
691
|
+
this.emitEvent('fallback_success', { provider: 'openai', originalModel: modelId, mappedModel: this.fallbackConfig.model });
|
|
692
|
+
return {
|
|
693
|
+
results: [
|
|
694
|
+
{
|
|
695
|
+
outputText,
|
|
696
|
+
tokenCount: outputTokens,
|
|
697
|
+
},
|
|
698
|
+
],
|
|
699
|
+
input_text_token_count: inputTokens,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
this.emitProgress('error', 0, `OpenAI fallback failed: ${error.message}`);
|
|
704
|
+
this.emitEvent('fallback_error', { provider: 'openai', error: error.message });
|
|
705
|
+
throw error;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async fallbackToBedrockCloud(modelId, request) {
|
|
709
|
+
if (!this.fallbackConfig) {
|
|
710
|
+
throw new Error('Bedrock fallback not configured');
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
return await this.invokeBedrockCloud(request.inputText, request.textGenerationConfig);
|
|
714
|
+
}
|
|
715
|
+
catch (error) {
|
|
716
|
+
this.emitProgress('error', 0, `Bedrock fallback failed: ${error.message}`);
|
|
717
|
+
this.emitEvent('fallback_error', { provider: 'bedrock', error: error.message });
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
async invokeBedrockCloud(inputText, config) {
|
|
722
|
+
if (!this.fallbackConfig) {
|
|
723
|
+
throw new Error('Bedrock fallback not configured');
|
|
724
|
+
}
|
|
725
|
+
const region = this.fallbackConfig.region || 'us-east-1';
|
|
726
|
+
const model = this.fallbackConfig.model || 'anthropic.claude-3-sonnet-20240229-v1:0';
|
|
727
|
+
// Bedrock endpoint format: https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/invoke
|
|
728
|
+
const endpoint = `https://bedrock-runtime.${region}.amazonaws.com/model/${model}/invoke`;
|
|
729
|
+
const payload = {
|
|
730
|
+
inputText,
|
|
731
|
+
textGenerationConfig: {
|
|
732
|
+
maxTokenCount: config?.maxTokenCount || 256,
|
|
733
|
+
temperature: config?.temperature || 0.7,
|
|
734
|
+
topP: config?.topP || 0.9,
|
|
735
|
+
topK: config?.topK,
|
|
736
|
+
stopSequences: config?.stopSequences,
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
try {
|
|
740
|
+
const response = await axios.post(endpoint, payload, {
|
|
741
|
+
headers: {
|
|
742
|
+
Authorization: `Bearer ${this.fallbackConfig.apiKey}`,
|
|
743
|
+
'Content-Type': 'application/json',
|
|
744
|
+
'X-Amz-Target': 'AmazonBedrockRuntime.InvokeModel',
|
|
745
|
+
},
|
|
746
|
+
});
|
|
747
|
+
this.emitEvent('fallback_success', { provider: 'bedrock', model });
|
|
748
|
+
return response.data;
|
|
749
|
+
}
|
|
750
|
+
catch (error) {
|
|
751
|
+
throw new Error(`Bedrock invocation failed: ${error.message}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
mapModelToOpenAI(slyModelId) {
|
|
755
|
+
const modelMapping = {
|
|
756
|
+
'quantum-1.7b': 'gpt-4o-mini',
|
|
757
|
+
'quantum-3b': 'gpt-4o',
|
|
758
|
+
'quantum-code-3b': 'gpt-4o',
|
|
759
|
+
'quantum-8b': 'gpt-4-turbo',
|
|
760
|
+
};
|
|
761
|
+
return modelMapping[slyModelId] || 'gpt-4o-mini';
|
|
762
|
+
}
|
|
763
|
+
// ── Static OpenAI Compatible Factory ────────────────────────────────
|
|
764
|
+
static openaiCompatible(config) {
|
|
765
|
+
const instance = new SlyOS({
|
|
766
|
+
apiKey: config.apiKey,
|
|
767
|
+
apiUrl: config.apiUrl,
|
|
768
|
+
fallback: { ...config.fallback, provider: config.fallback?.provider || 'openai' },
|
|
769
|
+
});
|
|
770
|
+
return {
|
|
771
|
+
chat: {
|
|
772
|
+
completions: {
|
|
773
|
+
async create(request) {
|
|
774
|
+
const { model, ...chatRequest } = request;
|
|
775
|
+
return instance.chatCompletion(model, chatRequest);
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
}
|
|
503
781
|
}
|
|
504
782
|
export default SlyOS;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -55,7 +55,7 @@ interface ProgressEvent {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
interface SlyEvent {
|
|
58
|
-
type: 'auth' | 'device_registered' | 'device_profiled' | 'model_download_start' | 'model_download_progress' | 'model_loaded' | 'inference_start' | 'inference_complete' | 'error';
|
|
58
|
+
type: 'auth' | 'device_registered' | 'device_profiled' | 'model_download_start' | 'model_download_progress' | 'model_loaded' | 'inference_start' | 'inference_complete' | 'error' | 'fallback_success' | 'fallback_error';
|
|
59
59
|
data?: any;
|
|
60
60
|
timestamp: number;
|
|
61
61
|
}
|
|
@@ -63,6 +63,94 @@ interface SlyEvent {
|
|
|
63
63
|
type ProgressCallback = (event: ProgressEvent) => void;
|
|
64
64
|
type EventCallback = (event: SlyEvent) => void;
|
|
65
65
|
|
|
66
|
+
// ─── OpenAI Compatibility Types ──────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
interface OpenAIMessage {
|
|
69
|
+
role: 'system' | 'user' | 'assistant';
|
|
70
|
+
content: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface OpenAIChatCompletionRequest {
|
|
74
|
+
messages: OpenAIMessage[];
|
|
75
|
+
temperature?: number;
|
|
76
|
+
top_p?: number;
|
|
77
|
+
max_tokens?: number;
|
|
78
|
+
frequency_penalty?: number;
|
|
79
|
+
presence_penalty?: number;
|
|
80
|
+
stop?: string | string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface OpenAIChoice {
|
|
84
|
+
index: number;
|
|
85
|
+
message: OpenAIMessage;
|
|
86
|
+
finish_reason: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface OpenAIUsage {
|
|
90
|
+
prompt_tokens: number;
|
|
91
|
+
completion_tokens: number;
|
|
92
|
+
total_tokens: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface OpenAIChatCompletionResponse {
|
|
96
|
+
id: string;
|
|
97
|
+
object: 'chat.completion';
|
|
98
|
+
created: number;
|
|
99
|
+
model: string;
|
|
100
|
+
choices: OpenAIChoice[];
|
|
101
|
+
usage: OpenAIUsage;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── AWS Bedrock Compatibility Types ─────────────────────────────────
|
|
105
|
+
|
|
106
|
+
interface BedrockTextGenerationConfig {
|
|
107
|
+
maxTokenCount?: number;
|
|
108
|
+
temperature?: number;
|
|
109
|
+
topP?: number;
|
|
110
|
+
topK?: number;
|
|
111
|
+
stopSequences?: string[];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface BedrockInvokeRequest {
|
|
115
|
+
inputText: string;
|
|
116
|
+
textGenerationConfig?: BedrockTextGenerationConfig;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface BedrockResult {
|
|
120
|
+
outputText: string;
|
|
121
|
+
tokenCount: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface BedrockInvokeResponse {
|
|
125
|
+
results: BedrockResult[];
|
|
126
|
+
input_text_token_count?: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ─── Fallback Configuration ─────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
type FallbackProvider = 'openai' | 'bedrock';
|
|
132
|
+
|
|
133
|
+
interface FallbackConfig {
|
|
134
|
+
provider: FallbackProvider;
|
|
135
|
+
apiKey: string;
|
|
136
|
+
model: string;
|
|
137
|
+
region?: string; // for Bedrock
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface SlyOSConfigWithFallback extends SlyOSConfig {
|
|
141
|
+
fallback?: FallbackConfig;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── OpenAI Compatible Client ───────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
interface OpenAICompatibleClient {
|
|
147
|
+
chat: {
|
|
148
|
+
completions: {
|
|
149
|
+
create(request: OpenAIChatCompletionRequest & { model: string }): Promise<OpenAIChatCompletionResponse>;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
66
154
|
// ─── Model Registry ─────────────────────────────────────────────────
|
|
67
155
|
|
|
68
156
|
const modelMap: Record<string, ModelInfo> = {
|
|
@@ -218,13 +306,15 @@ class SlyOS {
|
|
|
218
306
|
private deviceProfile: DeviceProfile | null = null;
|
|
219
307
|
private onProgress: ProgressCallback | null;
|
|
220
308
|
private onEvent: EventCallback | null;
|
|
309
|
+
private fallbackConfig: FallbackConfig | null;
|
|
221
310
|
|
|
222
|
-
constructor(config:
|
|
311
|
+
constructor(config: SlyOSConfigWithFallback) {
|
|
223
312
|
this.apiKey = config.apiKey;
|
|
224
313
|
this.apiUrl = config.apiUrl || 'https://api.slyos.world';
|
|
225
314
|
this.deviceId = `device-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
226
315
|
this.onProgress = config.onProgress || null;
|
|
227
316
|
this.onEvent = config.onEvent || null;
|
|
317
|
+
this.fallbackConfig = config.fallback || null;
|
|
228
318
|
}
|
|
229
319
|
|
|
230
320
|
// ── Progress & Event Helpers ────────────────────────────────────
|
|
@@ -525,7 +615,12 @@ class SlyOS {
|
|
|
525
615
|
do_sample: true,
|
|
526
616
|
});
|
|
527
617
|
|
|
528
|
-
const
|
|
618
|
+
const rawOutput = result[0].generated_text;
|
|
619
|
+
// HuggingFace transformers returns the prompt + generated text concatenated.
|
|
620
|
+
// Strip the original prompt so we only return the NEW tokens.
|
|
621
|
+
const response = rawOutput.startsWith(prompt)
|
|
622
|
+
? rawOutput.slice(prompt.length).trim()
|
|
623
|
+
: rawOutput.trim();
|
|
529
624
|
const latency = Date.now() - startTime;
|
|
530
625
|
const tokensGenerated = response.split(/\s+/).length;
|
|
531
626
|
const tokensPerSec = (tokensGenerated / (latency / 1000)).toFixed(1);
|
|
@@ -625,7 +720,329 @@ class SlyOS {
|
|
|
625
720
|
throw error;
|
|
626
721
|
}
|
|
627
722
|
}
|
|
723
|
+
|
|
724
|
+
// ── OpenAI Compatibility ────────────────────────────────────────────
|
|
725
|
+
|
|
726
|
+
async chatCompletion(modelId: string, request: OpenAIChatCompletionRequest): Promise<OpenAIChatCompletionResponse> {
|
|
727
|
+
try {
|
|
728
|
+
// Convert OpenAI message format to a prompt string
|
|
729
|
+
const prompt = request.messages
|
|
730
|
+
.map(msg => {
|
|
731
|
+
if (msg.role === 'system') {
|
|
732
|
+
return `System: ${msg.content}`;
|
|
733
|
+
} else if (msg.role === 'user') {
|
|
734
|
+
return `User: ${msg.content}`;
|
|
735
|
+
} else {
|
|
736
|
+
return `Assistant: ${msg.content}`;
|
|
737
|
+
}
|
|
738
|
+
})
|
|
739
|
+
.join('\n\n');
|
|
740
|
+
|
|
741
|
+
const response = await this.generate(modelId, prompt, {
|
|
742
|
+
temperature: request.temperature,
|
|
743
|
+
maxTokens: request.max_tokens,
|
|
744
|
+
topP: request.top_p,
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
// Estimate token counts (rough approximation: ~4 chars per token)
|
|
748
|
+
const promptTokens = Math.ceil(prompt.length / 4);
|
|
749
|
+
const completionTokens = Math.ceil(response.length / 4);
|
|
750
|
+
|
|
751
|
+
return {
|
|
752
|
+
id: `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
753
|
+
object: 'chat.completion',
|
|
754
|
+
created: Math.floor(Date.now() / 1000),
|
|
755
|
+
model: modelId,
|
|
756
|
+
choices: [
|
|
757
|
+
{
|
|
758
|
+
index: 0,
|
|
759
|
+
message: {
|
|
760
|
+
role: 'assistant',
|
|
761
|
+
content: response,
|
|
762
|
+
},
|
|
763
|
+
finish_reason: 'stop',
|
|
764
|
+
},
|
|
765
|
+
],
|
|
766
|
+
usage: {
|
|
767
|
+
prompt_tokens: promptTokens,
|
|
768
|
+
completion_tokens: completionTokens,
|
|
769
|
+
total_tokens: promptTokens + completionTokens,
|
|
770
|
+
},
|
|
771
|
+
};
|
|
772
|
+
} catch (error: any) {
|
|
773
|
+
// Fallback to cloud provider if configured
|
|
774
|
+
if (this.fallbackConfig?.provider === 'openai') {
|
|
775
|
+
return this.fallbackToOpenAI(modelId, request);
|
|
776
|
+
} else if (this.fallbackConfig?.provider === 'bedrock') {
|
|
777
|
+
return this.fallbackToBedrock(modelId, request);
|
|
778
|
+
}
|
|
779
|
+
throw error;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// ── AWS Bedrock Compatibility ──────────────────────────────────────
|
|
784
|
+
|
|
785
|
+
async bedrockInvoke(modelId: string, request: BedrockInvokeRequest): Promise<BedrockInvokeResponse> {
|
|
786
|
+
try {
|
|
787
|
+
const response = await this.generate(modelId, request.inputText, {
|
|
788
|
+
temperature: request.textGenerationConfig?.temperature,
|
|
789
|
+
maxTokens: request.textGenerationConfig?.maxTokenCount,
|
|
790
|
+
topP: request.textGenerationConfig?.topP,
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Estimate token counts
|
|
794
|
+
const inputTokens = Math.ceil(request.inputText.length / 4);
|
|
795
|
+
const outputTokens = Math.ceil(response.length / 4);
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
results: [
|
|
799
|
+
{
|
|
800
|
+
outputText: response,
|
|
801
|
+
tokenCount: outputTokens,
|
|
802
|
+
},
|
|
803
|
+
],
|
|
804
|
+
input_text_token_count: inputTokens,
|
|
805
|
+
};
|
|
806
|
+
} catch (error: any) {
|
|
807
|
+
// Fallback to cloud provider if configured
|
|
808
|
+
if (this.fallbackConfig?.provider === 'bedrock') {
|
|
809
|
+
return this.fallbackToBedrockCloud(modelId, request);
|
|
810
|
+
} else if (this.fallbackConfig?.provider === 'openai') {
|
|
811
|
+
return this.fallbackToOpenAICloud(modelId, request);
|
|
812
|
+
}
|
|
813
|
+
throw error;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// ── Fallback: OpenAI Cloud ────────────────────────────────────────
|
|
818
|
+
|
|
819
|
+
private async fallbackToOpenAI(modelId: string, request: OpenAIChatCompletionRequest): Promise<OpenAIChatCompletionResponse> {
|
|
820
|
+
if (!this.fallbackConfig) {
|
|
821
|
+
throw new Error('OpenAI fallback not configured');
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const mappedModel = this.mapModelToOpenAI(modelId);
|
|
825
|
+
const payload = {
|
|
826
|
+
model: this.fallbackConfig.model || mappedModel,
|
|
827
|
+
messages: request.messages,
|
|
828
|
+
temperature: request.temperature,
|
|
829
|
+
max_tokens: request.max_tokens,
|
|
830
|
+
top_p: request.top_p,
|
|
831
|
+
frequency_penalty: request.frequency_penalty,
|
|
832
|
+
presence_penalty: request.presence_penalty,
|
|
833
|
+
stop: request.stop,
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
try {
|
|
837
|
+
const response = await axios.post('https://api.openai.com/v1/chat/completions', payload, {
|
|
838
|
+
headers: {
|
|
839
|
+
Authorization: `Bearer ${this.fallbackConfig.apiKey}`,
|
|
840
|
+
'Content-Type': 'application/json',
|
|
841
|
+
},
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
this.emitEvent('fallback_success', { provider: 'openai', originalModel: modelId, mappedModel: this.fallbackConfig.model });
|
|
845
|
+
return response.data;
|
|
846
|
+
} catch (error: any) {
|
|
847
|
+
this.emitProgress('error', 0, `OpenAI fallback failed: ${error.message}`);
|
|
848
|
+
this.emitEvent('fallback_error', { provider: 'openai', error: error.message });
|
|
849
|
+
throw error;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
private async fallbackToBedrock(modelId: string, request: OpenAIChatCompletionRequest): Promise<OpenAIChatCompletionResponse> {
|
|
854
|
+
if (!this.fallbackConfig) {
|
|
855
|
+
throw new Error('Bedrock fallback not configured');
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Convert OpenAI format to Bedrock's expected format (simplified)
|
|
859
|
+
const lastMessage = request.messages[request.messages.length - 1];
|
|
860
|
+
const inputText = lastMessage.content;
|
|
861
|
+
|
|
862
|
+
const bedrockResponse = await this.invokeBedrockCloud(inputText, {
|
|
863
|
+
temperature: request.temperature,
|
|
864
|
+
maxTokenCount: request.max_tokens,
|
|
865
|
+
topP: request.top_p,
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// Convert Bedrock response back to OpenAI format
|
|
869
|
+
const promptTokens = Math.ceil(inputText.length / 4);
|
|
870
|
+
const completionTokens = bedrockResponse.results[0].tokenCount;
|
|
871
|
+
|
|
872
|
+
this.emitEvent('fallback_success', { provider: 'bedrock', originalModel: modelId, mappedModel: this.fallbackConfig.model });
|
|
873
|
+
|
|
874
|
+
return {
|
|
875
|
+
id: `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
876
|
+
object: 'chat.completion',
|
|
877
|
+
created: Math.floor(Date.now() / 1000),
|
|
878
|
+
model: modelId,
|
|
879
|
+
choices: [
|
|
880
|
+
{
|
|
881
|
+
index: 0,
|
|
882
|
+
message: {
|
|
883
|
+
role: 'assistant',
|
|
884
|
+
content: bedrockResponse.results[0].outputText,
|
|
885
|
+
},
|
|
886
|
+
finish_reason: 'stop',
|
|
887
|
+
},
|
|
888
|
+
],
|
|
889
|
+
usage: {
|
|
890
|
+
prompt_tokens: promptTokens,
|
|
891
|
+
completion_tokens: completionTokens,
|
|
892
|
+
total_tokens: promptTokens + completionTokens,
|
|
893
|
+
},
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
private async fallbackToOpenAICloud(modelId: string, request: BedrockInvokeRequest): Promise<BedrockInvokeResponse> {
|
|
898
|
+
if (!this.fallbackConfig) {
|
|
899
|
+
throw new Error('OpenAI fallback not configured');
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const mappedModel = this.mapModelToOpenAI(modelId);
|
|
903
|
+
const payload = {
|
|
904
|
+
model: this.fallbackConfig.model || mappedModel,
|
|
905
|
+
messages: [{ role: 'user', content: request.inputText }],
|
|
906
|
+
temperature: request.textGenerationConfig?.temperature,
|
|
907
|
+
max_tokens: request.textGenerationConfig?.maxTokenCount,
|
|
908
|
+
top_p: request.textGenerationConfig?.topP,
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
try {
|
|
912
|
+
const response = await axios.post('https://api.openai.com/v1/chat/completions', payload, {
|
|
913
|
+
headers: {
|
|
914
|
+
Authorization: `Bearer ${this.fallbackConfig.apiKey}`,
|
|
915
|
+
'Content-Type': 'application/json',
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
const outputText = response.data.choices[0].message.content;
|
|
920
|
+
const inputTokens = Math.ceil(request.inputText.length / 4);
|
|
921
|
+
const outputTokens = response.data.usage.completion_tokens;
|
|
922
|
+
|
|
923
|
+
this.emitEvent('fallback_success', { provider: 'openai', originalModel: modelId, mappedModel: this.fallbackConfig.model });
|
|
924
|
+
|
|
925
|
+
return {
|
|
926
|
+
results: [
|
|
927
|
+
{
|
|
928
|
+
outputText,
|
|
929
|
+
tokenCount: outputTokens,
|
|
930
|
+
},
|
|
931
|
+
],
|
|
932
|
+
input_text_token_count: inputTokens,
|
|
933
|
+
};
|
|
934
|
+
} catch (error: any) {
|
|
935
|
+
this.emitProgress('error', 0, `OpenAI fallback failed: ${error.message}`);
|
|
936
|
+
this.emitEvent('fallback_error', { provider: 'openai', error: error.message });
|
|
937
|
+
throw error;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
private async fallbackToBedrockCloud(modelId: string, request: BedrockInvokeRequest): Promise<BedrockInvokeResponse> {
|
|
942
|
+
if (!this.fallbackConfig) {
|
|
943
|
+
throw new Error('Bedrock fallback not configured');
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
try {
|
|
947
|
+
return await this.invokeBedrockCloud(request.inputText, request.textGenerationConfig);
|
|
948
|
+
} catch (error: any) {
|
|
949
|
+
this.emitProgress('error', 0, `Bedrock fallback failed: ${error.message}`);
|
|
950
|
+
this.emitEvent('fallback_error', { provider: 'bedrock', error: error.message });
|
|
951
|
+
throw error;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
private async invokeBedrockCloud(inputText: string, config?: BedrockTextGenerationConfig): Promise<BedrockInvokeResponse> {
|
|
956
|
+
if (!this.fallbackConfig) {
|
|
957
|
+
throw new Error('Bedrock fallback not configured');
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
const region = this.fallbackConfig.region || 'us-east-1';
|
|
961
|
+
const model = this.fallbackConfig.model || 'anthropic.claude-3-sonnet-20240229-v1:0';
|
|
962
|
+
|
|
963
|
+
// Bedrock endpoint format: https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/invoke
|
|
964
|
+
const endpoint = `https://bedrock-runtime.${region}.amazonaws.com/model/${model}/invoke`;
|
|
965
|
+
|
|
966
|
+
const payload = {
|
|
967
|
+
inputText,
|
|
968
|
+
textGenerationConfig: {
|
|
969
|
+
maxTokenCount: config?.maxTokenCount || 256,
|
|
970
|
+
temperature: config?.temperature || 0.7,
|
|
971
|
+
topP: config?.topP || 0.9,
|
|
972
|
+
topK: config?.topK,
|
|
973
|
+
stopSequences: config?.stopSequences,
|
|
974
|
+
},
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
try {
|
|
978
|
+
const response = await axios.post(endpoint, payload, {
|
|
979
|
+
headers: {
|
|
980
|
+
Authorization: `Bearer ${this.fallbackConfig.apiKey}`,
|
|
981
|
+
'Content-Type': 'application/json',
|
|
982
|
+
'X-Amz-Target': 'AmazonBedrockRuntime.InvokeModel',
|
|
983
|
+
},
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
this.emitEvent('fallback_success', { provider: 'bedrock', model });
|
|
987
|
+
return response.data;
|
|
988
|
+
} catch (error: any) {
|
|
989
|
+
throw new Error(`Bedrock invocation failed: ${error.message}`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
private mapModelToOpenAI(slyModelId: string): string {
|
|
994
|
+
const modelMapping: Record<string, string> = {
|
|
995
|
+
'quantum-1.7b': 'gpt-4o-mini',
|
|
996
|
+
'quantum-3b': 'gpt-4o',
|
|
997
|
+
'quantum-code-3b': 'gpt-4o',
|
|
998
|
+
'quantum-8b': 'gpt-4-turbo',
|
|
999
|
+
};
|
|
1000
|
+
return modelMapping[slyModelId] || 'gpt-4o-mini';
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// ── Static OpenAI Compatible Factory ────────────────────────────────
|
|
1004
|
+
|
|
1005
|
+
static openaiCompatible(config: { apiKey: string; apiUrl?: string; fallback?: FallbackConfig }): OpenAICompatibleClient {
|
|
1006
|
+
const instance = new SlyOS({
|
|
1007
|
+
apiKey: config.apiKey,
|
|
1008
|
+
apiUrl: config.apiUrl,
|
|
1009
|
+
fallback: { ...config.fallback, provider: config.fallback?.provider || 'openai' } as FallbackConfig,
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
return {
|
|
1013
|
+
chat: {
|
|
1014
|
+
completions: {
|
|
1015
|
+
async create(request: OpenAIChatCompletionRequest & { model: string }): Promise<OpenAIChatCompletionResponse> {
|
|
1016
|
+
const { model, ...chatRequest } = request;
|
|
1017
|
+
return instance.chatCompletion(model, chatRequest);
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
628
1023
|
}
|
|
629
1024
|
|
|
630
1025
|
export default SlyOS;
|
|
631
|
-
export type {
|
|
1026
|
+
export type {
|
|
1027
|
+
SlyOSConfig,
|
|
1028
|
+
SlyOSConfigWithFallback,
|
|
1029
|
+
GenerateOptions,
|
|
1030
|
+
TranscribeOptions,
|
|
1031
|
+
DeviceProfile,
|
|
1032
|
+
ProgressEvent,
|
|
1033
|
+
SlyEvent,
|
|
1034
|
+
QuantizationLevel,
|
|
1035
|
+
ModelCategory,
|
|
1036
|
+
OpenAIMessage,
|
|
1037
|
+
OpenAIChatCompletionRequest,
|
|
1038
|
+
OpenAIChatCompletionResponse,
|
|
1039
|
+
OpenAIChoice,
|
|
1040
|
+
OpenAIUsage,
|
|
1041
|
+
BedrockTextGenerationConfig,
|
|
1042
|
+
BedrockInvokeRequest,
|
|
1043
|
+
BedrockInvokeResponse,
|
|
1044
|
+
BedrockResult,
|
|
1045
|
+
FallbackConfig,
|
|
1046
|
+
FallbackProvider,
|
|
1047
|
+
OpenAICompatibleClient,
|
|
1048
|
+
};
|