@inference-gateway/sdk 0.1.6 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## [0.3.0](https://github.com/inference-gateway/typescript-sdk/compare/v0.2.0...v0.3.0) (2025-02-02)
2
+
3
+ ### ✨ Features
4
+
5
+ * add streaming content functionality to InferenceGatewayClient and update README ([ba41d2d](https://github.com/inference-gateway/typescript-sdk/commit/ba41d2dc136b83372820af2aefa63969932e16f0))
6
+
7
+ ### 📚 Documentation
8
+
9
+ * **fix:** Update examples in README.md ([4e972fc](https://github.com/inference-gateway/typescript-sdk/commit/4e972fc2c577f41b0b443f1c87cde7561717b577))
10
+ * Update OpenAPI spec - download it from Inference-gateway ([9816b15](https://github.com/inference-gateway/typescript-sdk/commit/9816b151db6b48b04723f93b988daf83239a09df))
11
+
12
+ ## [0.2.0](https://github.com/inference-gateway/typescript-sdk/compare/v0.1.6...v0.2.0) (2025-01-28)
13
+
14
+ ### ✨ Features
15
+
16
+ * add listModelsByProvider method and update README with new model listing features ([a8d7cd9](https://github.com/inference-gateway/typescript-sdk/commit/a8d7cd9e9332f6455271f4d8f2832631b46d2c3d))
17
+
18
+ ### 📚 Documentation
19
+
20
+ * add Contributing section to README with reference to CONTRIBUTING.md ([322baae](https://github.com/inference-gateway/typescript-sdk/commit/322baae9110f270615597e647835ed22e4fdbc65))
21
+ * add CONTRIBUTING.md with guidelines for contributing to the project ([d36b08f](https://github.com/inference-gateway/typescript-sdk/commit/d36b08f1647500795d279dcd5612d5a81c9c4a74))
22
+ * **openapi:** Download the latest openapi spec from inference-gateway ([733ee1e](https://github.com/inference-gateway/typescript-sdk/commit/733ee1e57d9fc6669bb2ec0197db1c2c772a0283))
23
+
1
24
  ## [0.1.6](https://github.com/inference-gateway/typescript-sdk/compare/v0.1.5...v0.1.6) (2025-01-23)
2
25
 
3
26
  ### 🐛 Bug Fixes
package/README.md CHANGED
@@ -6,9 +6,12 @@ An SDK written in Typescript for the [Inference Gateway](https://github.com/eden
6
6
  - [Installation](#installation)
7
7
  - [Usage](#usage)
8
8
  - [Creating a Client](#creating-a-client)
9
- - [Listing Models](#listing-models)
9
+ - [Listing All Models](#listing-all-models)
10
+ - [List Models by Provider](#list-models-by-provider)
10
11
  - [Generating Content](#generating-content)
12
+ - [Streaming Content](#streaming-content)
11
13
  - [Health Check](#health-check)
14
+ - [Contributing](#contributing)
12
15
  - [License](#license)
13
16
 
14
17
  ## Installation
@@ -35,20 +38,21 @@ async function main() {
35
38
  models.forEach((providerModels) => {
36
39
  console.log(`Provider: ${providerModels.provider}`);
37
40
  providerModels.models.forEach((model) => {
38
- console.log(`Model: ${model.id}`);
41
+ console.log(`Model: ${model.name}`);
39
42
  });
40
43
  });
41
44
 
45
+ // Generate content
42
46
  const response = await client.generateContent({
43
47
  provider: Provider.Ollama,
44
48
  model: 'llama2',
45
49
  messages: [
46
50
  {
47
- role: 'system',
51
+ role: MessageRole.System,
48
52
  content: 'You are a helpful llama',
49
53
  },
50
54
  {
51
- role: 'user',
55
+ role: MessageRole.User,
52
56
  content: 'Tell me a joke',
53
57
  },
54
58
  ],
@@ -63,9 +67,9 @@ async function main() {
63
67
  main();
64
68
  ```
65
69
 
66
- ### Listing Models
70
+ ### Listing All Models
67
71
 
68
- To list available models, use the `listModels` method:
72
+ To list all available models from all providers, use the `listModels` method:
69
73
 
70
74
  ```typescript
71
75
  try {
@@ -73,7 +77,7 @@ try {
73
77
  models.forEach((providerModels) => {
74
78
  console.log(`Provider: ${providerModels.provider}`);
75
79
  providerModels.models.forEach((model) => {
76
- console.log(`Model: ${model.id}`);
80
+ console.log(`Model: ${model.name}`);
77
81
  });
78
82
  });
79
83
  } catch (error) {
@@ -81,22 +85,46 @@ try {
81
85
  }
82
86
  ```
83
87
 
88
+ ### List Models by Provider
89
+
90
+ To list all available models from a specific provider, use the `listModelsByProvider` method:
91
+
92
+ ```typescript
93
+ try {
94
+ const providerModels = await client.listModelsByProvider(Provider.OpenAI);
95
+ console.log(`Provider: ${providerModels.provider}`);
96
+ providerModels.models.forEach((model) => {
97
+ console.log(`Model: ${model.name}`);
98
+ });
99
+ } catch (error) {
100
+ console.error('Error:', error);
101
+ }
102
+ ```
103
+
84
104
  ### Generating Content
85
105
 
86
106
  To generate content using a model, use the `generateContent` method:
87
107
 
88
108
  ```typescript
89
- try {
109
+ import {
110
+ InferenceGatewayClient,
111
+ Message,
112
+ MessageRole,
113
+ Provider,
114
+ } from '@inference-gateway/sdk';
115
+
116
+ const client = new InferenceGatewayClient('http://localhost:8080');
117
+
90
118
  const response = await client.generateContent({
91
119
  provider: Provider.Ollama,
92
120
  model: 'llama2',
93
121
  messages: [
94
122
  {
95
- role: 'system',
123
+ role: MessageRole.System,
96
124
  content: 'You are a helpful llama',
97
125
  },
98
126
  {
99
- role: 'user',
127
+ role: MessageRole.User,
100
128
  content: 'Tell me a joke',
101
129
  },
102
130
  ],
@@ -109,6 +137,32 @@ try {
109
137
  }
110
138
  ```
111
139
 
140
+ ### Streaming Content
141
+
142
+ To stream content using a model, use the `streamContent` method:
143
+
144
+ ```typescript
145
+ const client = new InferenceGatewayClient('http://localhost:8080');
146
+
147
+ await client.generateContentStream(
148
+ {
149
+ provider: Provider.Groq,
150
+ model: 'deepseek-r1-distill-llama-70b',
151
+ messages: [
152
+ {
153
+ role: MessageRole.User,
154
+ content: 'Tell me a story',
155
+ },
156
+ ],
157
+ },
158
+ {
159
+ onMessageStart: (role) => console.log('Message started:', role),
160
+ onContentDelta: (content) => process.stdout.write(content),
161
+ onStreamEnd: () => console.log('\nStream completed'),
162
+ }
163
+ );
164
+ ```
165
+
112
166
  ### Health Check
113
167
 
114
168
  To check if the Inference Gateway is running, use the `healthCheck` method:
@@ -122,6 +176,10 @@ try {
122
176
  }
123
177
  ```
124
178
 
179
+ ## Contributing
180
+
181
+ Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file for information about how to get involved. We welcome issues, questions, and pull requests.
182
+
125
183
  ## License
126
184
 
127
185
  This SDK is distributed under the MIT License, see [LICENSE](LICENSE) for more information.
@@ -1,10 +1,12 @@
1
- import { GenerateContentRequest, GenerateContentResponse, ProviderModels } from './types';
1
+ import { GenerateContentOptions, GenerateContentRequest, GenerateContentResponse, Provider, ProviderModels } from './types';
2
2
  export declare class InferenceGatewayClient {
3
3
  private baseUrl;
4
4
  private authToken?;
5
5
  constructor(baseUrl: string, authToken?: string);
6
6
  private request;
7
7
  listModels(): Promise<ProviderModels[]>;
8
+ listModelsByProvider(provider: Provider): Promise<ProviderModels>;
8
9
  generateContent(params: GenerateContentRequest): Promise<GenerateContentResponse>;
10
+ generateContentStream(params: GenerateContentRequest, options?: GenerateContentOptions): Promise<void>;
9
11
  healthCheck(): Promise<boolean>;
10
12
  }
@@ -29,6 +29,9 @@ class InferenceGatewayClient {
29
29
  async listModels() {
30
30
  return this.request('/llms');
31
31
  }
32
+ async listModelsByProvider(provider) {
33
+ return this.request(`/llms/${provider}`);
34
+ }
32
35
  async generateContent(params) {
33
36
  return this.request(`/llms/${params.provider}/generate`, {
34
37
  method: 'POST',
@@ -38,6 +41,64 @@ class InferenceGatewayClient {
38
41
  }),
39
42
  });
40
43
  }
44
+ async generateContentStream(params, options) {
45
+ const response = await fetch(`${this.baseUrl}/llms/${params.provider}/generate`, {
46
+ method: 'POST',
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ ...(this.authToken && { Authorization: `Bearer ${this.authToken}` }),
50
+ },
51
+ body: JSON.stringify({
52
+ model: params.model,
53
+ messages: params.messages,
54
+ stream: true,
55
+ ssevents: true,
56
+ }),
57
+ });
58
+ if (!response.ok) {
59
+ const error = await response.json();
60
+ throw new Error(error.error || `HTTP error! status: ${response.status}`);
61
+ }
62
+ const reader = response.body?.getReader();
63
+ if (!reader)
64
+ throw new Error('Response body is not readable');
65
+ const decoder = new TextDecoder();
66
+ while (true) {
67
+ const { done, value } = await reader.read();
68
+ if (done)
69
+ break;
70
+ const events = decoder.decode(value).split('\n\n');
71
+ for (const event of events) {
72
+ if (!event.trim())
73
+ continue;
74
+ const [eventType, ...data] = event.split('\n');
75
+ const eventData = JSON.parse(data.join('\n').replace('data: ', ''));
76
+ switch (eventType.replace('event: ', '')) {
77
+ case 'message-start':
78
+ options?.onMessageStart?.(eventData.role);
79
+ break;
80
+ case 'stream-start':
81
+ options?.onStreamStart?.();
82
+ break;
83
+ case 'content-start':
84
+ options?.onContentStart?.();
85
+ break;
86
+ case 'content-delta':
87
+ options?.onContentDelta?.(eventData.content);
88
+ break;
89
+ case 'content-end':
90
+ options?.onContentEnd?.();
91
+ break;
92
+ case 'message-end':
93
+ options?.onMessageEnd?.();
94
+ break;
95
+ case 'stream-end':
96
+ options?.onStreamEnd?.();
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ }
41
102
  async healthCheck() {
42
103
  try {
43
104
  await this.request('/health');
@@ -7,15 +7,17 @@ export declare enum Provider {
7
7
  Cohere = "cohere",
8
8
  Anthropic = "anthropic"
9
9
  }
10
+ export declare enum MessageRole {
11
+ System = "system",
12
+ User = "user",
13
+ Assistant = "assistant"
14
+ }
10
15
  export interface Message {
11
- role: 'system' | 'user' | 'assistant';
16
+ role: MessageRole;
12
17
  content: string;
13
18
  }
14
19
  export interface Model {
15
- id: string;
16
- object: string;
17
- owned_by: string;
18
- created: number;
20
+ name: string;
19
21
  }
20
22
  export interface ProviderModels {
21
23
  provider: Provider;
@@ -34,3 +36,12 @@ export interface GenerateContentResponse {
34
36
  content: string;
35
37
  };
36
38
  }
39
+ export interface GenerateContentOptions {
40
+ onMessageStart?: (role: string) => void;
41
+ onStreamStart?: () => void;
42
+ onContentStart?: () => void;
43
+ onContentDelta?: (content: string) => void;
44
+ onContentEnd?: () => void;
45
+ onMessageEnd?: () => void;
46
+ onStreamEnd?: () => void;
47
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Provider = void 0;
3
+ exports.MessageRole = exports.Provider = void 0;
4
4
  var Provider;
5
5
  (function (Provider) {
6
6
  Provider["Ollama"] = "ollama";
@@ -11,3 +11,9 @@ var Provider;
11
11
  Provider["Cohere"] = "cohere";
12
12
  Provider["Anthropic"] = "anthropic";
13
13
  })(Provider || (exports.Provider = Provider = {}));
14
+ var MessageRole;
15
+ (function (MessageRole) {
16
+ MessageRole["System"] = "system";
17
+ MessageRole["User"] = "user";
18
+ MessageRole["Assistant"] = "assistant";
19
+ })(MessageRole || (exports.MessageRole = MessageRole = {}));
@@ -16,10 +16,7 @@ describe('InferenceGatewayClient', () => {
16
16
  provider: types_1.Provider.Ollama,
17
17
  models: [
18
18
  {
19
- id: 'llama2',
20
- object: 'model',
21
- owned_by: 'ollama',
22
- created: 1234567890,
19
+ name: 'llama2',
23
20
  },
24
21
  ],
25
22
  },
@@ -35,20 +32,50 @@ describe('InferenceGatewayClient', () => {
35
32
  }));
36
33
  });
37
34
  });
35
+ describe('listModelsByProvider', () => {
36
+ it('should fetch models for a specific provider', async () => {
37
+ const mockResponse = {
38
+ provider: types_1.Provider.OpenAI,
39
+ models: [
40
+ {
41
+ name: 'gpt-4',
42
+ },
43
+ ],
44
+ };
45
+ global.fetch.mockResolvedValueOnce({
46
+ ok: true,
47
+ json: () => Promise.resolve(mockResponse),
48
+ });
49
+ const result = await client.listModelsByProvider(types_1.Provider.OpenAI);
50
+ expect(result).toEqual(mockResponse);
51
+ expect(global.fetch).toHaveBeenCalledWith(`${mockBaseUrl}/llms/${types_1.Provider.OpenAI}`, expect.objectContaining({
52
+ headers: expect.any(Headers),
53
+ }));
54
+ });
55
+ it('should throw error when provider request fails', async () => {
56
+ const errorMessage = 'Provider not found';
57
+ global.fetch.mockResolvedValueOnce({
58
+ ok: false,
59
+ status: 404,
60
+ json: () => Promise.resolve({ error: errorMessage }),
61
+ });
62
+ await expect(client.listModelsByProvider(types_1.Provider.OpenAI)).rejects.toThrow(errorMessage);
63
+ });
64
+ });
38
65
  describe('generateContent', () => {
39
66
  it('should generate content with the specified provider', async () => {
40
67
  const mockRequest = {
41
68
  provider: types_1.Provider.Ollama,
42
69
  model: 'llama2',
43
70
  messages: [
44
- { role: 'system', content: 'You are a helpful assistant' },
45
- { role: 'user', content: 'Hello' },
71
+ { role: types_1.MessageRole.System, content: 'You are a helpful assistant' },
72
+ { role: types_1.MessageRole.User, content: 'Hello' },
46
73
  ],
47
74
  };
48
75
  const mockResponse = {
49
76
  provider: types_1.Provider.Ollama,
50
77
  response: {
51
- role: 'assistant',
78
+ role: types_1.MessageRole.Assistant,
52
79
  model: 'llama2',
53
80
  content: 'Hi there!',
54
81
  },
@@ -95,4 +122,109 @@ describe('InferenceGatewayClient', () => {
95
122
  await expect(client.listModels()).rejects.toThrow(errorMessage);
96
123
  });
97
124
  });
125
+ describe('generateContentStream', () => {
126
+ it('should handle SSE events correctly', async () => {
127
+ const mockRequest = {
128
+ provider: types_1.Provider.Ollama,
129
+ model: 'llama2',
130
+ messages: [
131
+ { role: types_1.MessageRole.System, content: 'You are a helpful assistant' },
132
+ { role: types_1.MessageRole.User, content: 'Hello' },
133
+ ],
134
+ };
135
+ const mockStream = new TransformStream();
136
+ const writer = mockStream.writable.getWriter();
137
+ const encoder = new TextEncoder();
138
+ global.fetch.mockResolvedValueOnce({
139
+ ok: true,
140
+ body: mockStream.readable,
141
+ });
142
+ const callbacks = {
143
+ onMessageStart: jest.fn(),
144
+ onStreamStart: jest.fn(),
145
+ onContentStart: jest.fn(),
146
+ onContentDelta: jest.fn(),
147
+ onContentEnd: jest.fn(),
148
+ onMessageEnd: jest.fn(),
149
+ onStreamEnd: jest.fn(),
150
+ };
151
+ const streamPromise = client.generateContentStream(mockRequest, callbacks);
152
+ await writer.write(encoder.encode('event: message-start\ndata: {"role": "assistant"}\n\n' +
153
+ 'event: stream-start\ndata: {}\n\n' +
154
+ 'event: content-start\ndata: {}\n\n' +
155
+ 'event: content-delta\ndata: {"content": "Hello"}\n\n' +
156
+ 'event: content-delta\ndata: {"content": " there!"}\n\n' +
157
+ 'event: content-end\ndata: {}\n\n' +
158
+ 'event: message-end\ndata: {}\n\n' +
159
+ 'event: stream-end\ndata: {}\n\n'));
160
+ await writer.close();
161
+ await streamPromise;
162
+ expect(callbacks.onMessageStart).toHaveBeenCalledWith('assistant');
163
+ expect(callbacks.onStreamStart).toHaveBeenCalledTimes(1);
164
+ expect(callbacks.onContentStart).toHaveBeenCalledTimes(1);
165
+ expect(callbacks.onContentDelta).toHaveBeenCalledWith('Hello');
166
+ expect(callbacks.onContentDelta).toHaveBeenCalledWith(' there!');
167
+ expect(callbacks.onContentEnd).toHaveBeenCalledTimes(1);
168
+ expect(callbacks.onMessageEnd).toHaveBeenCalledTimes(1);
169
+ expect(callbacks.onStreamEnd).toHaveBeenCalledTimes(1);
170
+ expect(global.fetch).toHaveBeenCalledWith(`${mockBaseUrl}/llms/${mockRequest.provider}/generate`, expect.objectContaining({
171
+ method: 'POST',
172
+ body: JSON.stringify({
173
+ model: mockRequest.model,
174
+ messages: mockRequest.messages,
175
+ stream: true,
176
+ ssevents: true,
177
+ }),
178
+ }));
179
+ });
180
+ it('should handle errors in the stream response', async () => {
181
+ const mockRequest = {
182
+ provider: types_1.Provider.Ollama,
183
+ model: 'llama2',
184
+ messages: [{ role: types_1.MessageRole.User, content: 'Hello' }],
185
+ };
186
+ global.fetch.mockResolvedValueOnce({
187
+ ok: false,
188
+ status: 400,
189
+ json: () => Promise.resolve({ error: 'Bad Request' }),
190
+ });
191
+ await expect(client.generateContentStream(mockRequest, {})).rejects.toThrow('Bad Request');
192
+ });
193
+ it('should handle non-readable response body', async () => {
194
+ const mockRequest = {
195
+ provider: types_1.Provider.Ollama,
196
+ model: 'llama2',
197
+ messages: [{ role: types_1.MessageRole.User, content: 'Hello' }],
198
+ };
199
+ global.fetch.mockResolvedValueOnce({
200
+ ok: true,
201
+ body: null,
202
+ });
203
+ await expect(client.generateContentStream(mockRequest, {})).rejects.toThrow('Response body is not readable');
204
+ });
205
+ it('should handle empty events in the stream', async () => {
206
+ const mockRequest = {
207
+ provider: types_1.Provider.Ollama,
208
+ model: 'llama2',
209
+ messages: [{ role: types_1.MessageRole.User, content: 'Hello' }],
210
+ };
211
+ const mockStream = new TransformStream();
212
+ const writer = mockStream.writable.getWriter();
213
+ const encoder = new TextEncoder();
214
+ global.fetch.mockResolvedValueOnce({
215
+ ok: true,
216
+ body: mockStream.readable,
217
+ });
218
+ const callbacks = {
219
+ onContentDelta: jest.fn(),
220
+ };
221
+ const streamPromise = client.generateContentStream(mockRequest, callbacks);
222
+ await writer.write(encoder.encode('\n\n'));
223
+ await writer.write(encoder.encode('event: content-delta\ndata: {"content": "Hello"}\n\n'));
224
+ await writer.close();
225
+ await streamPromise;
226
+ expect(callbacks.onContentDelta).toHaveBeenCalledTimes(1);
227
+ expect(callbacks.onContentDelta).toHaveBeenCalledWith('Hello');
228
+ });
229
+ });
98
230
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inference-gateway/sdk",
3
- "version": "0.1.6",
3
+ "version": "0.3.0",
4
4
  "description": "An SDK written in Typescript for the [Inference Gateway](https://github.com/inference-gateway/inference-gateway).",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",