waterlight 0.1.0 → 0.2.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 +4 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.js +62 -30
- package/dist/errors.d.ts +7 -5
- package/dist/errors.js +11 -9
- package/dist/streaming.d.ts +2 -1
- package/dist/streaming.js +25 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,6 +145,8 @@ try {
|
|
|
145
145
|
|-----------|---------|---------|
|
|
146
146
|
| `apiKey` | `WATERLIGHT_API_KEY` | — (required) |
|
|
147
147
|
| `baseUrl` | `WATERLIGHT_BASE_URL` | `https://api.waterlight.ai` |
|
|
148
|
+
| `timeout` | — | `120000` (ms) |
|
|
149
|
+
| `maxRetries` | — | `2` |
|
|
148
150
|
|
|
149
151
|
```typescript
|
|
150
152
|
// Using env var
|
|
@@ -155,6 +157,8 @@ const client = new Waterlight(); // picks up from env
|
|
|
155
157
|
const client = new Waterlight({
|
|
156
158
|
apiKey: 'wl-...',
|
|
157
159
|
baseUrl: 'https://custom.endpoint.com',
|
|
160
|
+
timeout: 60_000, // 60s timeout
|
|
161
|
+
maxRetries: 3, // retry 429/5xx up to 3 times
|
|
158
162
|
});
|
|
159
163
|
```
|
|
160
164
|
|
package/dist/client.d.ts
CHANGED
|
@@ -89,6 +89,8 @@ declare class Billing {
|
|
|
89
89
|
export declare class Waterlight {
|
|
90
90
|
readonly apiKey: string;
|
|
91
91
|
readonly baseUrl: string;
|
|
92
|
+
readonly timeout: number;
|
|
93
|
+
readonly maxRetries: number;
|
|
92
94
|
readonly chat: Chat;
|
|
93
95
|
readonly embeddings: Embeddings;
|
|
94
96
|
readonly models: Models;
|
|
@@ -96,8 +98,11 @@ export declare class Waterlight {
|
|
|
96
98
|
constructor(opts?: {
|
|
97
99
|
apiKey?: string;
|
|
98
100
|
baseUrl?: string;
|
|
101
|
+
timeout?: number;
|
|
102
|
+
maxRetries?: number;
|
|
99
103
|
});
|
|
100
104
|
private _post;
|
|
101
105
|
private _get;
|
|
106
|
+
private _request;
|
|
102
107
|
}
|
|
103
108
|
export {};
|
package/dist/client.js
CHANGED
|
@@ -4,16 +4,26 @@ exports.Waterlight = void 0;
|
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
const streaming_1 = require("./streaming");
|
|
6
6
|
const DEFAULT_BASE_URL = 'https://api.waterlight.ai';
|
|
7
|
-
|
|
7
|
+
const DEFAULT_TIMEOUT = 120000;
|
|
8
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
9
|
+
const RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);
|
|
10
|
+
function sleep(ms) {
|
|
11
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
function handleError(status, body, headers) {
|
|
8
14
|
const errObj = body?.error;
|
|
9
15
|
const msg = (typeof errObj === 'object' ? errObj?.message : errObj) ?? 'Request failed';
|
|
16
|
+
const requestId = headers?.get('x-request-id') ?? undefined;
|
|
10
17
|
if (status === 401)
|
|
11
|
-
throw new errors_1.AuthenticationError(msg);
|
|
12
|
-
if (status === 429)
|
|
13
|
-
|
|
18
|
+
throw new errors_1.AuthenticationError(msg, requestId);
|
|
19
|
+
if (status === 429) {
|
|
20
|
+
const ra = headers?.get('retry-after');
|
|
21
|
+
const retryAfter = ra ? parseFloat(ra) : undefined;
|
|
22
|
+
throw new errors_1.RateLimitError(msg, retryAfter, requestId);
|
|
23
|
+
}
|
|
14
24
|
if (status === 402)
|
|
15
|
-
throw new errors_1.InsufficientCreditsError(msg);
|
|
16
|
-
throw new errors_1.APIError(msg, status);
|
|
25
|
+
throw new errors_1.InsufficientCreditsError(msg, requestId);
|
|
26
|
+
throw new errors_1.APIError(msg, status, requestId);
|
|
17
27
|
}
|
|
18
28
|
/** Chat completions namespace. */
|
|
19
29
|
class Completions {
|
|
@@ -22,7 +32,7 @@ class Completions {
|
|
|
22
32
|
}
|
|
23
33
|
create(params) {
|
|
24
34
|
if (params.stream) {
|
|
25
|
-
return new streaming_1.Stream(`${this.client.baseUrl}/v1/chat/completions`, this.client.apiKey, params);
|
|
35
|
+
return new streaming_1.Stream(`${this.client.baseUrl}/v1/chat/completions`, this.client.apiKey, params, this.client.timeout);
|
|
26
36
|
}
|
|
27
37
|
return this.client['_post']('/v1/chat/completions', { ...params, stream: false });
|
|
28
38
|
}
|
|
@@ -96,37 +106,59 @@ class Waterlight {
|
|
|
96
106
|
}
|
|
97
107
|
this.apiKey = key;
|
|
98
108
|
this.baseUrl = (opts.baseUrl ?? process.env.WATERLIGHT_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
109
|
+
this.timeout = opts.timeout ?? DEFAULT_TIMEOUT;
|
|
110
|
+
this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
99
111
|
this.chat = new Chat(this);
|
|
100
112
|
this.embeddings = new Embeddings(this);
|
|
101
113
|
this.models = new Models(this);
|
|
102
114
|
this.billing = new Billing(this);
|
|
103
115
|
}
|
|
104
116
|
async _post(path, body) {
|
|
105
|
-
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: {
|
|
108
|
-
'Authorization': `Bearer ${this.apiKey}`,
|
|
109
|
-
'Content-Type': 'application/json',
|
|
110
|
-
'User-Agent': 'waterlight-node/0.1.0',
|
|
111
|
-
},
|
|
112
|
-
body: JSON.stringify(body),
|
|
113
|
-
});
|
|
114
|
-
const data = await res.json();
|
|
115
|
-
if (!res.ok)
|
|
116
|
-
handleError(res.status, data);
|
|
117
|
-
return data;
|
|
117
|
+
return this._request('POST', path, body);
|
|
118
118
|
}
|
|
119
119
|
async _get(path) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
return this._request('GET', path);
|
|
121
|
+
}
|
|
122
|
+
async _request(method, path, body) {
|
|
123
|
+
const url = `${this.baseUrl}${path}`;
|
|
124
|
+
let attempt = 0;
|
|
125
|
+
while (true) {
|
|
126
|
+
const controller = new AbortController();
|
|
127
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
128
|
+
try {
|
|
129
|
+
const res = await fetch(url, {
|
|
130
|
+
method,
|
|
131
|
+
headers: {
|
|
132
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
133
|
+
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|
134
|
+
'User-Agent': 'waterlight-node/0.2.0',
|
|
135
|
+
},
|
|
136
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
137
|
+
signal: controller.signal,
|
|
138
|
+
});
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
if (!res.ok) {
|
|
141
|
+
if (RETRYABLE_STATUS.has(res.status) && attempt < this.maxRetries) {
|
|
142
|
+
const retryAfter = res.headers.get('retry-after');
|
|
143
|
+
const delay = retryAfter ? parseFloat(retryAfter) * 1000 : 500 * 2 ** attempt;
|
|
144
|
+
await sleep(delay);
|
|
145
|
+
attempt++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const data = await res.json().catch(() => ({}));
|
|
149
|
+
handleError(res.status, data, res.headers);
|
|
150
|
+
}
|
|
151
|
+
return await res.json();
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
if (e instanceof errors_1.WaterlightError)
|
|
156
|
+
throw e;
|
|
157
|
+
if (e?.name === 'AbortError')
|
|
158
|
+
throw new errors_1.APIError('Request timed out', 408);
|
|
159
|
+
throw new errors_1.APIError(`Network error: ${e?.message ?? e}`, 0);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
130
162
|
}
|
|
131
163
|
}
|
|
132
164
|
exports.Waterlight = Waterlight;
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
export declare class WaterlightError extends Error {
|
|
2
2
|
readonly status?: number;
|
|
3
|
-
|
|
3
|
+
readonly requestId?: string;
|
|
4
|
+
constructor(message: string, status?: number, requestId?: string);
|
|
4
5
|
}
|
|
5
6
|
export declare class AuthenticationError extends WaterlightError {
|
|
6
|
-
constructor(message: string);
|
|
7
|
+
constructor(message: string, requestId?: string);
|
|
7
8
|
}
|
|
8
9
|
export declare class RateLimitError extends WaterlightError {
|
|
9
|
-
|
|
10
|
+
readonly retryAfter?: number;
|
|
11
|
+
constructor(message: string, retryAfter?: number, requestId?: string);
|
|
10
12
|
}
|
|
11
13
|
export declare class InsufficientCreditsError extends WaterlightError {
|
|
12
|
-
constructor(message: string);
|
|
14
|
+
constructor(message: string, requestId?: string);
|
|
13
15
|
}
|
|
14
16
|
export declare class APIError extends WaterlightError {
|
|
15
|
-
constructor(message: string, status?: number);
|
|
17
|
+
constructor(message: string, status?: number, requestId?: string);
|
|
16
18
|
}
|
package/dist/errors.js
CHANGED
|
@@ -2,37 +2,39 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.APIError = exports.InsufficientCreditsError = exports.RateLimitError = exports.AuthenticationError = exports.WaterlightError = void 0;
|
|
4
4
|
class WaterlightError extends Error {
|
|
5
|
-
constructor(message, status) {
|
|
5
|
+
constructor(message, status, requestId) {
|
|
6
6
|
super(message);
|
|
7
7
|
this.name = 'WaterlightError';
|
|
8
8
|
this.status = status;
|
|
9
|
+
this.requestId = requestId;
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
exports.WaterlightError = WaterlightError;
|
|
12
13
|
class AuthenticationError extends WaterlightError {
|
|
13
|
-
constructor(message) {
|
|
14
|
-
super(message, 401);
|
|
14
|
+
constructor(message, requestId) {
|
|
15
|
+
super(message, 401, requestId);
|
|
15
16
|
this.name = 'AuthenticationError';
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
exports.AuthenticationError = AuthenticationError;
|
|
19
20
|
class RateLimitError extends WaterlightError {
|
|
20
|
-
constructor(message) {
|
|
21
|
-
super(message, 429);
|
|
21
|
+
constructor(message, retryAfter, requestId) {
|
|
22
|
+
super(message, 429, requestId);
|
|
22
23
|
this.name = 'RateLimitError';
|
|
24
|
+
this.retryAfter = retryAfter;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
exports.RateLimitError = RateLimitError;
|
|
26
28
|
class InsufficientCreditsError extends WaterlightError {
|
|
27
|
-
constructor(message) {
|
|
28
|
-
super(message, 402);
|
|
29
|
+
constructor(message, requestId) {
|
|
30
|
+
super(message, 402, requestId);
|
|
29
31
|
this.name = 'InsufficientCreditsError';
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
exports.InsufficientCreditsError = InsufficientCreditsError;
|
|
33
35
|
class APIError extends WaterlightError {
|
|
34
|
-
constructor(message, status) {
|
|
35
|
-
super(message, status);
|
|
36
|
+
constructor(message, status, requestId) {
|
|
37
|
+
super(message, status, requestId);
|
|
36
38
|
this.name = 'APIError';
|
|
37
39
|
}
|
|
38
40
|
}
|
package/dist/streaming.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class Stream implements AsyncIterable<ChatCompletionChunk> {
|
|
|
12
12
|
private readonly url;
|
|
13
13
|
private readonly apiKey;
|
|
14
14
|
private readonly body;
|
|
15
|
-
|
|
15
|
+
private readonly timeout;
|
|
16
|
+
constructor(url: string, apiKey: string, params: object, timeout?: number);
|
|
16
17
|
[Symbol.asyncIterator](): AsyncIterator<ChatCompletionChunk>;
|
|
17
18
|
}
|
package/dist/streaming.js
CHANGED
|
@@ -12,21 +12,35 @@ const errors_1 = require("./errors");
|
|
|
12
12
|
* }
|
|
13
13
|
*/
|
|
14
14
|
class Stream {
|
|
15
|
-
constructor(url, apiKey, params) {
|
|
15
|
+
constructor(url, apiKey, params, timeout = 120000) {
|
|
16
16
|
this.url = url;
|
|
17
17
|
this.apiKey = apiKey;
|
|
18
18
|
this.body = JSON.stringify({ ...params, stream: true });
|
|
19
|
+
this.timeout = timeout;
|
|
19
20
|
}
|
|
20
21
|
async *[Symbol.asyncIterator]() {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
24
|
+
let res;
|
|
25
|
+
try {
|
|
26
|
+
res = await fetch(this.url, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'Accept': 'text/event-stream',
|
|
32
|
+
'User-Agent': 'waterlight-node/0.2.0',
|
|
33
|
+
},
|
|
34
|
+
body: this.body,
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
if (e?.name === 'AbortError')
|
|
41
|
+
throw new errors_1.APIError('Stream request timed out', 408);
|
|
42
|
+
throw new errors_1.APIError(`Network error: ${e?.message ?? e}`, 0);
|
|
43
|
+
}
|
|
30
44
|
if (!res.ok) {
|
|
31
45
|
let errMsg = '';
|
|
32
46
|
try {
|
|
@@ -66,6 +80,7 @@ class Stream {
|
|
|
66
80
|
}
|
|
67
81
|
}
|
|
68
82
|
finally {
|
|
83
|
+
clearTimeout(timer);
|
|
69
84
|
reader.releaseLock();
|
|
70
85
|
}
|
|
71
86
|
}
|