@inferencesh/sdk 0.1.1 → 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/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 data = await response.json();
47
- if (!data.success) {
48
- throw new Error(data.error?.message || "Request failed");
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
- return data.data;
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}`);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const client_1 = require("./client");
4
+ const errors_1 = require("./errors");
4
5
  // Mock fetch globally
5
6
  const mockFetch = jest.fn();
6
7
  global.fetch = mockFetch;
@@ -39,8 +40,12 @@ describe('Inference', () => {
39
40
  input: { message: 'hello world' },
40
41
  output: { result: 'success' },
41
42
  };
43
+ const responseData = { success: true, data: mockTask };
42
44
  mockFetch.mockResolvedValueOnce({
43
- json: () => Promise.resolve({ success: true, data: mockTask }),
45
+ ok: true,
46
+ status: 200,
47
+ text: () => Promise.resolve(JSON.stringify(responseData)),
48
+ json: () => Promise.resolve(responseData),
44
49
  });
45
50
  const client = new client_1.Inference({ apiKey: 'test-api-key' });
46
51
  // Use input that won't trigger base64 detection (contains spaces/special chars)
@@ -55,17 +60,95 @@ describe('Inference', () => {
55
60
  }));
56
61
  });
57
62
  it('should throw error on API failure', async () => {
63
+ const responseData = { success: false, error: { message: 'Invalid app' } };
58
64
  mockFetch.mockResolvedValueOnce({
59
- json: () => Promise.resolve({ success: false, error: { message: 'Invalid app' } }),
65
+ ok: false,
66
+ status: 400,
67
+ text: () => Promise.resolve(JSON.stringify(responseData)),
68
+ json: () => Promise.resolve(responseData),
60
69
  });
61
70
  const client = new client_1.Inference({ apiKey: 'test-api-key' });
62
71
  await expect(client.run({ app: 'invalid-app', input: { message: 'test!' } }, { wait: false })).rejects.toThrow('Invalid app');
63
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
+ });
64
143
  });
65
144
  describe('cancel', () => {
66
145
  it('should make a POST request to cancel endpoint', async () => {
146
+ const responseData = { success: true, data: null };
67
147
  mockFetch.mockResolvedValueOnce({
68
- json: () => Promise.resolve({ success: true, data: null }),
148
+ ok: true,
149
+ status: 200,
150
+ text: () => Promise.resolve(JSON.stringify(responseData)),
151
+ json: () => Promise.resolve(responseData),
69
152
  });
70
153
  const client = new client_1.Inference({ apiKey: 'test-api-key' });
71
154
  await client.cancel('task-123');
@@ -92,9 +175,13 @@ describe('uploadFile', () => {
92
175
  uri: 'https://example.com/file.png',
93
176
  upload_url: 'https://upload.example.com/signed-url',
94
177
  };
178
+ const responseData = { success: true, data: [mockFile] };
95
179
  mockFetch
96
180
  .mockResolvedValueOnce({
97
- json: () => Promise.resolve({ success: true, data: [mockFile] }),
181
+ ok: true,
182
+ status: 200,
183
+ text: () => Promise.resolve(JSON.stringify(responseData)),
184
+ json: () => Promise.resolve(responseData),
98
185
  })
99
186
  .mockResolvedValueOnce({ ok: true });
100
187
  const client = new client_1.Inference({ apiKey: 'test-api-key' });
@@ -112,8 +199,12 @@ describe('uploadFile', () => {
112
199
  uri: 'https://example.com/file.png',
113
200
  // Missing upload_url
114
201
  };
202
+ const responseData = { success: true, data: [mockFile] };
115
203
  mockFetch.mockResolvedValueOnce({
116
- json: () => Promise.resolve({ success: true, data: [mockFile] }),
204
+ ok: true,
205
+ status: 200,
206
+ text: () => Promise.resolve(JSON.stringify(responseData)),
207
+ json: () => Promise.resolve(responseData),
117
208
  });
118
209
  const client = new client_1.Inference({ apiKey: 'test-api-key' });
119
210
  await expect(client.uploadFile('SGVsbG8gV29ybGQh', { filename: 'test.txt' })).rejects.toThrow('No upload URL provided by the server');
@@ -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,4 +1,5 @@
1
1
  export { Inference, inference, InferenceConfig, RunOptions, UploadFileOptions } from './client';
2
2
  export { StreamManager, PartialDataWrapper } from './stream';
3
+ export { InferenceError, RequirementsNotMetException } from './errors';
3
4
  export * from './types';
4
5
  export type { TaskDTO as Task } from './types';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ 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.StreamManager = 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; } });
@@ -22,5 +22,9 @@ Object.defineProperty(exports, "inference", { enumerable: true, get: function ()
22
22
  // Stream utilities
23
23
  var stream_1 = require("./stream");
24
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; } });
25
29
  // Types - includes TaskStatus constants and all DTOs
26
30
  __exportStar(require("./types"), exports);