@ai-sdk/togetherai 2.0.19 → 2.0.20

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,5 +1,15 @@
1
1
  # @ai-sdk/togetherai
2
2
 
3
+ ## 2.0.20
4
+
5
+ ### Patch Changes
6
+
7
+ - 4de5a1d: chore: excluded tests from src folder in npm package
8
+ - Updated dependencies [4de5a1d]
9
+ - @ai-sdk/openai-compatible@2.0.18
10
+ - @ai-sdk/provider@3.0.5
11
+ - @ai-sdk/provider-utils@4.0.9
12
+
3
13
  ## 2.0.19
4
14
 
5
15
  ### Patch Changes
package/dist/index.js CHANGED
@@ -272,7 +272,7 @@ var togetheraiImageProviderOptionsSchema = (0, import_provider_utils4.lazySchema
272
272
  );
273
273
 
274
274
  // src/version.ts
275
- var VERSION = true ? "2.0.19" : "0.0.0-test";
275
+ var VERSION = true ? "2.0.20" : "0.0.0-test";
276
276
 
277
277
  // src/togetherai-provider.ts
278
278
  function createTogetherAI(options = {}) {
package/dist/index.mjs CHANGED
@@ -267,7 +267,7 @@ var togetheraiImageProviderOptionsSchema = lazySchema3(
267
267
  );
268
268
 
269
269
  // src/version.ts
270
- var VERSION = true ? "2.0.19" : "0.0.0-test";
270
+ var VERSION = true ? "2.0.20" : "0.0.0-test";
271
271
 
272
272
  // src/togetherai-provider.ts
273
273
  function createTogetherAI(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/togetherai",
3
- "version": "2.0.19",
3
+ "version": "2.0.20",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -10,6 +10,10 @@
10
10
  "dist/**/*",
11
11
  "docs/**/*",
12
12
  "src",
13
+ "!src/**/*.test.ts",
14
+ "!src/**/*.test-d.ts",
15
+ "!src/**/__snapshots__",
16
+ "!src/**/__fixtures__",
13
17
  "CHANGELOG.md",
14
18
  "README.md"
15
19
  ],
@@ -25,16 +29,16 @@
25
29
  }
26
30
  },
27
31
  "dependencies": {
28
- "@ai-sdk/openai-compatible": "2.0.17",
29
- "@ai-sdk/provider": "3.0.4",
30
- "@ai-sdk/provider-utils": "4.0.8"
32
+ "@ai-sdk/openai-compatible": "2.0.18",
33
+ "@ai-sdk/provider": "3.0.5",
34
+ "@ai-sdk/provider-utils": "4.0.9"
31
35
  },
32
36
  "devDependencies": {
33
37
  "@types/node": "20.17.24",
34
38
  "tsup": "^8",
35
39
  "typescript": "5.8.3",
36
40
  "zod": "3.25.76",
37
- "@ai-sdk/test-server": "1.0.2",
41
+ "@ai-sdk/test-server": "1.0.3",
38
42
  "@vercel/ai-tsconfig": "0.0.0"
39
43
  },
