@inferencesh/sdk 0.1.0 → 0.1.3
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/CHANGELOG.md +14 -1
- package/README.md +4 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +47 -5
- package/dist/client.test.d.ts +1 -0
- package/dist/client.test.js +212 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.js +61 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -1
- package/dist/stream.d.ts +8 -1
- package/dist/stream.js +27 -2
- package/dist/stream.test.d.ts +1 -0
- package/dist/stream.test.js +168 -0
- package/dist/types.d.ts +1364 -436
- package/dist/types.js +167 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [0.1.
|
|
10
|
+
## [0.1.1] - 2024-11-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Partial data handling for streaming updates (matches Python SDK behavior)
|
|
15
|
+
- `onPartialUpdate` callback option to receive list of changed fields
|
|
16
|
+
- Export `StreamManager` and `PartialDataWrapper` types
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Stream updates now properly extract data from server's partial update wrapper
|
|
21
|
+
- Removed unused `onYield` callback
|
|
22
|
+
|
|
23
|
+
## [0.1.0] - 2024-11-30
|
|
11
24
|
|
|
12
25
|
### Added
|
|
13
26
|
|
package/README.md
CHANGED
package/dist/client.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface InferenceConfig {
|
|
|
14
14
|
export interface RunOptions {
|
|
15
15
|
/** Callback for real-time status updates */
|
|
16
16
|
onUpdate?: (update: Task) => void;
|
|
17
|
+
/** Callback for partial updates with list of changed fields */
|
|
18
|
+
onPartialUpdate?: (update: Task, fields: string[]) => void;
|
|
17
19
|
/** Wait for task completion (default: true) */
|
|
18
20
|
wait?: boolean;
|
|
19
21
|
/** Auto-reconnect on connection loss (default: true) */
|
package/dist/client.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.inference = exports.Inference = void 0;
|
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const stream_1 = require("./stream");
|
|
6
6
|
const eventsource_1 = require("eventsource");
|
|
7
|
+
const errors_1 = require("./errors");
|
|
7
8
|
/**
|
|
8
9
|
* Inference.sh SDK Client
|
|
9
10
|
*
|
|
@@ -43,11 +44,45 @@ class Inference {
|
|
|
43
44
|
fetchOptions.body = JSON.stringify(options.data);
|
|
44
45
|
}
|
|
45
46
|
const response = await fetch(url.toString(), fetchOptions);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const responseText = await response.text();
|
|
48
|
+
// Try to parse as JSON
|
|
49
|
+
let data = null;
|
|
50
|
+
try {
|
|
51
|
+
data = JSON.parse(responseText);
|
|
49
52
|
}
|
|
50
|
-
|
|
53
|
+
catch {
|
|
54
|
+
// Not JSON
|
|
55
|
+
}
|
|
56
|
+
// Check for HTTP errors
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
// Check for RequirementsNotMetException (412 with errors array)
|
|
59
|
+
if (response.status === 412 && data && 'errors' in data && Array.isArray(data.errors)) {
|
|
60
|
+
throw errors_1.RequirementsNotMetException.fromResponse(data, response.status);
|
|
61
|
+
}
|
|
62
|
+
// General error handling
|
|
63
|
+
let errorDetail;
|
|
64
|
+
if (data && typeof data === 'object') {
|
|
65
|
+
const apiData = data;
|
|
66
|
+
if (apiData.error) {
|
|
67
|
+
errorDetail = typeof apiData.error === 'object' ? apiData.error.message : String(apiData.error);
|
|
68
|
+
}
|
|
69
|
+
else if ('message' in data) {
|
|
70
|
+
errorDetail = String(data.message);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
errorDetail = JSON.stringify(data);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (responseText) {
|
|
77
|
+
errorDetail = responseText.slice(0, 500);
|
|
78
|
+
}
|
|
79
|
+
throw new errors_1.InferenceError(response.status, errorDetail || 'Request failed', responseText);
|
|
80
|
+
}
|
|
81
|
+
const apiResponse = data;
|
|
82
|
+
if (!apiResponse?.success) {
|
|
83
|
+
throw new errors_1.InferenceError(response.status, apiResponse?.error?.message || 'Request failed', responseText);
|
|
84
|
+
}
|
|
85
|
+
return apiResponse.data;
|
|
51
86
|
}
|
|
52
87
|
createEventSource(endpoint) {
|
|
53
88
|
const url = new URL(`${this.baseUrl}${endpoint}`);
|
|
@@ -128,7 +163,7 @@ class Inference {
|
|
|
128
163
|
* ```
|
|
129
164
|
*/
|
|
130
165
|
async run(params, options = {}) {
|
|
131
|
-
const { onUpdate, wait = true, autoReconnect = true, maxReconnects = 5, reconnectDelayMs = 1000, } = options;
|
|
166
|
+
const { onUpdate, onPartialUpdate, wait = true, autoReconnect = true, maxReconnects = 5, reconnectDelayMs = 1000, } = options;
|
|
132
167
|
// Process input data and upload any files
|
|
133
168
|
const processedInput = await this.processInputData(params.input);
|
|
134
169
|
const task = await this.request("post", "/run", {
|
|
@@ -165,6 +200,13 @@ class Inference {
|
|
|
165
200
|
reject(new Error("task cancelled"));
|
|
166
201
|
}
|
|
167
202
|
},
|
|
203
|
+
onPartialData: (data, fields) => {
|
|
204
|
+
// Call onPartialUpdate if provided
|
|
205
|
+
if (onPartialUpdate) {
|
|
206
|
+
const stripped = this._stripTask(data);
|
|
207
|
+
onPartialUpdate(stripped, fields);
|
|
208
|
+
}
|
|
209
|
+
},
|
|
168
210
|
onError: (error) => {
|
|
169
211
|
reject(error);
|
|
170
212
|
streamManager.stop();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const client_1 = require("./client");
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
// Mock fetch globally
|
|
6
|
+
const mockFetch = jest.fn();
|
|
7
|
+
global.fetch = mockFetch;
|
|
8
|
+
describe('Inference', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
describe('constructor', () => {
|
|
13
|
+
it('should create an instance with valid config', () => {
|
|
14
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
15
|
+
expect(client).toBeInstanceOf(client_1.Inference);
|
|
16
|
+
});
|
|
17
|
+
it('should throw error when apiKey is missing', () => {
|
|
18
|
+
expect(() => new client_1.Inference({ apiKey: '' })).toThrow('API key is required');
|
|
19
|
+
expect(() => new client_1.Inference({})).toThrow('API key is required');
|
|
20
|
+
});
|
|
21
|
+
it('should use default baseUrl when not provided', () => {
|
|
22
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
23
|
+
expect(client).toBeDefined();
|
|
24
|
+
});
|
|
25
|
+
it('should accept custom baseUrl', () => {
|
|
26
|
+
const client = new client_1.Inference({
|
|
27
|
+
apiKey: 'test-api-key',
|
|
28
|
+
baseUrl: 'https://custom-api.example.com',
|
|
29
|
+
});
|
|
30
|
+
expect(client).toBeDefined();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('run', () => {
|
|
34
|
+
it('should make a POST request to /run', async () => {
|
|
35
|
+
const mockTask = {
|
|
36
|
+
id: 'task-123',
|
|
37
|
+
status: 9, // TaskStatusCompleted
|
|
38
|
+
created_at: new Date().toISOString(),
|
|
39
|
+
updated_at: new Date().toISOString(),
|
|
40
|
+
input: { message: 'hello world' },
|
|
41
|
+
output: { result: 'success' },
|
|
42
|
+
};
|
|
43
|
+
const responseData = { success: true, data: mockTask };
|
|
44
|
+
mockFetch.mockResolvedValueOnce({
|
|
45
|
+
ok: true,
|
|
46
|
+
status: 200,
|
|
47
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
48
|
+
json: () => Promise.resolve(responseData),
|
|
49
|
+
});
|
|
50
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
51
|
+
// Use input that won't trigger base64 detection (contains spaces/special chars)
|
|
52
|
+
const result = await client.run({ app: 'test-app', input: { message: 'hello world!' } }, { wait: false });
|
|
53
|
+
expect(result.id).toBe('task-123');
|
|
54
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/run'), expect.objectContaining({
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: expect.objectContaining({
|
|
57
|
+
Authorization: 'Bearer test-api-key',
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
}),
|
|
60
|
+
}));
|
|
61
|
+
});
|
|
62
|
+
it('should throw error on API failure', async () => {
|
|
63
|
+
const responseData = { success: false, error: { message: 'Invalid app' } };
|
|
64
|
+
mockFetch.mockResolvedValueOnce({
|
|
65
|
+
ok: false,
|
|
66
|
+
status: 400,
|
|
67
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
68
|
+
json: () => Promise.resolve(responseData),
|
|
69
|
+
});
|
|
70
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
71
|
+
await expect(client.run({ app: 'invalid-app', input: { message: 'test!' } }, { wait: false })).rejects.toThrow('Invalid app');
|
|
72
|
+
});
|
|
73
|
+
it('should throw RequirementsNotMetException on 412 with errors', async () => {
|
|
74
|
+
const requirementErrors = [
|
|
75
|
+
{
|
|
76
|
+
type: 'secret',
|
|
77
|
+
key: 'OPENAI_API_KEY',
|
|
78
|
+
message: 'Missing secret: OPENAI_API_KEY',
|
|
79
|
+
action: { type: 'add_secret', secret_key: 'OPENAI_API_KEY' },
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: 'integration',
|
|
83
|
+
key: 'google',
|
|
84
|
+
message: 'Integration not connected: google',
|
|
85
|
+
action: { type: 'connect', provider: 'google' },
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
const responseData = { errors: requirementErrors };
|
|
89
|
+
mockFetch.mockResolvedValueOnce({
|
|
90
|
+
ok: false,
|
|
91
|
+
status: 412,
|
|
92
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
93
|
+
json: () => Promise.resolve(responseData),
|
|
94
|
+
});
|
|
95
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
96
|
+
try {
|
|
97
|
+
await client.run({ app: 'test-app', input: { message: 'test!' } }, { wait: false });
|
|
98
|
+
fail('Expected RequirementsNotMetException to be thrown');
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
expect(e).toBeInstanceOf(errors_1.RequirementsNotMetException);
|
|
102
|
+
const exception = e;
|
|
103
|
+
expect(exception.errors).toHaveLength(2);
|
|
104
|
+
expect(exception.errors[0].type).toBe('secret');
|
|
105
|
+
expect(exception.errors[0].key).toBe('OPENAI_API_KEY');
|
|
106
|
+
expect(exception.errors[1].type).toBe('integration');
|
|
107
|
+
expect(exception.statusCode).toBe(412);
|
|
108
|
+
expect(exception.message).toBe('Missing secret: OPENAI_API_KEY');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
it('should include action details in RequirementsNotMetException', async () => {
|
|
112
|
+
const requirementErrors = [
|
|
113
|
+
{
|
|
114
|
+
type: 'scope',
|
|
115
|
+
key: 'calendar.readonly',
|
|
116
|
+
message: 'Missing scope: calendar.readonly',
|
|
117
|
+
action: {
|
|
118
|
+
type: 'add_scopes',
|
|
119
|
+
provider: 'google',
|
|
120
|
+
scopes: ['calendar.readonly', 'calendar.events'],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
const responseData = { errors: requirementErrors };
|
|
125
|
+
mockFetch.mockResolvedValueOnce({
|
|
126
|
+
ok: false,
|
|
127
|
+
status: 412,
|
|
128
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
129
|
+
json: () => Promise.resolve(responseData),
|
|
130
|
+
});
|
|
131
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
132
|
+
try {
|
|
133
|
+
await client.run({ app: 'test-app', input: {} }, { wait: false });
|
|
134
|
+
fail('Expected RequirementsNotMetException to be thrown');
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
expect(e).toBeInstanceOf(errors_1.RequirementsNotMetException);
|
|
138
|
+
const exception = e;
|
|
139
|
+
expect(exception.errors[0].action?.type).toBe('add_scopes');
|
|
140
|
+
expect(exception.errors[0].action?.scopes).toEqual(['calendar.readonly', 'calendar.events']);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('cancel', () => {
|
|
145
|
+
it('should make a POST request to cancel endpoint', async () => {
|
|
146
|
+
const responseData = { success: true, data: null };
|
|
147
|
+
mockFetch.mockResolvedValueOnce({
|
|
148
|
+
ok: true,
|
|
149
|
+
status: 200,
|
|
150
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
151
|
+
json: () => Promise.resolve(responseData),
|
|
152
|
+
});
|
|
153
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
154
|
+
await client.cancel('task-123');
|
|
155
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/tasks/task-123/cancel'), expect.objectContaining({ method: 'POST' }));
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('backward compatibility', () => {
|
|
159
|
+
it('should export lowercase inference as alias', () => {
|
|
160
|
+
expect(client_1.inference).toBe(client_1.Inference);
|
|
161
|
+
});
|
|
162
|
+
it('should work with lowercase inference', () => {
|
|
163
|
+
const client = new client_1.inference({ apiKey: 'test-api-key' });
|
|
164
|
+
expect(client).toBeInstanceOf(client_1.Inference);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('uploadFile', () => {
|
|
169
|
+
beforeEach(() => {
|
|
170
|
+
jest.clearAllMocks();
|
|
171
|
+
});
|
|
172
|
+
it('should upload a base64 string', async () => {
|
|
173
|
+
const mockFile = {
|
|
174
|
+
id: 'file-123',
|
|
175
|
+
uri: 'https://example.com/file.png',
|
|
176
|
+
upload_url: 'https://upload.example.com/signed-url',
|
|
177
|
+
};
|
|
178
|
+
const responseData = { success: true, data: [mockFile] };
|
|
179
|
+
mockFetch
|
|
180
|
+
.mockResolvedValueOnce({
|
|
181
|
+
ok: true,
|
|
182
|
+
status: 200,
|
|
183
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
184
|
+
json: () => Promise.resolve(responseData),
|
|
185
|
+
})
|
|
186
|
+
.mockResolvedValueOnce({ ok: true });
|
|
187
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
188
|
+
// Use valid base64 that won't be mistaken for regular text
|
|
189
|
+
const result = await client.uploadFile('SGVsbG8gV29ybGQh', {
|
|
190
|
+
filename: 'test.txt',
|
|
191
|
+
contentType: 'text/plain',
|
|
192
|
+
});
|
|
193
|
+
expect(result.uri).toBe('https://example.com/file.png');
|
|
194
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
195
|
+
});
|
|
196
|
+
it('should throw error when no upload URL provided', async () => {
|
|
197
|
+
const mockFile = {
|
|
198
|
+
id: 'file-123',
|
|
199
|
+
uri: 'https://example.com/file.png',
|
|
200
|
+
// Missing upload_url
|
|
201
|
+
};
|
|
202
|
+
const responseData = { success: true, data: [mockFile] };
|
|
203
|
+
mockFetch.mockResolvedValueOnce({
|
|
204
|
+
ok: true,
|
|
205
|
+
status: 200,
|
|
206
|
+
text: () => Promise.resolve(JSON.stringify(responseData)),
|
|
207
|
+
json: () => Promise.resolve(responseData),
|
|
208
|
+
});
|
|
209
|
+
const client = new client_1.Inference({ apiKey: 'test-api-key' });
|
|
210
|
+
await expect(client.uploadFile('SGVsbG8gV29ybGQh', { filename: 'test.txt' })).rejects.toThrow('No upload URL provided by the server');
|
|
211
|
+
});
|
|
212
|
+
});
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error classes for the inference.sh SDK.
|
|
3
|
+
*
|
|
4
|
+
* Note: types.ts contains interfaces (APIError, RequirementsNotMetError) that describe
|
|
5
|
+
* the API response data shapes. These classes are throwable Error subclasses for SDK use.
|
|
6
|
+
*/
|
|
7
|
+
import type { RequirementError } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* General HTTP/API error thrown by the SDK.
|
|
10
|
+
*
|
|
11
|
+
* Note: This is distinct from the `APIError` interface in types.ts which describes
|
|
12
|
+
* the error payload shape in API responses.
|
|
13
|
+
*/
|
|
14
|
+
export declare class InferenceError extends Error {
|
|
15
|
+
readonly statusCode: number;
|
|
16
|
+
readonly responseBody?: string;
|
|
17
|
+
constructor(statusCode: number, message: string, responseBody?: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Error thrown when app requirements (secrets, integrations, scopes) are not met.
|
|
21
|
+
*
|
|
22
|
+
* Thrown for HTTP 412 responses that contain structured requirement errors.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* try {
|
|
27
|
+
* const task = await client.run(params);
|
|
28
|
+
* } catch (e) {
|
|
29
|
+
* if (e instanceof RequirementsNotMetException) {
|
|
30
|
+
* for (const err of e.errors) {
|
|
31
|
+
* console.log(`Missing ${err.type}: ${err.key}`);
|
|
32
|
+
* if (err.action) {
|
|
33
|
+
* console.log(` Fix: ${err.action.type}`);
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare class RequirementsNotMetException extends Error {
|
|
41
|
+
readonly errors: RequirementError[];
|
|
42
|
+
readonly statusCode: number;
|
|
43
|
+
constructor(errors: RequirementError[], statusCode?: number);
|
|
44
|
+
/**
|
|
45
|
+
* Create from API response data.
|
|
46
|
+
*/
|
|
47
|
+
static fromResponse(data: {
|
|
48
|
+
errors?: RequirementError[];
|
|
49
|
+
}, statusCode?: number): RequirementsNotMetException;
|
|
50
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Error classes for the inference.sh SDK.
|
|
4
|
+
*
|
|
5
|
+
* Note: types.ts contains interfaces (APIError, RequirementsNotMetError) that describe
|
|
6
|
+
* the API response data shapes. These classes are throwable Error subclasses for SDK use.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.RequirementsNotMetException = exports.InferenceError = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* General HTTP/API error thrown by the SDK.
|
|
12
|
+
*
|
|
13
|
+
* Note: This is distinct from the `APIError` interface in types.ts which describes
|
|
14
|
+
* the error payload shape in API responses.
|
|
15
|
+
*/
|
|
16
|
+
class InferenceError extends Error {
|
|
17
|
+
constructor(statusCode, message, responseBody) {
|
|
18
|
+
super(`HTTP ${statusCode}: ${message}`);
|
|
19
|
+
this.name = 'InferenceError';
|
|
20
|
+
this.statusCode = statusCode;
|
|
21
|
+
this.responseBody = responseBody;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.InferenceError = InferenceError;
|
|
25
|
+
/**
|
|
26
|
+
* Error thrown when app requirements (secrets, integrations, scopes) are not met.
|
|
27
|
+
*
|
|
28
|
+
* Thrown for HTTP 412 responses that contain structured requirement errors.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* try {
|
|
33
|
+
* const task = await client.run(params);
|
|
34
|
+
* } catch (e) {
|
|
35
|
+
* if (e instanceof RequirementsNotMetException) {
|
|
36
|
+
* for (const err of e.errors) {
|
|
37
|
+
* console.log(`Missing ${err.type}: ${err.key}`);
|
|
38
|
+
* if (err.action) {
|
|
39
|
+
* console.log(` Fix: ${err.action.type}`);
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
class RequirementsNotMetException extends Error {
|
|
47
|
+
constructor(errors, statusCode = 412) {
|
|
48
|
+
const message = errors.length > 0 ? errors[0].message : 'requirements not met';
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = 'RequirementsNotMetException';
|
|
51
|
+
this.errors = errors;
|
|
52
|
+
this.statusCode = statusCode;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create from API response data.
|
|
56
|
+
*/
|
|
57
|
+
static fromResponse(data, statusCode = 412) {
|
|
58
|
+
return new RequirementsNotMetException(data.errors || [], statusCode);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.RequirementsNotMetException = RequirementsNotMetException;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { Inference, inference, InferenceConfig, RunOptions, UploadFileOptions } from './client';
|
|
2
|
+
export { StreamManager, PartialDataWrapper } from './stream';
|
|
3
|
+
export { InferenceError, RequirementsNotMetException } from './errors';
|
|
2
4
|
export * from './types';
|
|
3
5
|
export type { TaskDTO as Task } from './types';
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,17 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.inference = exports.Inference = void 0;
|
|
17
|
+
exports.RequirementsNotMetException = exports.InferenceError = exports.StreamManager = exports.inference = exports.Inference = void 0;
|
|
18
18
|
// Main client export
|
|
19
19
|
var client_1 = require("./client");
|
|
20
20
|
Object.defineProperty(exports, "Inference", { enumerable: true, get: function () { return client_1.Inference; } });
|
|
21
21
|
Object.defineProperty(exports, "inference", { enumerable: true, get: function () { return client_1.inference; } });
|
|
22
|
+
// Stream utilities
|
|
23
|
+
var stream_1 = require("./stream");
|
|
24
|
+
Object.defineProperty(exports, "StreamManager", { enumerable: true, get: function () { return stream_1.StreamManager; } });
|
|
25
|
+
// Error classes (throwable)
|
|
26
|
+
var errors_1 = require("./errors");
|
|
27
|
+
Object.defineProperty(exports, "InferenceError", { enumerable: true, get: function () { return errors_1.InferenceError; } });
|
|
28
|
+
Object.defineProperty(exports, "RequirementsNotMetException", { enumerable: true, get: function () { return errors_1.RequirementsNotMetException; } });
|
|
22
29
|
// Types - includes TaskStatus constants and all DTOs
|
|
23
30
|
__exportStar(require("./types"), exports);
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** Partial data structure from server (contains data and list of updated fields) */
|
|
2
|
+
export interface PartialDataWrapper<T> {
|
|
3
|
+
data: T;
|
|
4
|
+
fields: string[];
|
|
5
|
+
}
|
|
1
6
|
export interface StreamManagerOptions<T> {
|
|
2
7
|
createEventSource: () => Promise<EventSource | null>;
|
|
3
8
|
autoReconnect?: boolean;
|
|
@@ -6,8 +11,10 @@ export interface StreamManagerOptions<T> {
|
|
|
6
11
|
onError?: (error: Error) => void;
|
|
7
12
|
onStart?: () => void;
|
|
8
13
|
onStop?: () => void;
|
|
14
|
+
/** Called with the extracted data (handles both full and partial data) */
|
|
9
15
|
onData?: (data: T) => void;
|
|
10
|
-
|
|
16
|
+
/** Called specifically for partial updates with data and the list of updated fields */
|
|
17
|
+
onPartialData?: (data: T, fields: string[]) => void;
|
|
11
18
|
}
|
|
12
19
|
export declare class StreamManager<T> {
|
|
13
20
|
private options;
|
package/dist/stream.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StreamManager = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Check if the parsed data is a partial data wrapper from the server.
|
|
6
|
+
* The server sends partial updates in the format: { data: T, fields: string[] }
|
|
7
|
+
*/
|
|
8
|
+
function isPartialDataWrapper(parsed) {
|
|
9
|
+
return (typeof parsed === 'object' &&
|
|
10
|
+
parsed !== null &&
|
|
11
|
+
'data' in parsed &&
|
|
12
|
+
'fields' in parsed &&
|
|
13
|
+
Array.isArray(parsed.fields));
|
|
14
|
+
}
|
|
4
15
|
class StreamManager {
|
|
5
16
|
constructor(options) {
|
|
6
17
|
this.eventSource = null;
|
|
@@ -87,8 +98,22 @@ class StreamManager {
|
|
|
87
98
|
return;
|
|
88
99
|
try {
|
|
89
100
|
const parsed = JSON.parse(e.data);
|
|
90
|
-
this
|
|
91
|
-
|
|
101
|
+
// Check if this is a partial data wrapper from the server
|
|
102
|
+
if (isPartialDataWrapper(parsed)) {
|
|
103
|
+
// Extract the actual data from the wrapper
|
|
104
|
+
const actualData = parsed.data;
|
|
105
|
+
const fields = parsed.fields;
|
|
106
|
+
// Call onPartialData if provided
|
|
107
|
+
if (this.options.onPartialData) {
|
|
108
|
+
this.options.onPartialData(actualData, fields);
|
|
109
|
+
}
|
|
110
|
+
// Always call onData with the extracted data
|
|
111
|
+
this.options.onData?.(actualData);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Not a partial wrapper, treat as full data
|
|
115
|
+
this.options.onData?.(parsed);
|
|
116
|
+
}
|
|
92
117
|
}
|
|
93
118
|
catch (err) {
|
|
94
119
|
const error = err instanceof Error ? err : new Error("Invalid JSON");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|