@inferencesh/sdk 0.6.8 → 0.6.10

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.
@@ -0,0 +1,67 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { AppsAPI } from './apps';
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+ function mockJsonResponse(body) {
6
+ mockFetch.mockResolvedValueOnce({
7
+ ok: true,
8
+ status: 200,
9
+ text: () => Promise.resolve(JSON.stringify(body)),
10
+ });
11
+ }
12
+ describe('AppsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new AppsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST /apps/list for list()', async () => {
18
+ const apps = { items: [{ id: 'app-1' }], next_cursor: null };
19
+ mockJsonResponse(apps);
20
+ const result = await api().list({ limit: 10 });
21
+ expect(result).toEqual(apps);
22
+ const [url, init] = mockFetch.mock.calls[0];
23
+ expect(url).toContain('/apps/list');
24
+ expect(init.method).toBe('POST');
25
+ expect(JSON.parse(init.body)).toEqual({ limit: 10 });
26
+ });
27
+ it('should GET /apps/{name} for getByName()', async () => {
28
+ const app = { id: 'app-1', name: 'inference/claude-haiku' };
29
+ mockJsonResponse(app);
30
+ const result = await api().getByName('inference/claude-haiku');
31
+ expect(result).toEqual(app);
32
+ const [url] = mockFetch.mock.calls[0];
33
+ expect(url).toContain('/apps/inference/claude-haiku');
34
+ });
35
+ it('should POST transfer with team_id for transferOwnership()', async () => {
36
+ const app = { id: 'app-1' };
37
+ mockJsonResponse(app);
38
+ await api().transferOwnership('app-1', 'team-99');
39
+ const [url, init] = mockFetch.mock.calls[0];
40
+ expect(url).toContain('/apps/app-1/transfer');
41
+ expect(JSON.parse(init.body)).toEqual({ team_id: 'team-99' });
42
+ });
43
+ it('should POST license payload for saveLicense()', async () => {
44
+ const license = { app_id: 'app-1', license: 'key-abc' };
45
+ mockJsonResponse(license);
46
+ const result = await api().saveLicense('app-1', 'key-abc');
47
+ expect(result).toEqual(license);
48
+ const [, init] = mockFetch.mock.calls[0];
49
+ expect(JSON.parse(init.body)).toEqual({ license: 'key-abc' });
50
+ });
51
+ it('should POST version_id for setCurrentVersion()', async () => {
52
+ const app = { id: 'app-1', current_version_id: 'ver-2' };
53
+ mockJsonResponse(app);
54
+ await api().setCurrentVersion('app-1', 'ver-2');
55
+ const [, init] = mockFetch.mock.calls[0];
56
+ expect(JSON.parse(init.body)).toEqual({ version_id: 'ver-2' });
57
+ });
58
+ it('should POST /apps/{id}/duplicate for duplicate()', async () => {
59
+ const app = { id: 'app-copy' };
60
+ mockJsonResponse(app);
61
+ const result = await api().duplicate('app-1');
62
+ expect(result).toEqual(app);
63
+ const [url, init] = mockFetch.mock.calls[0];
64
+ expect(url).toContain('/apps/app-1/duplicate');
65
+ expect(init.method).toBe('POST');
66
+ });
67
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { ChatsAPI } from './chats';
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+ function mockJsonResponse(body) {
6
+ mockFetch.mockResolvedValueOnce({
7
+ ok: true,
8
+ status: 200,
9
+ text: () => Promise.resolve(JSON.stringify(body)),
10
+ });
11
+ }
12
+ describe('ChatsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new ChatsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should GET /chats/{id}/trace for getTrace()', async () => {
18
+ const trace = { chat_id: 'chat-1', spans: [] };
19
+ mockJsonResponse(trace);
20
+ const result = await api().getTrace('chat-1');
21
+ expect(result).toEqual(trace);
22
+ const [url, init] = mockFetch.mock.calls[0];
23
+ expect(url).toContain('/chats/chat-1/trace');
24
+ expect(init.method).toBe('GET');
25
+ });
26
+ it('should DELETE /chats/{id} for delete()', async () => {
27
+ mockJsonResponse(null);
28
+ await api().delete('chat-9');
29
+ const [url, init] = mockFetch.mock.calls[0];
30
+ expect(url).toContain('/chats/chat-9');
31
+ expect(init.method).toBe('DELETE');
32
+ });
33
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { EnginesAPI } from './engines';
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+ function mockJsonResponse(body) {
6
+ mockFetch.mockResolvedValueOnce({
7
+ ok: true,
8
+ status: 200,
9
+ text: () => Promise.resolve(JSON.stringify(body)),
10
+ });
11
+ }
12
+ describe('EnginesAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new EnginesAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST resource ids for getForResources()', async () => {
18
+ const engines = [{ id: 'eng-1' }];
19
+ mockJsonResponse(engines);
20
+ const result = await api().getForResources({
21
+ app_ids: ['app-1'],
22
+ agent_ids: ['agent-1'],
23
+ });
24
+ expect(result).toEqual(engines);
25
+ const [url, init] = mockFetch.mock.calls[0];
26
+ expect(url).toContain('/engines/resources');
27
+ expect(JSON.parse(init.body)).toEqual({
28
+ app_ids: ['app-1'],
29
+ agent_ids: ['agent-1'],
30
+ });
31
+ });
32
+ it('should POST /engines/{id}/stop for stop()', async () => {
33
+ mockJsonResponse(null);
34
+ await api().stop('eng-1');
35
+ const [url, init] = mockFetch.mock.calls[0];
36
+ expect(url).toContain('/engines/eng-1/stop');
37
+ expect(init.method).toBe('POST');
38
+ });
39
+ it('should POST /engines/{id}/restart for restart()', async () => {
40
+ mockJsonResponse(null);
41
+ await api().restart('eng-1');
42
+ const [url] = mockFetch.mock.calls[0];
43
+ expect(url).toContain('/engines/eng-1/restart');
44
+ });
45
+ it('should open SSE on /engines/{id}/stream for stream()', async () => {
46
+ const http = new HttpClient({ apiKey: 'test-key' });
47
+ const createEventSource = jest
48
+ .spyOn(http, 'createEventSource')
49
+ .mockResolvedValue(null);
50
+ const engines = new EnginesAPI(http);
51
+ await engines.stream('eng-7');
52
+ expect(createEventSource).toHaveBeenCalledWith('/engines/eng-7/stream');
53
+ createEventSource.mockRestore();
54
+ });
55
+ });
@@ -27,7 +27,7 @@ describe('FilesAPI', () => {
27
27
  upload_url: 'https://upload.example.com/put',
28
28
  content_type: 'image/png',
29
29
  };
30
- mockJsonResponse({ success: true, data: [fileRecord] });
30
+ mockJsonResponse([fileRecord]);
31
31
  mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
32
32
  const input = {
33
33
  prompt: 'draw',
@@ -41,10 +41,7 @@ describe('FilesAPI', () => {
41
41
  });
42
42
  describe('upload', () => {
43
43
  it('should reject invalid data URI format when uploading content', async () => {
44
- mockJsonResponse({
45
- success: true,
46
- data: [{ id: 'file-x', uri: '', upload_url: 'https://upload.example.com/put' }],
47
- });
44
+ mockJsonResponse([{ id: 'file-x', uri: '', upload_url: 'https://upload.example.com/put' }]);
48
45
  await expect(api().upload('data:invalid')).rejects.toThrow('Invalid data URI format');
49
46
  });
50
47
  it('should decode URL-safe base64 in data URIs', async () => {
@@ -54,7 +51,7 @@ describe('FilesAPI', () => {
54
51
  upload_url: 'https://upload.example.com/put',
55
52
  content_type: 'text/plain',
56
53
  };
57
- mockJsonResponse({ success: true, data: [fileRecord] });
54
+ mockJsonResponse([fileRecord]);
58
55
  mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
59
56
  // "SGVsbG8" is "Hello" in standard base64; URL-safe variant uses '-' instead of '+'
60
57
  const dataUri = 'data:text/plain;base64,SGVsbG8';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { FlowRunsAPI } from './flow-runs';
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+ function mockJsonResponse(body) {
6
+ mockFetch.mockResolvedValueOnce({
7
+ ok: true,
8
+ status: 200,
9
+ text: () => Promise.resolve(JSON.stringify(body)),
10
+ });
11
+ }
12
+ describe('FlowRunsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new FlowRunsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST flow and input for create()', async () => {
18
+ const flowRun = { id: 'fr-1', flow: 'flow-1' };
19
+ mockJsonResponse(flowRun);
20
+ const result = await api().create('flow-1', { prompt: 'hi' });
21
+ expect(result).toEqual(flowRun);
22
+ const [, init] = mockFetch.mock.calls[0];
23
+ expect(JSON.parse(init.body)).toEqual({
24
+ flow: 'flow-1',
25
+ input: { prompt: 'hi' },
26
+ });
27
+ });
28
+ it('should POST /flowruns/{id}/cancel for cancel()', async () => {
29
+ mockJsonResponse(null);
30
+ await api().cancel('fr-1');
31
+ const [url, init] = mockFetch.mock.calls[0];
32
+ expect(url).toContain('/flowruns/fr-1/cancel');
33
+ expect(init.method).toBe('POST');
34
+ });
35
+ it('should open SSE on /flowruns/{id}/stream for stream()', async () => {
36
+ const http = new HttpClient({ apiKey: 'test-key' });
37
+ const createEventSource = jest
38
+ .spyOn(http, 'createEventSource')
39
+ .mockResolvedValue(null);
40
+ const flowRuns = new FlowRunsAPI(http);
41
+ await flowRuns.stream('fr-42');
42
+ expect(createEventSource).toHaveBeenCalledWith('/flowruns/fr-42/stream');
43
+ createEventSource.mockRestore();
44
+ });
45
+ it('should open task SSE for streamTasks()', async () => {
46
+ const http = new HttpClient({ apiKey: 'test-key' });
47
+ const createEventSource = jest
48
+ .spyOn(http, 'createEventSource')
49
+ .mockResolvedValue(null);
50
+ const flowRuns = new FlowRunsAPI(http);
51
+ await flowRuns.streamTasks('fr-42');
52
+ expect(createEventSource).toHaveBeenCalledWith('/flowruns/fr-42/tasks/stream');
53
+ createEventSource.mockRestore();
54
+ });
55
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { HttpClient } from '../http/client';
2
+ import { FlowsAPI } from './flows';
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+ function mockJsonResponse(body) {
6
+ mockFetch.mockResolvedValueOnce({
7
+ ok: true,
8
+ status: 200,
9
+ text: () => Promise.resolve(JSON.stringify(body)),
10
+ });
11
+ }
12
+ describe('FlowsAPI', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ const api = () => new FlowsAPI(new HttpClient({ apiKey: 'test-key' }));
17
+ it('should POST name for create()', async () => {
18
+ const flow = { id: 'flow-1', name: 'My Flow' };
19
+ mockJsonResponse(flow);
20
+ const result = await api().create('My Flow');
21
+ expect(result).toEqual(flow);
22
+ const [, init] = mockFetch.mock.calls[0];
23
+ expect(JSON.parse(init.body)).toEqual({ name: 'My Flow' });
24
+ });
25
+ it('should POST /flows/{id}/app for createApp()', async () => {
26
+ const app = { id: 'app-from-flow' };
27
+ mockJsonResponse(app);
28
+ const result = await api().createApp('flow-1');
29
+ expect(result).toEqual(app);
30
+ const [url] = mockFetch.mock.calls[0];
31
+ expect(url).toContain('/flows/flow-1/app');
32
+ });
33
+ it('should open SSE on /flows/{id}/stream for stream()', async () => {
34
+ const http = new HttpClient({ apiKey: 'test-key' });
35
+ const createEventSource = jest
36
+ .spyOn(http, 'createEventSource')
37
+ .mockResolvedValue(null);
38
+ const flows = new FlowsAPI(http);
39
+ await flows.stream('flow-5');
40
+ expect(createEventSource).toHaveBeenCalledWith('/flows/flow-5/stream');
41
+ createEventSource.mockRestore();
42
+ });
43
+ });
@@ -16,7 +16,7 @@ describe('SessionsAPI', () => {
16
16
  const api = () => new SessionsAPI(new HttpClient({ apiKey: 'test-key' }));
17
17
  it('should GET /sessions/{id} for get()', async () => {
18
18
  const session = { id: 'sess_1', status: 'active' };
19
- mockJsonResponse({ success: true, data: session });
19
+ mockJsonResponse(session);
20
20
  const result = await api().get('sess_1');
21
21
  expect(result).toEqual(session);
22
22
  const [url, init] = mockFetch.mock.calls[0];
@@ -24,7 +24,7 @@ describe('SessionsAPI', () => {
24
24
  expect(init.method).toBe('GET');
25
25
  });
26
26
  it('should return an empty array when list() response is null', async () => {
27
- mockJsonResponse({ success: true, data: null });
27
+ mockJsonResponse(null);
28
28
  const result = await api().list();
29
29
  expect(result).toEqual([]);
30
30
  const [url] = mockFetch.mock.calls[0];
@@ -32,7 +32,7 @@ describe('SessionsAPI', () => {
32
32
  });
33
33
  it('should POST /sessions/{id}/keepalive for keepalive()', async () => {
34
34
  const session = { id: 'sess_2', status: 'active' };
35
- mockJsonResponse({ success: true, data: session });
35
+ mockJsonResponse(session);
36
36
  const result = await api().keepalive('sess_2');
37
37
  expect(result).toEqual(session);
38
38
  const [url, init] = mockFetch.mock.calls[0];
@@ -40,7 +40,7 @@ describe('SessionsAPI', () => {
40
40
  expect(init.method).toBe('POST');
41
41
  });
42
42
  it('should DELETE /sessions/{id} for end()', async () => {
43
- mockJsonResponse({ success: true, data: null });
43
+ mockJsonResponse(null);
44
44
  await api().end('sess_3');
45
45
  const [url, init] = mockFetch.mock.calls[0];
46
46
  expect(url).toContain('/sessions/sess_3');
@@ -35,10 +35,10 @@ describe('TasksAPI.run (polling mode)', () => {
35
35
  it('should resolve when status polling detects completion', async () => {
36
36
  const runningTask = makeTask();
37
37
  const completedTask = makeTask({ status: TaskStatusCompleted, output: { ok: true } });
38
- mockJsonResponse({ success: true, data: runningTask });
39
- mockJsonResponse({ success: true, data: { status: TaskStatusRunning } });
40
- mockJsonResponse({ success: true, data: { status: TaskStatusCompleted } });
41
- mockJsonResponse({ success: true, data: completedTask });
38
+ mockJsonResponse(runningTask);
39
+ mockJsonResponse({ status: TaskStatusRunning });
40
+ mockJsonResponse({ status: TaskStatusCompleted });
41
+ mockJsonResponse(completedTask);
42
42
  const onUpdate = jest.fn();
43
43
  const result = await api().run({ app: 'test-app', input: {} }, { prompt: 'hi' }, { wait: true, stream: false, onUpdate });
44
44
  expect(result.status).toBe(TaskStatusCompleted);
@@ -47,42 +47,42 @@ describe('TasksAPI.run (polling mode)', () => {
47
47
  it('should reject when polling detects a failed task', async () => {
48
48
  const runningTask = makeTask();
49
49
  const failedTask = makeTask({ status: TaskStatusFailed, error: 'model error' });
50
- mockJsonResponse({ success: true, data: runningTask });
51
- mockJsonResponse({ success: true, data: { status: TaskStatusRunning } });
52
- mockJsonResponse({ success: true, data: { status: TaskStatusFailed } });
53
- mockJsonResponse({ success: true, data: failedTask });
50
+ mockJsonResponse(runningTask);
51
+ mockJsonResponse({ status: TaskStatusRunning });
52
+ mockJsonResponse({ status: TaskStatusFailed });
53
+ mockJsonResponse(failedTask);
54
54
  await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false })).rejects.toThrow('model error');
55
55
  });
56
56
  it('should reject when polling detects a cancelled task', async () => {
57
57
  const runningTask = makeTask();
58
58
  const cancelledTask = makeTask({ status: TaskStatusCancelled });
59
- mockJsonResponse({ success: true, data: runningTask });
60
- mockJsonResponse({ success: true, data: { status: TaskStatusRunning } });
61
- mockJsonResponse({ success: true, data: { status: TaskStatusCancelled } });
62
- mockJsonResponse({ success: true, data: cancelledTask });
59
+ mockJsonResponse(runningTask);
60
+ mockJsonResponse({ status: TaskStatusRunning });
61
+ mockJsonResponse({ status: TaskStatusCancelled });
62
+ mockJsonResponse(cancelledTask);
63
63
  await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false })).rejects.toThrow('task cancelled');
64
64
  });
65
65
  it('should reject when full task fetch fails after status change', async () => {
66
66
  const runningTask = makeTask();
67
- mockJsonResponse({ success: true, data: runningTask });
68
- mockJsonResponse({ success: true, data: { status: TaskStatusRunning } });
69
- mockJsonResponse({ success: true, data: { status: TaskStatusCompleted } });
67
+ mockJsonResponse(runningTask);
68
+ mockJsonResponse({ status: TaskStatusRunning });
69
+ mockJsonResponse({ status: TaskStatusCompleted });
70
70
  mockFetch.mockRejectedValueOnce(new Error('network down'));
71
71
  await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false })).rejects.toThrow('network down');
72
72
  });
73
73
  it('should reject when status polling fails', async () => {
74
74
  const runningTask = makeTask();
75
- mockJsonResponse({ success: true, data: runningTask });
75
+ mockJsonResponse(runningTask);
76
76
  mockFetch.mockRejectedValueOnce(new Error('status endpoint down'));
77
77
  await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false })).rejects.toThrow('status endpoint down');
78
78
  });
79
79
  it('should parse string terminal statuses from the status endpoint', async () => {
80
80
  const runningTask = makeTask();
81
81
  const completedTask = makeTask({ status: TaskStatusCompleted });
82
- mockJsonResponse({ success: true, data: runningTask });
83
- mockJsonResponse({ success: true, data: { status: TaskStatusRunning } });
84
- mockJsonResponse({ success: true, data: { status: 'completed' } });
85
- mockJsonResponse({ success: true, data: completedTask });
82
+ mockJsonResponse(runningTask);
83
+ mockJsonResponse({ status: TaskStatusRunning });
84
+ mockJsonResponse({ status: 'completed' });
85
+ mockJsonResponse(completedTask);
86
86
  const result = await api().run({ app: 'test-app', input: {} }, {}, { wait: true, stream: false });
87
87
  expect(result.status).toBe(TaskStatusCompleted);
88
88
  });
@@ -111,7 +111,7 @@ describe('TasksAPI.run (general)', () => {
111
111
  const streamingApi = () => new TasksAPI(new HttpClient({ apiKey: 'test-key', stream: true }));
112
112
  it('should return immediately when wait is false', async () => {
113
113
  const task = makeTask();
114
- mockJsonResponse({ success: true, data: task });
114
+ mockJsonResponse(task);
115
115
  const result = await streamingApi().run({ app: 'test-app', input: {} }, { prompt: 'hi' }, { wait: false });
116
116
  expect(result.id).toBe('task-1');
117
117
  expect(mockFetch).toHaveBeenCalledTimes(1);
@@ -128,7 +128,7 @@ describe('TasksAPI.run (streaming mode)', () => {
128
128
  return Promise.resolve({
129
129
  ok: true,
130
130
  status: 200,
131
- text: () => Promise.resolve(JSON.stringify({ success: true, data: initialTask })),
131
+ text: () => Promise.resolve(JSON.stringify(initialTask)),
132
132
  });
133
133
  }
134
134
  return Promise.resolve(mockNdjsonStream(ndjsonChunks));
@@ -156,6 +156,24 @@ describe('TasksAPI.run (streaming mode)', () => {
156
156
  ]);
157
157
  await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true })).rejects.toThrow('task cancelled');
158
158
  });
159
+ it('should reject when partial stream reports failure', async () => {
160
+ setupStreamMocks([
161
+ `${JSON.stringify({
162
+ data: { status: TaskStatusFailed, id: 'task-1', error: 'partial fail' },
163
+ fields: ['status'],
164
+ })}\n`,
165
+ ]);
166
+ await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true })).rejects.toThrow('partial fail');
167
+ });
168
+ it('should reject when partial stream reports cancellation', async () => {
169
+ setupStreamMocks([
170
+ `${JSON.stringify({
171
+ data: { status: TaskStatusCancelled, id: 'task-1' },
172
+ fields: ['status'],
173
+ })}\n`,
174
+ ]);
175
+ await expect(api().run({ app: 'test-app', input: {} }, {}, { wait: true })).rejects.toThrow('task cancelled');
176
+ });
159
177
  it('should handle partial updates via onPartialUpdate', async () => {
160
178
  setupStreamMocks([
161
179
  `${JSON.stringify({
@@ -38,7 +38,7 @@ describe('Inference', () => {
38
38
  input: { message: 'hello world' },
39
39
  output: { result: 'success' },
40
40
  };
41
- const responseData = { success: true, data: mockTask };
41
+ const responseData = mockTask;
42
42
  mockFetch.mockResolvedValueOnce({
43
43
  ok: true,
44
44
  status: 200,
@@ -58,7 +58,7 @@ describe('Inference', () => {
58
58
  }));
59
59
  });
60
60
  it('should throw error on API failure', async () => {
61
- const responseData = { success: false, error: { message: 'Invalid app' } };
61
+ const responseData = { message: 'Invalid app' };
62
62
  mockFetch.mockResolvedValueOnce({
63
63
  ok: false,
64
64
  status: 400,
@@ -141,7 +141,7 @@ describe('Inference', () => {
141
141
  });
142
142
  describe('cancel', () => {
143
143
  it('should make a POST request to cancel endpoint', async () => {
144
- const responseData = { success: true, data: null };
144
+ const responseData = null;
145
145
  mockFetch.mockResolvedValueOnce({
146
146
  ok: true,
147
147
  status: 200,
@@ -185,7 +185,7 @@ describe('namespaced APIs', () => {
185
185
  updated_at: new Date().toISOString(),
186
186
  input: { message: 'hello world' },
187
187
  };
188
- const responseData = { success: true, data: mockTask };
188
+ const responseData = mockTask;
189
189
  mockFetch.mockResolvedValueOnce({
190
190
  ok: true,
191
191
  status: 200,
@@ -202,7 +202,7 @@ describe('namespaced APIs', () => {
202
202
  });
203
203
  it('should get task via tasks.get()', async () => {
204
204
  const mockTask = { id: 'task-123', status: 7 };
205
- const responseData = { success: true, data: mockTask };
205
+ const responseData = mockTask;
206
206
  mockFetch.mockResolvedValueOnce({
207
207
  ok: true,
208
208
  status: 200,
@@ -215,7 +215,7 @@ describe('namespaced APIs', () => {
215
215
  expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/tasks/task-123'), expect.objectContaining({ method: 'GET' }));
216
216
  });
217
217
  it('should cancel task via tasks.cancel()', async () => {
218
- const responseData = { success: true, data: null };
218
+ const responseData = null;
219
219
  mockFetch.mockResolvedValueOnce({
220
220
  ok: true,
221
221
  status: 200,
@@ -298,7 +298,7 @@ describe('uploadFile', () => {
298
298
  uri: 'https://example.com/file.png',
299
299
  upload_url: 'https://upload.example.com/signed-url',
300
300
  };
301
- const responseData = { success: true, data: [mockFile] };
301
+ const responseData = [mockFile];
302
302
  mockFetch
303
303
  .mockResolvedValueOnce({
304
304
  ok: true,
@@ -322,7 +322,7 @@ describe('uploadFile', () => {
322
322
  uri: 'https://example.com/file.png',
323
323
  // Missing upload_url
324
324
  };
325
- const responseData = { success: true, data: [mockFile] };
325
+ const responseData = [mockFile];
326
326
  mockFetch.mockResolvedValueOnce({
327
327
  ok: true,
328
328
  status: 200,
@@ -16,7 +16,7 @@ export class HttpClient {
16
16
  this.baseUrl = config.baseUrl || 'https://api.inference.sh';
17
17
  this.proxyUrl = config.proxyUrl;
18
18
  this.getToken = config.getToken;
19
- this.customHeaders = { 'X-Client-Source': 'inference-sdk-js/0.5.13', ...config.headers };
19
+ this.customHeaders = { 'X-Client-Source': 'inference-sdk-js/0.5.13', 'X-API-Version': '2', ...config.headers };
20
20
  this.credentials = config.credentials || 'include';
21
21
  this.onError = config.onError;
22
22
  this.streamDefault = config.stream ?? true;
@@ -114,7 +114,6 @@ export class HttpClient {
114
114
  }
115
115
  const response = await fetch(fetchUrl, fetchOptions);
116
116
  const responseText = await response.text();
117
- // Try to parse as JSON
118
117
  let data = null;
119
118
  try {
120
119
  data = JSON.parse(responseText);
@@ -122,44 +121,24 @@ export class HttpClient {
122
121
  catch {
123
122
  // Not JSON
124
123
  }
125
- // Check for HTTP errors
124
+ // HTTP errors RFC 9457 problem+json
126
125
  if (!response.ok) {
127
- // Check for RequirementsNotMetException (412 with errors array)
128
126
  if (response.status === 412 && data && 'errors' in data && Array.isArray(data.errors)) {
129
127
  throw RequirementsNotMetException.fromResponse(data, response.status);
130
128
  }
131
- // General error handling
132
129
  let errorDetail;
133
130
  if (data && typeof data === 'object') {
134
- const apiData = data;
135
- if (apiData.error) {
136
- errorDetail = typeof apiData.error === 'object' ? apiData.error.message : String(apiData.error);
137
- }
138
- else if ('message' in data) {
139
- errorDetail = String(data.message);
140
- }
141
- else {
142
- errorDetail = JSON.stringify(data);
143
- }
131
+ errorDetail = data.detail || data.title || data.message || JSON.stringify(data);
144
132
  }
145
133
  else if (responseText) {
146
134
  errorDetail = responseText.slice(0, 500);
147
135
  }
148
136
  throw new InferenceError(response.status, errorDetail || 'Request failed', responseText);
149
137
  }
150
- // Handle 204 No Content (e.g., DELETE requests)
151
- if (response.status === 204) {
138
+ if (response.status === 204 || !responseText) {
152
139
  return undefined;
153
140
  }
154
- const apiResponse = data;
155
- if (!apiResponse?.success) {
156
- let errorMessage = apiResponse?.error?.message;
157
- if (!errorMessage) {
158
- errorMessage = `Request failed (success=false). Response: ${responseText.slice(0, 500)}`;
159
- }
160
- throw new InferenceError(response.status, errorMessage, responseText);
161
- }
162
- return (apiResponse.data ?? null);
141
+ return data;
163
142
  }
164
143
  /**
165
144
  * Get URL and headers for NDJSON streaming.