40
44
  "peerDependencies": {
@@ -1,22 +0,0 @@
1
- {
2
- "id": "oGs6Zt9-62bZhn-99529372487b1b0a",
3
- "object": "rerank",
4
- "model": "Salesforce/Llama-Rank-v1",
5
- "results": [
6
- {
7
- "index": 0,
8
- "relevance_score": 0.6475887154399037,
9
- "document": {}
10
- },
11
- {
12
- "index": 5,
13
- "relevance_score": 0.6323295373206566,
14
- "document": {}
15
- }
16
- ],
17
- "usage": {
18
- "prompt_tokens": 2966,
19
- "completion_tokens": 0,
20
- "total_tokens": 2966
21
- }
22
- }
@@ -1,245 +0,0 @@
1
- import { createTestServer } from '@ai-sdk/test-server/with-vitest';
2
- import fs from 'node:fs';
3
- import { beforeEach, describe, expect, it } from 'vitest';
4
- import { createTogetherAI } from '../togetherai-provider';
5
- import { TogetherAIRerankingOptions } from './togetherai-reranking-options';
6
-
7
- const provider = createTogetherAI({ apiKey: 'test-api-key' });
8
- const model = provider.rerankingModel('Salesforce/Llama-Rank-v1');
9
-
10
- describe('doRerank', () => {
11
- const server = createTestServer({
12
- 'https://api.together.xyz/v1/rerank': {},
13
- });
14
-
15
- function prepareJsonFixtureResponse(filename: string) {
16
- server.urls['https://api.together.xyz/v1/rerank'].response = {
17
- type: 'json-value',
18
- body: JSON.parse(
19
- fs.readFileSync(`src/reranking/__fixtures__/${filename}.json`, 'utf8'),
20
- ),
21
- };
22
- return;
23
- }
24
-
25
- describe('json documents', () => {
26
- let result: Awaited<ReturnType<typeof model.doRerank>>;
27
-
28
- beforeEach(async () => {
29
- prepareJsonFixtureResponse('togetherai-reranking.1');
30
-
31
- result = await model.doRerank({
32
- documents: {
33
- type: 'object',
34
- values: [
35
- { example: 'sunny day at the beach' },
36
- { example: 'rainy day in the city' },
37
- ],
38
- },
39
- query: 'rainy day',
40
- topN: 2,
41
- providerOptions: {
42
- togetherai: {
43
- rankFields: ['example'],
44
- } satisfies TogetherAIRerankingOptions,
45
- },
46
- });
47
- });
48
-
49
- it('should send request with stringified json documents', async () => {
50
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
51
- {
52
- "documents": [
53
- {
54
- "example": "sunny day at the beach",
55
- },
56
- {
57
- "example": "rainy day in the city",
58
- },
59
- ],
60
- "model": "Salesforce/Llama-Rank-v1",
61
- "query": "rainy day",
62
- "rank_fields": [
63
- "example",
64
- ],
65
- "return_documents": false,
66
- "top_n": 2,
67
- }
68
- `);
69
- });
70
-
71
- it('should send request with the correct headers', async () => {
72
- expect(server.calls[0].requestHeaders).toMatchInlineSnapshot(`
73
- {
74
- "authorization": "Bearer test-api-key",
75
- "content-type": "application/json",
76
- }
77
- `);
78
- });
79
-
80
- it('should return result with warnings', async () => {
81
- expect(result.warnings).toMatchInlineSnapshot(`undefined`);
82
- });
83
-
84
- it('should return result with the correct ranking', async () => {
85
- expect(result.ranking).toMatchInlineSnapshot(`
86
- [
87
- {
88
- "index": 0,
89
- "relevanceScore": 0.6475887154399037,
90
- },
91
- {
92
- "index": 5,
93
- "relevanceScore": 0.6323295373206566,
94
- },
95
- ]
96
- `);
97
- });
98
-
99
- it('should not return provider metadata (use response body instead)', async () => {
100
- expect(result.providerMetadata).toMatchInlineSnapshot(`undefined`);
101
- });
102
-
103
- it('should return result with the correct response', async () => {
104
- expect(result.response).toMatchInlineSnapshot(`
105
- {
106
- "body": {
107
- "id": "oGs6Zt9-62bZhn-99529372487b1b0a",
108
- "model": "Salesforce/Llama-Rank-v1",
109
- "object": "rerank",
110
- "results": [
111
- {
112
- "document": {},
113
- "index": 0,
114
- "relevance_score": 0.6475887154399037,
115
- },
116
- {
117
- "document": {},
118
- "index": 5,
119
- "relevance_score": 0.6323295373206566,
120
- },
121
- ],
122
- "usage": {
123
- "completion_tokens": 0,
124
- "prompt_tokens": 2966,
125
- "total_tokens": 2966,
126
- },
127
- },
128
- "headers": {
129
- "content-length": "304",
130
- "content-type": "application/json",
131
- },
132
- "id": "oGs6Zt9-62bZhn-99529372487b1b0a",
133
- "modelId": "Salesforce/Llama-Rank-v1",
134
- }
135
- `);
136
- });
137
- });
138
-
139
- describe('text documents', () => {
140
- let result: Awaited<ReturnType<typeof model.doRerank>>;
141
-
142
- beforeEach(async () => {
143
- prepareJsonFixtureResponse('togetherai-reranking.1');
144
-
145
- result = await model.doRerank({
146
- documents: {
147
- type: 'text',
148
- values: ['sunny day at the beach', 'rainy day in the city'],
149
- },
150
- query: 'rainy day',
151
- topN: 2,
152
- providerOptions: {
153
- togetherai: {
154
- rankFields: ['example'],
155
- } satisfies TogetherAIRerankingOptions,
156
- },
157
- });
158
- });
159
-
160
- it('should send request with text documents', async () => {
161
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
162
- {
163
- "documents": [
164
- "sunny day at the beach",
165
- "rainy day in the city",
166
- ],
167
- "model": "Salesforce/Llama-Rank-v1",
168
- "query": "rainy day",
169
- "rank_fields": [
170
- "example",
171
- ],
172
- "return_documents": false,
173
- "top_n": 2,
174
- }
175
- `);
176
- });
177
-
178
- it('should send request with the correct headers', async () => {
179
- expect(server.calls[0].requestHeaders).toMatchInlineSnapshot(`
180
- {
181
- "authorization": "Bearer test-api-key",
182
- "content-type": "application/json",
183
- }
184
- `);
185
- });
186
-
187
- it('should return result without warnings', async () => {
188
- expect(result.warnings).toMatchInlineSnapshot(`undefined`);
189
- });
190
-
191
- it('should return result with the correct ranking', async () => {
192
- expect(result.ranking).toMatchInlineSnapshot(`
193
- [
194
- {
195
- "index": 0,
196
- "relevanceScore": 0.6475887154399037,
197
- },
198
- {
199
- "index": 5,
200
- "relevanceScore": 0.6323295373206566,
201
- },
202
- ]
203
- `);
204
- });
205
-
206
- it('should not return provider metadata (use response body instead)', async () => {
207
- expect(result.providerMetadata).toMatchInlineSnapshot(`undefined`);
208
- });
209
-
210
- it('should return result with the correct response', async () => {
211
- expect(result.response).toMatchInlineSnapshot(`
212
- {
213
- "body": {
214
- "id": "oGs6Zt9-62bZhn-99529372487b1b0a",
215
- "model": "Salesforce/Llama-Rank-v1",
216
- "object": "rerank",
217
- "results": [
218
- {
219
- "document": {},
220
- "index": 0,
221
- "relevance_score": 0.6475887154399037,
222
- },
223
- {
224
- "document": {},
225
- "index": 5,
226
- "relevance_score": 0.6323295373206566,
227
- },
228
- ],
229
- "usage": {
230
- "completion_tokens": 0,
231
- "prompt_tokens": 2966,
232
- "total_tokens": 2966,
233
- },
234
- },
235
- "headers": {
236
- "content-length": "304",
237
- "content-type": "application/json",
238
- },
239
- "id": "oGs6Zt9-62bZhn-99529372487b1b0a",
240
- "modelId": "Salesforce/Llama-Rank-v1",
241
- }
242
- `);
243
- });
244
- });
245
- });
@@ -1,488 +0,0 @@
1
- import { FetchFunction } from '@ai-sdk/provider-utils';
2
- import { createTestServer } from '@ai-sdk/test-server/with-vitest';
3
- import { describe, expect, it } from 'vitest';
4
- import { TogetherAIImageModel } from './togetherai-image-model';
5
-
6
- const prompt = 'A cute baby sea otter';
7
-
8
- function createBasicModel({
9
- headers,
10
- fetch,
11
- currentDate,
12
- }: {
13
- headers?: () => Record<string, string>;
14
- fetch?: FetchFunction;
15
- currentDate?: () => Date;
16
- } = {}) {
17
- return new TogetherAIImageModel('stabilityai/stable-diffusion-xl', {
18
- provider: 'togetherai',
19
- baseURL: 'https://api.example.com',
20
- headers: headers ?? (() => ({ 'api-key': 'test-key' })),
21
- fetch,
22
- _internal: {
23
- currentDate,
24
- },
25
- });
26
- }
27
-
28
- const server = createTestServer({
29
- 'https://api.example.com/*': {
30
- response: {
31
- type: 'json-value',
32
- body: {
33
- id: 'test-id',
34
- data: [{ index: 0, b64_json: 'test-base64-content' }],
35
- model: 'stabilityai/stable-diffusion-xl',
36
- object: 'list',
37
- },
38
- },
39
- },
40
- });
41
-
42
- describe('doGenerate', () => {
43
- it('should pass the correct parameters including size and seed', async () => {
44
- const model = createBasicModel();
45
-
46
- await model.doGenerate({
47
- prompt,
48
- files: undefined,
49
- mask: undefined,
50
- n: 1,
51
- size: '1024x1024',
52
- seed: 42,
53
- providerOptions: { togetherai: { additional_param: 'value' } },
54
- aspectRatio: undefined,
55
- });
56
-
57
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
58
- model: 'stabilityai/stable-diffusion-xl',
59
- prompt,
60
- seed: 42,
61
- width: 1024,
62
- height: 1024,
63
- response_format: 'base64',
64
- additional_param: 'value',
65
- });
66
- });
67
-
68
- it('should include n parameter when requesting multiple images', async () => {
69
- const model = createBasicModel();
70
-
71
- await model.doGenerate({
72
- prompt,
73
- files: undefined,
74
- mask: undefined,
75
- n: 3,
76
- size: '1024x1024',
77
- seed: 42,
78
- providerOptions: {},
79
- aspectRatio: undefined,
80
- });
81
-
82
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
83
- model: 'stabilityai/stable-diffusion-xl',
84
- prompt,
85
- seed: 42,
86
- n: 3,
87
- width: 1024,
88
- height: 1024,
89
- response_format: 'base64',
90
- });
91
- });
92
-
93
- it('should call the correct url', async () => {
94
- const model = createBasicModel();
95
-
96
- await model.doGenerate({
97
- prompt,
98
- files: undefined,
99
- mask: undefined,
100
- n: 1,
101
- size: '1024x1024',
102
- seed: 42,
103
- providerOptions: {},
104
- aspectRatio: undefined,
105
- });
106
-
107
- expect(server.calls[0].requestMethod).toStrictEqual('POST');
108
- expect(server.calls[0].requestUrl).toStrictEqual(
109
- 'https://api.example.com/images/generations',
110
- );
111
- });
112
-
113
- it('should pass headers', async () => {
114
- const modelWithHeaders = createBasicModel({
115
- headers: () => ({
116
- 'Custom-Provider-Header': 'provider-header-value',
117
- }),
118
- });
119
-
120
- await modelWithHeaders.doGenerate({
121
- prompt,
122
- files: undefined,
123
- mask: undefined,
124
- n: 1,
125
- size: undefined,
126
- seed: undefined,
127
- providerOptions: {},
128
- aspectRatio: undefined,
129
- headers: {
130
- 'Custom-Request-Header': 'request-header-value',
131
- },
132
- });
133
-
134
- expect(server.calls[0].requestHeaders).toStrictEqual({
135
- 'content-type': 'application/json',
136
- 'custom-provider-header': 'provider-header-value',
137
- 'custom-request-header': 'request-header-value',
138
- });
139
- });
140
-
141
- it('should handle API errors', async () => {
142
- server.urls['https://api.example.com/*'].response = {
143
- type: 'error',
144
- status: 400,
145
- body: JSON.stringify({
146
- error: {
147
- message: 'Bad Request',
148
- },
149
- }),
150
- };
151
-
152
- const model = createBasicModel();
153
- await expect(
154
- model.doGenerate({
155
- prompt,
156
- files: undefined,
157
- mask: undefined,
158
- n: 1,
159
- size: undefined,
160
- seed: undefined,
161
- providerOptions: {},
162
- aspectRatio: undefined,
163
- }),
164
- ).rejects.toMatchObject({
165
- message: 'Bad Request',
166
- });
167
- });
168
-
169
- describe('warnings', () => {
170
- it('should return aspectRatio warning when aspectRatio is provided', async () => {
171
- const model = createBasicModel();
172
-
173
- const result = await model.doGenerate({
174
- prompt,
175
- files: undefined,
176
- mask: undefined,
177
- n: 1,
178
- size: '1024x1024',
179
- aspectRatio: '1:1',
180
- seed: 123,
181
- providerOptions: {},
182
- });
183
-
184
- expect(result.warnings).toMatchInlineSnapshot(`
185
- [
186
- {
187
- "details": "This model does not support the \`aspectRatio\` option. Use \`size\` instead.",
188
- "feature": "aspectRatio",
189
- "type": "unsupported",
190
- },
191
- ]
192
- `);
193
- });
194
- });
195
-
196
- it('should respect the abort signal', async () => {
197
- const model = createBasicModel();
198
- const controller = new AbortController();
199
-
200
- const generatePromise = model.doGenerate({
201
- prompt,
202
- files: undefined,
203
- mask: undefined,
204
- n: 1,
205
- size: undefined,
206
- seed: undefined,
207
- providerOptions: {},
208
- aspectRatio: undefined,
209
- abortSignal: controller.signal,
210
- });
211
-
212
- controller.abort();
213
-
214
- await expect(generatePromise).rejects.toThrow('This operation was aborted');
215
- });
216
-
217
- describe('response metadata', () => {
218
- it('should include timestamp, headers and modelId in response', async () => {
219
- const testDate = new Date('2024-01-01T00:00:00Z');
220
- const model = createBasicModel({
221
- currentDate: () => testDate,
222
- });
223
-
224
- const result = await model.doGenerate({
225
- prompt,
226
- files: undefined,
227
- mask: undefined,
228
- n: 1,
229
- size: undefined,
230
- seed: undefined,
231
- providerOptions: {},
232
- aspectRatio: undefined,
233
- });
234
-
235
- expect(result.response).toStrictEqual({
236
- timestamp: testDate,
237
- modelId: 'stabilityai/stable-diffusion-xl',
238
- headers: expect.any(Object),
239
- });
240
- });
241
-
242
- it('should include response headers from API call', async () => {
243
- server.urls['https://api.example.com/*'].response = {
244
- type: 'json-value',
245
- body: {
246
- id: 'test-id',
247
- data: [{ index: 0, b64_json: 'test-base64-content' }],
248
- model: 'stabilityai/stable-diffusion-xl',
249
- object: 'list',
250
- },
251
- headers: {
252
- 'x-request-id': 'test-request-id',
253
- 'content-length': '128',
254
- },
255
- };
256
-
257
- const model = createBasicModel();
258
- const result = await model.doGenerate({
259
- prompt,
260
- files: undefined,
261
- mask: undefined,
262
- n: 1,
263
- size: undefined,
264
- seed: undefined,
265
- providerOptions: {},
266
- aspectRatio: undefined,
267
- });
268
-
269
- expect(result.response.headers).toStrictEqual({
270
- 'x-request-id': 'test-request-id',
271
- 'content-type': 'application/json',
272
- 'content-length': '128',
273
- });
274
- });
275
- });
276
- });
277
-
278
- describe('constructor', () => {
279
- it('should expose correct provider and model information', () => {
280
- const model = createBasicModel();
281
-
282
- expect(model.provider).toBe('togetherai');
283
- expect(model.modelId).toBe('stabilityai/stable-diffusion-xl');
284
- expect(model.specificationVersion).toBe('v3');
285
- expect(model.maxImagesPerCall).toBe(1);
286
- });
287
- });
288
-
289
- describe('Image Editing', () => {
290
- const server = createTestServer({
291
- 'https://api.example.com/*': {
292
- response: {
293
- type: 'json-value',
294
- body: {
295
- id: 'test-id',
296
- data: [{ index: 0, b64_json: 'test-base64-content' }],
297
- model: 'black-forest-labs/FLUX.1-kontext-pro',
298
- object: 'list',
299
- },
300
- },
301
- },
302
- });
303
-
304
- it('should send image_url when URL file is provided', async () => {
305
- const model = createBasicModel();
306
-
307
- await model.doGenerate({
308
- prompt: 'Make the shirt yellow',
309
- files: [
310
- {
311
- type: 'url',
312
- url: 'https://example.com/input.jpg',
313
- },
314
- ],
315
- mask: undefined,
316
- n: 1,
317
- size: undefined,
318
- aspectRatio: undefined,
319
- seed: undefined,
320
- providerOptions: {},
321
- });
322
-
323
- const requestBody = await server.calls[0].requestBodyJson;
324
- expect(requestBody).toMatchInlineSnapshot(`
325
- {
326
- "image_url": "https://example.com/input.jpg",
327
- "model": "stabilityai/stable-diffusion-xl",
328
- "prompt": "Make the shirt yellow",
329
- "response_format": "base64",
330
- }
331
- `);
332
- });
333
-
334
- it('should convert Uint8Array file to data URI', async () => {
335
- const model = createBasicModel();
336
- const testImageData = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
337
-
338
- await model.doGenerate({
339
- prompt: 'Transform this image',
340
- files: [
341
- {
342
- type: 'file',
343
- data: testImageData,
344
- mediaType: 'image/png',
345
- },
346
- ],
347
- mask: undefined,
348
- n: 1,
349
- size: undefined,
350
- aspectRatio: undefined,
351
- seed: undefined,
352
- providerOptions: {},
353
- });
354
-
355
- const requestBody = await server.calls[0].requestBodyJson;
356
- expect(requestBody.image_url).toMatch(/^data:image\/png;base64,/);
357
- expect(requestBody.prompt).toBe('Transform this image');
358
- });
359
-
360
- it('should convert file with base64 string data to data URI', async () => {
361
- const model = createBasicModel();
362
-
363
- await model.doGenerate({
364
- prompt: 'Edit this',
365
- files: [
366
- {
367
- type: 'file',
368
- data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
369
- mediaType: 'image/png',
370
- },
371
- ],
372
- mask: undefined,
373
- n: 1,
374
- size: undefined,
375
- aspectRatio: undefined,
376
- seed: undefined,
377
- providerOptions: {},
378
- });
379
-
380
- const requestBody = await server.calls[0].requestBodyJson;
381
- expect(requestBody.image_url).toBe(
382
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
383
- );
384
- });
385
-
386
- it('should throw error when mask is provided', async () => {
387
- const model = createBasicModel();
388
-
389
- await expect(
390
- model.doGenerate({
391
- prompt: 'Inpaint this area',
392
- files: [
393
- {
394
- type: 'url',
395
- url: 'https://example.com/input.jpg',
396
- },
397
- ],
398
- mask: {
399
- type: 'url',
400
- url: 'https://example.com/mask.png',
401
- },
402
- n: 1,
403
- size: undefined,
404
- aspectRatio: undefined,
405
- seed: undefined,
406
- providerOptions: {},
407
- }),
408
- ).rejects.toThrow(
409
- 'Together AI does not support mask-based image editing. ' +
410
- 'Use FLUX Kontext models (e.g., black-forest-labs/FLUX.1-kontext-pro) ' +
411
- 'with a reference image and descriptive prompt instead.',
412
- );
413
- });
414
-
415
- it('should warn when multiple files are provided', async () => {
416
- const model = createBasicModel();
417
-
418
- const result = await model.doGenerate({
419
- prompt: 'Edit multiple images',
420
- files: [
421
- {
422
- type: 'url',
423
- url: 'https://example.com/input1.jpg',
424
- },
425
- {
426
- type: 'url',
427
- url: 'https://example.com/input2.jpg',
428
- },
429
- ],
430
- mask: undefined,
431
- n: 1,
432
- size: undefined,
433
- aspectRatio: undefined,
434
- seed: undefined,
435
- providerOptions: {},
436
- });
437
-
438
- expect(result.warnings).toMatchInlineSnapshot(`
439
- [
440
- {
441
- "message": "Together AI only supports a single input image. Additional images are ignored.",
442
- "type": "other",
443
- },
444
- ]
445
- `);
446
-
447
- // Should only use the first image
448
- const requestBody = await server.calls[0].requestBodyJson;
449
- expect(requestBody.image_url).toBe('https://example.com/input1.jpg');
450
- });
451
-
452
- it('should pass provider options with image editing', async () => {
453
- const model = createBasicModel();
454
-
455
- await model.doGenerate({
456
- prompt: 'Transform the style',
457
- files: [
458
- {
459
- type: 'url',
460
- url: 'https://example.com/input.jpg',
461
- },
462
- ],
463
- mask: undefined,
464
- n: 1,
465
- size: undefined,
466
- aspectRatio: undefined,
467
- seed: undefined,
468
- providerOptions: {
469
- togetherai: {
470
- steps: 28,
471
- guidance: 3.5,
472
- },
473
- },
474
- });
475
-
476
- const requestBody = await server.calls[0].requestBodyJson;
477
- expect(requestBody).toMatchInlineSnapshot(`
478
- {
479
- "guidance": 3.5,
480
- "image_url": "https://example.com/input.jpg",
481
- "model": "stabilityai/stable-diffusion-xl",
482
- "prompt": "Transform the style",
483
- "response_format": "base64",
484
- "steps": 28,
485
- }
486
- `);
487
- });
488
- });
@@ -1,196 +0,0 @@
1
- import {
2
- OpenAICompatibleChatLanguageModel,
3
- OpenAICompatibleCompletionLanguageModel,
4
- OpenAICompatibleEmbeddingModel,
5
- } from '@ai-sdk/openai-compatible';
6
- import {
7
- EmbeddingModelV3,
8
- LanguageModelV3,
9
- RerankingModelV3,
10
- } from '@ai-sdk/provider';
11
- import { loadApiKey } from '@ai-sdk/provider-utils';
12
- import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
13
- import { TogetherAIRerankingModel } from './reranking/togetherai-reranking-model';
14
- import { TogetherAIImageModel } from './togetherai-image-model';
15
- import { createTogetherAI } from './togetherai-provider';
16
-
17
- // Add type assertion for the mocked class
18
- const OpenAICompatibleChatLanguageModelMock =
19
- OpenAICompatibleChatLanguageModel as unknown as Mock;
20
-
21
- vi.mock('@ai-sdk/openai-compatible', () => ({
22
- OpenAICompatibleChatLanguageModel: vi.fn(),
23
- OpenAICompatibleCompletionLanguageModel: vi.fn(),
24
- OpenAICompatibleEmbeddingModel: vi.fn(),
25
- }));
26
-
27
- vi.mock('@ai-sdk/provider-utils', async () => {
28
- const actual = await vi.importActual('@ai-sdk/provider-utils');
29
- return {
30
- ...actual,
31
- loadApiKey: vi.fn().mockReturnValue('mock-api-key'),
32
- withoutTrailingSlash: vi.fn(url => url),
33
- };
34
- });
35
-
36
- vi.mock('./togetherai-image-model', () => ({
37
- TogetherAIImageModel: vi.fn(),
38
- }));
39
-
40
- vi.mock('./reranking/togetherai-reranking-model', () => ({
41
- TogetherAIRerankingModel: vi.fn(),
42
- }));
43
-
44
- describe('TogetherAIProvider', () => {
45
- let mockLanguageModel: LanguageModelV3;
46
- let mockEmbeddingModel: EmbeddingModelV3;
47
- let mockRerankingModel: RerankingModelV3;
48
-
49
- beforeEach(() => {
50
- // Mock implementations of models
51
- mockLanguageModel = {
52
- // Add any required methods for LanguageModelV3
53
- } as LanguageModelV3;
54
- mockEmbeddingModel = {
55
- // Add any required methods for EmbeddingModelV3
56
- } as EmbeddingModelV3;
57
- mockRerankingModel = {
58
- // Add any required methods for RerankingModelV3
59
- } as RerankingModelV3;
60
-
61
- // Reset mocks
62
- vi.clearAllMocks();
63
- });
64
-
65
- describe('createTogetherAI', () => {
66
- it('should create a TogetherAIProvider instance with default options', () => {
67
- const provider = createTogetherAI();
68
- const model = provider('model-id');
69
-
70
- // Use the mocked version
71
- const constructorCall =
72
- OpenAICompatibleChatLanguageModelMock.mock.calls[0];
73
- const config = constructorCall[1];
74
- config.headers();
75
-
76
- expect(loadApiKey).toHaveBeenCalledWith({
77
- apiKey: undefined,
78
- environmentVariableName: 'TOGETHER_AI_API_KEY',
79
- description: 'TogetherAI',
80
- });
81
- });
82
-
83
- it('should create a TogetherAIProvider instance with custom options', () => {
84
- const options = {
85
- apiKey: 'custom-key',
86
- baseURL: 'https://custom.url',
87
- headers: { 'Custom-Header': 'value' },
88
- };
89
- const provider = createTogetherAI(options);
90
- const model = provider('model-id');
91
-
92
- const constructorCall =
93
- OpenAICompatibleChatLanguageModelMock.mock.calls[0];
94
- const config = constructorCall[1];
95
- config.headers();
96
-
97
- expect(loadApiKey).toHaveBeenCalledWith({
98
- apiKey: 'custom-key',
99
- environmentVariableName: 'TOGETHER_AI_API_KEY',
100
- description: 'TogetherAI',
101
- });
102
- });
103
-
104
- it('should return a chat model when called as a function', () => {
105
- const provider = createTogetherAI();
106
- const modelId = 'foo-model-id';
107
-
108
- const model = provider(modelId);
109
- expect(model).toBeInstanceOf(OpenAICompatibleChatLanguageModel);
110
- });
111
- });
112
-
113
- describe('chatModel', () => {
114
- it('should construct a chat model with correct configuration', () => {
115
- const provider = createTogetherAI();
116
- const modelId = 'together-chat-model';
117
-
118
- const model = provider.chatModel(modelId);
119
-
120
- expect(model).toBeInstanceOf(OpenAICompatibleChatLanguageModel);
121
- });
122
- });
123
-
124
- describe('completionModel', () => {
125
- it('should construct a completion model with correct configuration', () => {
126
- const provider = createTogetherAI();
127
- const modelId = 'together-completion-model';
128
-
129
- const model = provider.completionModel(modelId);
130
-
131
- expect(model).toBeInstanceOf(OpenAICompatibleCompletionLanguageModel);
132
- });
133
- });
134
-
135
- describe('embeddingModel', () => {
136
- it('should construct a text embedding model with correct configuration', () => {
137
- const provider = createTogetherAI();
138
- const modelId = 'together-embedding-model';
139
-
140
- const model = provider.embeddingModel(modelId);
141
-
142
- expect(model).toBeInstanceOf(OpenAICompatibleEmbeddingModel);
143
- });
144
- });
145
-
146
- describe('image', () => {
147
- it('should construct an image model with correct configuration', () => {
148
- const provider = createTogetherAI();
149
- const modelId = 'stabilityai/stable-diffusion-xl';
150
-
151
- const model = provider.image(modelId);
152
-
153
- expect(TogetherAIImageModel).toHaveBeenCalledWith(
154
- modelId,
155
- expect.objectContaining({
156
- provider: 'togetherai.image',
157
- baseURL: 'https://api.together.xyz/v1/',
158
- }),
159
- );
160
- expect(model).toBeInstanceOf(TogetherAIImageModel);
161
- });
162
-
163
- it('should pass custom baseURL to image model', () => {
164
- const provider = createTogetherAI({
165
- baseURL: 'https://custom.url/',
166
- });
167
- const modelId = 'stabilityai/stable-diffusion-xl';
168
-
169
- provider.image(modelId);
170
-
171
- expect(TogetherAIImageModel).toHaveBeenCalledWith(
172
- modelId,
173
- expect.objectContaining({
174
- baseURL: 'https://custom.url/',
175
- }),
176
- );
177
- });
178
- });
179
-
180
- describe('rerankingModel', () => {
181
- it('should construct a reranking model with correct configuration', () => {
182
- const provider = createTogetherAI();
183
- const modelId = 'Salesforce/Llama-Rank-v1';
184
- 0;
185
- const model = provider.rerankingModel(modelId);
186
-
187
- expect(TogetherAIRerankingModel).toHaveBeenCalledWith(
188
- modelId,
189
- expect.objectContaining({
190
- baseURL: 'https://api.together.xyz/v1/',
191
- }),
192
- );
193
- expect(model).toBeInstanceOf(TogetherAIRerankingModel);
194
- });
195
- });
196
- });