valenceai 0.5.1 → 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 +357 -177
- package/package.json +15 -6
- package/src/config.js +25 -14
- package/src/rateLimit.js +77 -0
- package/src/streaming.js +193 -0
- package/src/utils/logger.js +3 -3
- package/src/valenceClient.js +173 -68
- package/tests/asyncAudio.test.js +128 -71
- package/tests/client.test.js +10 -25
- package/tests/config.test.js +21 -21
- package/tests/e2e.asyncWorkflow.test.js +343 -0
- package/tests/e2e.streaming.test.js +420 -0
- package/tests/logger.test.js +3 -0
- package/tests/rateLimit.test.js +137 -0
- package/tests/setup.js +5 -4
- package/tests/streaming.test.js +187 -0
- package/tests/valenceClient.test.js +50 -5
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { describe, test, expect, jest, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import { ValenceClient } from '../src/valenceClient.js';
|
|
3
|
+
|
|
4
|
+
// Mock socket.io-client
|
|
5
|
+
const mockSocket = {
|
|
6
|
+
connect: jest.fn(),
|
|
7
|
+
emit: jest.fn(),
|
|
8
|
+
on: jest.fn(),
|
|
9
|
+
disconnect: jest.fn(),
|
|
10
|
+
connected: false
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
jest.unstable_mockModule('socket.io-client', () => ({
|
|
14
|
+
io: jest.fn(() => mockSocket)
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe('Streaming API', () => {
|
|
18
|
+
const originalEnv = process.env;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
process.env = { ...originalEnv };
|
|
22
|
+
process.env.VALENCE_API_KEY = 'test-api-key';
|
|
23
|
+
process.env.VALENCE_WEBSOCKET_URL = 'wss://test-api.com';
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
process.env = originalEnv;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should have streaming property', () => {
|
|
32
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
33
|
+
expect(client).toHaveProperty('streaming');
|
|
34
|
+
expect(typeof client.streaming.connect).toBe('function');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should create StreamConnection with default model', () => {
|
|
38
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
39
|
+
const stream = client.streaming.connect();
|
|
40
|
+
|
|
41
|
+
expect(stream).toBeDefined();
|
|
42
|
+
expect(stream.model).toBe('4emotions');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('should create StreamConnection with custom model', () => {
|
|
46
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
47
|
+
const stream = client.streaming.connect('7emotions');
|
|
48
|
+
|
|
49
|
+
expect(stream).toBeDefined();
|
|
50
|
+
expect(stream.model).toBe('7emotions');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should throw error for invalid model', () => {
|
|
54
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
55
|
+
|
|
56
|
+
expect(() => {
|
|
57
|
+
client.streaming.connect('invalid_model');
|
|
58
|
+
}).toThrow('Invalid model');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should use custom WebSocket URL', () => {
|
|
62
|
+
const client = new ValenceClient({
|
|
63
|
+
apiKey: 'test-key',
|
|
64
|
+
websocketUrl: 'wss://custom.example.com'
|
|
65
|
+
});
|
|
66
|
+
const stream = client.streaming.connect();
|
|
67
|
+
|
|
68
|
+
expect(stream.websocketUrl).toBe('wss://custom.example.com');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('StreamConnection should have required methods', () => {
|
|
72
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
73
|
+
const stream = client.streaming.connect();
|
|
74
|
+
|
|
75
|
+
expect(typeof stream.connect).toBe('function');
|
|
76
|
+
expect(typeof stream.sendAudio).toBe('function');
|
|
77
|
+
expect(typeof stream.disconnect).toBe('function');
|
|
78
|
+
expect(typeof stream.onPrediction).toBe('function');
|
|
79
|
+
expect(typeof stream.onError).toBe('function');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should register prediction callback', () => {
|
|
83
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
84
|
+
const stream = client.streaming.connect();
|
|
85
|
+
|
|
86
|
+
const callback = jest.fn();
|
|
87
|
+
stream.onPrediction(callback);
|
|
88
|
+
|
|
89
|
+
expect(stream.predictionCallback).toBe(callback);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('should register error callback', () => {
|
|
93
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
94
|
+
const stream = client.streaming.connect();
|
|
95
|
+
|
|
96
|
+
const callback = jest.fn();
|
|
97
|
+
stream.onError(callback);
|
|
98
|
+
|
|
99
|
+
expect(stream.errorCallback).toBe(callback);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should register connect callback', () => {
|
|
103
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
104
|
+
const stream = client.streaming.connect();
|
|
105
|
+
|
|
106
|
+
const callback = jest.fn();
|
|
107
|
+
stream.onConnect(callback);
|
|
108
|
+
|
|
109
|
+
expect(stream.connectCallback).toBe(callback);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('should register disconnect callback', () => {
|
|
113
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
114
|
+
const stream = client.streaming.connect();
|
|
115
|
+
|
|
116
|
+
const callback = jest.fn();
|
|
117
|
+
stream.onDisconnect(callback);
|
|
118
|
+
|
|
119
|
+
expect(stream.disconnectCallback).toBe(callback);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('should encode audio data to base64', () => {
|
|
123
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
124
|
+
const stream = client.streaming.connect();
|
|
125
|
+
|
|
126
|
+
const audioBuffer = Buffer.from([0x00, 0x01, 0x02, 0x03]);
|
|
127
|
+
const base64 = stream._encodeAudio(audioBuffer);
|
|
128
|
+
|
|
129
|
+
expect(typeof base64).toBe('string');
|
|
130
|
+
expect(base64).toBe(audioBuffer.toString('base64'));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('should handle connection with API key', () => {
|
|
134
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
135
|
+
const stream = client.streaming.connect();
|
|
136
|
+
|
|
137
|
+
expect(stream.apiKey).toBe('test-api-key');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should use default WebSocket URL when not specified', () => {
|
|
141
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
142
|
+
const stream = client.streaming.connect();
|
|
143
|
+
|
|
144
|
+
// Should use the default staging URL
|
|
145
|
+
expect(stream.websocketUrl).toBe('wss://demo.getvalenceai.com');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('should validate model parameter', () => {
|
|
149
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
150
|
+
|
|
151
|
+
// Valid models
|
|
152
|
+
expect(() => client.streaming.connect('4emotions')).not.toThrow();
|
|
153
|
+
expect(() => client.streaming.connect('7emotions')).not.toThrow();
|
|
154
|
+
|
|
155
|
+
// Invalid model
|
|
156
|
+
expect(() => client.streaming.connect('invalid')).toThrow('Invalid model');
|
|
157
|
+
expect(() => client.streaming.connect('')).toThrow('Invalid model');
|
|
158
|
+
expect(() => client.streaming.connect(null)).toThrow('Invalid model');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('should throw error if sendAudio called before connect', () => {
|
|
162
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
163
|
+
const stream = client.streaming.connect();
|
|
164
|
+
|
|
165
|
+
const audioData = Buffer.from([0x00, 0x01]);
|
|
166
|
+
|
|
167
|
+
expect(() => {
|
|
168
|
+
stream.sendAudio(audioData);
|
|
169
|
+
}).toThrow('Not connected');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('should validate audio data in sendAudio', () => {
|
|
173
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
174
|
+
const stream = client.streaming.connect();
|
|
175
|
+
|
|
176
|
+
// Mock connection
|
|
177
|
+
stream.sio = { connected: true, emit: jest.fn() };
|
|
178
|
+
|
|
179
|
+
expect(() => {
|
|
180
|
+
stream.sendAudio(null);
|
|
181
|
+
}).toThrow('Audio data is required');
|
|
182
|
+
|
|
183
|
+
expect(() => {
|
|
184
|
+
stream.sendAudio(undefined);
|
|
185
|
+
}).toThrow('Audio data is required');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -2,20 +2,65 @@ import { describe, test, expect } from '@jest/globals';
|
|
|
2
2
|
import { ValenceClient } from '../src/valenceClient.js';
|
|
3
3
|
|
|
4
4
|
describe('ValenceClient', () => {
|
|
5
|
-
test('should create client with nested
|
|
6
|
-
const client = new ValenceClient();
|
|
7
|
-
|
|
5
|
+
test('should create client with all nested API properties', () => {
|
|
6
|
+
const client = new ValenceClient({ apiKey: 'test_key' });
|
|
7
|
+
|
|
8
|
+
// Check all four API clients
|
|
8
9
|
expect(client).toHaveProperty('discrete');
|
|
9
10
|
expect(client).toHaveProperty('asynch');
|
|
11
|
+
expect(client).toHaveProperty('streaming');
|
|
12
|
+
expect(client).toHaveProperty('rateLimit');
|
|
13
|
+
|
|
14
|
+
// Check discrete methods
|
|
10
15
|
expect(typeof client.discrete.emotions).toBe('function');
|
|
16
|
+
|
|
17
|
+
// Check asynch methods
|
|
11
18
|
expect(typeof client.asynch.upload).toBe('function');
|
|
12
19
|
expect(typeof client.asynch.emotions).toBe('function');
|
|
20
|
+
expect(typeof client.asynch.getTimeline).toBe('function');
|
|
21
|
+
expect(typeof client.asynch.getEmotionAtTime).toBe('function');
|
|
22
|
+
expect(typeof client.asynch.getDominantEmotion).toBe('function');
|
|
23
|
+
|
|
24
|
+
// Check streaming methods
|
|
25
|
+
expect(typeof client.streaming.connect).toBe('function');
|
|
26
|
+
|
|
27
|
+
// Check rate limit methods
|
|
28
|
+
expect(typeof client.rateLimit.getStatus).toBe('function');
|
|
29
|
+
expect(typeof client.rateLimit.getHealth).toBe('function');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should use default staging URLs', () => {
|
|
33
|
+
const client = new ValenceClient({ apiKey: 'test_key' });
|
|
34
|
+
|
|
35
|
+
expect(client.config.baseUrl).toBe('https://demo.getvalenceai.com');
|
|
36
|
+
expect(client.config.websocketUrl).toBe('wss://demo.getvalenceai.com');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('should accept custom URLs via constructor', () => {
|
|
40
|
+
const client = new ValenceClient({
|
|
41
|
+
apiKey: 'test_key',
|
|
42
|
+
baseUrl: 'https://custom.example.com',
|
|
43
|
+
websocketUrl: 'wss://custom.example.com'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(client.config.baseUrl).toBe('https://custom.example.com');
|
|
47
|
+
expect(client.config.websocketUrl).toBe('wss://custom.example.com');
|
|
13
48
|
});
|
|
14
49
|
|
|
15
50
|
test('should pass constructor parameters to AsyncClient', () => {
|
|
16
|
-
const client = new ValenceClient(
|
|
17
|
-
|
|
51
|
+
const client = new ValenceClient({
|
|
52
|
+
apiKey: 'test_key',
|
|
53
|
+
partSize: 1024,
|
|
54
|
+
maxRetries: 5
|
|
55
|
+
});
|
|
56
|
+
|
|
18
57
|
expect(client.asynch.partSize).toBe(1024);
|
|
19
58
|
expect(client.asynch.maxRetries).toBe(5);
|
|
20
59
|
});
|
|
60
|
+
|
|
61
|
+
test('should throw error when API key is missing', () => {
|
|
62
|
+
expect(() => {
|
|
63
|
+
new ValenceClient({ apiKey: '' });
|
|
64
|
+
}).toThrow('API key not provided and not set in environment (VALENCE_API_KEY)');
|
|
65
|
+
});
|
|
21
66
|
});
|