@tanstack/ai-byteplus 0.0.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/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/esm/adapters/image.d.ts +89 -0
- package/dist/esm/adapters/image.js +229 -0
- package/dist/esm/adapters/image.js.map +1 -0
- package/dist/esm/adapters/text.d.ts +163 -0
- package/dist/esm/adapters/text.js +347 -0
- package/dist/esm/adapters/text.js.map +1 -0
- package/dist/esm/adapters/transcription.d.ts +102 -0
- package/dist/esm/adapters/transcription.js +274 -0
- package/dist/esm/adapters/transcription.js.map +1 -0
- package/dist/esm/adapters/tts.d.ts +143 -0
- package/dist/esm/adapters/tts.js +307 -0
- package/dist/esm/adapters/tts.js.map +1 -0
- package/dist/esm/adapters/video.d.ts +182 -0
- package/dist/esm/adapters/video.js +442 -0
- package/dist/esm/adapters/video.js.map +1 -0
- package/dist/esm/audio/transcription-provider-options.d.ts +46 -0
- package/dist/esm/audio/tts-provider-options.d.ts +114 -0
- package/dist/esm/audio/wire-types.d.ts +261 -0
- package/dist/esm/audio/wire-types.js +28 -0
- package/dist/esm/audio/wire-types.js.map +1 -0
- package/dist/esm/image/image-provider-options.d.ts +165 -0
- package/dist/esm/image/image-provider-options.js +134 -0
- package/dist/esm/image/image-provider-options.js.map +1 -0
- package/dist/esm/image/wire-types.d.ts +149 -0
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/message-types.d.ts +154 -0
- package/dist/esm/model-meta.d.ts +594 -0
- package/dist/esm/model-meta.js +619 -0
- package/dist/esm/model-meta.js.map +1 -0
- package/dist/esm/text/text-provider-options.d.ts +109 -0
- package/dist/esm/utils/client.d.ts +183 -0
- package/dist/esm/utils/client.js +253 -0
- package/dist/esm/utils/client.js.map +1 -0
- package/dist/esm/video/video-provider-options.d.ts +197 -0
- package/dist/esm/video/video-provider-options.js +191 -0
- package/dist/esm/video/video-provider-options.js.map +1 -0
- package/dist/esm/video/wire-types.d.ts +248 -0
- package/package.json +77 -0
- package/src/adapters/image.ts +409 -0
- package/src/adapters/text.ts +539 -0
- package/src/adapters/transcription.ts +479 -0
- package/src/adapters/tts.ts +447 -0
- package/src/adapters/video.ts +732 -0
- package/src/audio/transcription-provider-options.ts +46 -0
- package/src/audio/tts-provider-options.ts +122 -0
- package/src/audio/wire-types.ts +290 -0
- package/src/image/image-provider-options.ts +288 -0
- package/src/image/wire-types.ts +169 -0
- package/src/index.ts +222 -0
- package/src/message-types.ts +169 -0
- package/src/model-meta.ts +954 -0
- package/src/text/text-provider-options.ts +151 -0
- package/src/utils/client.ts +377 -0
- package/src/video/video-provider-options.ts +361 -0
- package/src/video/wire-types.ts +293 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { resolveMediaPrompt } from '@tanstack/ai'
|
|
2
|
+
import { BaseImageAdapter } from '@tanstack/ai/adapters'
|
|
3
|
+
import { toRunErrorPayload } from '@tanstack/ai/adapter-internals'
|
|
4
|
+
import { generateId } from '@tanstack/ai-utils'
|
|
5
|
+
import {
|
|
6
|
+
bytePlusArkError,
|
|
7
|
+
bytePlusArkHeaders,
|
|
8
|
+
bytePlusTimeoutSignal,
|
|
9
|
+
describeBody,
|
|
10
|
+
getBytePlusArkApiKeyFromEnv,
|
|
11
|
+
readJsonBody,
|
|
12
|
+
toHeaderRecord,
|
|
13
|
+
withBytePlusArkDefaults,
|
|
14
|
+
} from '../utils/client'
|
|
15
|
+
import {
|
|
16
|
+
resolveBytePlusImageSize,
|
|
17
|
+
resolveBytePlusSequentialImages,
|
|
18
|
+
validateBytePlusImagePrompt,
|
|
19
|
+
validateBytePlusReferenceImages,
|
|
20
|
+
} from '../image/image-provider-options'
|
|
21
|
+
import type {
|
|
22
|
+
GeneratedImage,
|
|
23
|
+
ImageGenerationOptions,
|
|
24
|
+
ImageGenerationResult,
|
|
25
|
+
ImagePart,
|
|
26
|
+
MediaInputMetadata,
|
|
27
|
+
TokenUsage,
|
|
28
|
+
} from '@tanstack/ai'
|
|
29
|
+
import type { InternalLogger } from '@tanstack/ai/adapter-internals'
|
|
30
|
+
import type {
|
|
31
|
+
BytePlusImageErrorObject,
|
|
32
|
+
BytePlusImageGenerationRequest,
|
|
33
|
+
BytePlusImageGenerationResponse,
|
|
34
|
+
BytePlusImageUsage,
|
|
35
|
+
} from '../image/wire-types'
|
|
36
|
+
import type {
|
|
37
|
+
BytePlusImageModelInputModalitiesByName,
|
|
38
|
+
BytePlusImageModelProviderOptionsByName,
|
|
39
|
+
BytePlusImageProviderOptions,
|
|
40
|
+
} from '../image/image-provider-options'
|
|
41
|
+
import type {
|
|
42
|
+
BytePlusImageModel,
|
|
43
|
+
BytePlusImageModelSizeByName,
|
|
44
|
+
} from '../model-meta'
|
|
45
|
+
import type { BytePlusArkConfig } from '../utils/client'
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Configuration for the BytePlus Seedream image adapter.
|
|
49
|
+
*/
|
|
50
|
+
export interface BytePlusImageConfig extends BytePlusArkConfig {}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Roles Seedream can honour. Every input image is a reference — there is no
|
|
54
|
+
* inpainting mask, control-image or frame channel — so this is an allow-list
|
|
55
|
+
* rather than a deny-list: a role added to the core union later (or a
|
|
56
|
+
* video-oriented one like `start_frame`) fails loudly here instead of being
|
|
57
|
+
* silently flattened into a plain reference.
|
|
58
|
+
*/
|
|
59
|
+
const SUPPORTED_INPUT_ROLES: ReadonlySet<string> = new Set([
|
|
60
|
+
'reference',
|
|
61
|
+
'character',
|
|
62
|
+
])
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Converts a prompt image part to the string Seedream's `image` field takes:
|
|
66
|
+
* URLs pass through (BytePlus fetches them server-side), data sources become
|
|
67
|
+
* data URIs. BytePlus requires the format in `data:image/<format>;base64,` to
|
|
68
|
+
* be lowercase, so the mime type is lowercased on the way out.
|
|
69
|
+
*/
|
|
70
|
+
function imagePartToImageRef(part: ImagePart<MediaInputMetadata>): string {
|
|
71
|
+
const { source } = part
|
|
72
|
+
if (source.type === 'url') return source.value
|
|
73
|
+
if (source.value.startsWith('data:')) return source.value
|
|
74
|
+
return `data:${source.mimeType.toLowerCase()};base64,${source.value}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Renders provider error objects as `code: message` pairs for a log line or
|
|
79
|
+
* an error message.
|
|
80
|
+
*/
|
|
81
|
+
function describeFailures(
|
|
82
|
+
failures: ReadonlyArray<BytePlusImageErrorObject>,
|
|
83
|
+
): string {
|
|
84
|
+
return failures
|
|
85
|
+
.map((failure) =>
|
|
86
|
+
[failure.code, failure.message].filter(Boolean).join(': '),
|
|
87
|
+
)
|
|
88
|
+
.filter((text) => text.length > 0)
|
|
89
|
+
.join('; ')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Maps Seedream's usage block onto `TokenUsage`.
|
|
94
|
+
*
|
|
95
|
+
* BytePlus bills per generated image and does not count input tokens, so
|
|
96
|
+
* `promptTokens` is always 0 and `generated_images` is surfaced as
|
|
97
|
+
* `unitsBilled` — the count the price is applied to.
|
|
98
|
+
*/
|
|
99
|
+
function buildBytePlusImageUsage(
|
|
100
|
+
usage: BytePlusImageUsage | undefined,
|
|
101
|
+
): TokenUsage | undefined {
|
|
102
|
+
if (!usage) return undefined
|
|
103
|
+
|
|
104
|
+
const completionTokens = usage.output_tokens ?? 0
|
|
105
|
+
return {
|
|
106
|
+
promptTokens: 0,
|
|
107
|
+
completionTokens,
|
|
108
|
+
totalTokens: usage.total_tokens ?? completionTokens,
|
|
109
|
+
...(usage.generated_images !== undefined && {
|
|
110
|
+
unitsBilled: usage.generated_images,
|
|
111
|
+
}),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* BytePlus Seedream image generation adapter.
|
|
117
|
+
*
|
|
118
|
+
* Drives Ark's `POST /images/generations` endpoint directly rather than
|
|
119
|
+
* through the OpenAI SDK: the endpoint takes size tokens (`2K`) as well as
|
|
120
|
+
* pixel sizes, has no `n` parameter, and carries reference images for editing
|
|
121
|
+
* in the generation request instead of a separate edits endpoint.
|
|
122
|
+
*
|
|
123
|
+
* Features:
|
|
124
|
+
* - Text-to-image and image-conditioned generation (editing, multi-reference)
|
|
125
|
+
* from a single call, with per-model reference-count limits enforced.
|
|
126
|
+
* - Size validation across both accepted forms.
|
|
127
|
+
* - `numberOfImages` mapped onto Seedream's group-image mode.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const adapter = byteplusImage('seedream-4-0-250828')
|
|
132
|
+
* const result = await generateImage({
|
|
133
|
+
* adapter,
|
|
134
|
+
* prompt: 'A guitar in a sunlit workshop',
|
|
135
|
+
* size: '2K',
|
|
136
|
+
* modelOptions: { watermark: false },
|
|
137
|
+
* })
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export class BytePlusImageAdapter<
|
|
141
|
+
TModel extends BytePlusImageModel,
|
|
142
|
+
> extends BaseImageAdapter<
|
|
143
|
+
TModel,
|
|
144
|
+
BytePlusImageProviderOptions,
|
|
145
|
+
BytePlusImageModelProviderOptionsByName,
|
|
146
|
+
BytePlusImageModelSizeByName,
|
|
147
|
+
BytePlusImageModelInputModalitiesByName
|
|
148
|
+
> {
|
|
149
|
+
override readonly kind = 'image' as const
|
|
150
|
+
readonly name = 'byteplus' as const
|
|
151
|
+
|
|
152
|
+
/** Config with the Ark base URL resolved and trailing slashes trimmed. */
|
|
153
|
+
private readonly clientConfig: Omit<BytePlusImageConfig, 'baseURL'> & {
|
|
154
|
+
baseURL: string
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
constructor(model: TModel, config: BytePlusImageConfig) {
|
|
158
|
+
super(model, {})
|
|
159
|
+
this.clientConfig = withBytePlusArkDefaults(config)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async generateImages(
|
|
163
|
+
options: ImageGenerationOptions<
|
|
164
|
+
BytePlusImageProviderOptions,
|
|
165
|
+
BytePlusImageModelSizeByName[TModel]
|
|
166
|
+
>,
|
|
167
|
+
): Promise<ImageGenerationResult> {
|
|
168
|
+
const { numberOfImages, size, modelOptions, logger } = options
|
|
169
|
+
const model = this.model
|
|
170
|
+
|
|
171
|
+
const resolved = resolveMediaPrompt(options.prompt)
|
|
172
|
+
|
|
173
|
+
if (resolved.videos.length > 0 || resolved.audios.length > 0) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`byteplus.generateImages does not support video / audio prompt parts on model ${model}.`,
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const unsupportedRole = resolved.images.find(
|
|
180
|
+
(part) =>
|
|
181
|
+
part.metadata?.role !== undefined &&
|
|
182
|
+
!SUPPORTED_INPUT_ROLES.has(part.metadata.role),
|
|
183
|
+
)
|
|
184
|
+
if (unsupportedRole) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`byteplus: Seedream has no ${unsupportedRole.metadata?.role} input; ` +
|
|
187
|
+
`it accepts reference images only (${[...SUPPORTED_INPUT_ROLES].join(', ')}).`,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
validateBytePlusImagePrompt(model, resolved.text)
|
|
192
|
+
validateBytePlusReferenceImages(model, resolved.images.length)
|
|
193
|
+
|
|
194
|
+
const imageRefs = resolved.images.map(imagePartToImageRef)
|
|
195
|
+
const request: BytePlusImageGenerationRequest = {
|
|
196
|
+
...(imageRefs.length > 0 && { image: imageRefs }),
|
|
197
|
+
...(size !== undefined && {
|
|
198
|
+
size: resolveBytePlusImageSize(size),
|
|
199
|
+
}),
|
|
200
|
+
...resolveBytePlusSequentialImages(model, numberOfImages),
|
|
201
|
+
// Explicit provider options win over the values derived from the
|
|
202
|
+
// generic options above (e.g. forcing `sequential_image_generation`).
|
|
203
|
+
...modelOptions,
|
|
204
|
+
model,
|
|
205
|
+
prompt: resolved.text,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
logger.request(
|
|
210
|
+
`activity=image provider=${this.name} model=${model} size=${request.size ?? 'default'} refs=${imageRefs.length}`,
|
|
211
|
+
{ provider: this.name, model },
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
const fetchImpl = this.clientConfig.fetch ?? fetch
|
|
215
|
+
const signal = bytePlusTimeoutSignal(this.clientConfig.timeout)
|
|
216
|
+
const response = await fetchImpl(
|
|
217
|
+
`${this.clientConfig.baseURL}/images/generations`,
|
|
218
|
+
{
|
|
219
|
+
method: 'POST',
|
|
220
|
+
...(signal && { signal }),
|
|
221
|
+
headers: bytePlusArkHeaders(
|
|
222
|
+
this.clientConfig.apiKey,
|
|
223
|
+
toHeaderRecord(this.clientConfig.defaultHeaders),
|
|
224
|
+
),
|
|
225
|
+
body: JSON.stringify(request),
|
|
226
|
+
},
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
const body = await readJsonBody(response)
|
|
230
|
+
if (!response.ok) {
|
|
231
|
+
throw bytePlusArkError(response.status, body, 'image generation')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return this.transformResponse(body, logger, numberOfImages)
|
|
235
|
+
} catch (error: unknown) {
|
|
236
|
+
logger.errors(`${this.name}.generateImages fatal`, {
|
|
237
|
+
error: toRunErrorPayload(error, `${this.name}.generateImages failed`),
|
|
238
|
+
source: `${this.name}.generateImages`,
|
|
239
|
+
})
|
|
240
|
+
throw error
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private transformResponse(
|
|
245
|
+
body: unknown,
|
|
246
|
+
logger: InternalLogger,
|
|
247
|
+
numberOfImages: number | undefined,
|
|
248
|
+
): ImageGenerationResult {
|
|
249
|
+
// Shape pinned by a live seedream-4-0-250828 call and the Ark OpenAPI
|
|
250
|
+
// document. Validate rather than cast: `readJsonBody` returns `undefined`
|
|
251
|
+
// for an empty body and the raw text for a non-JSON one (an HTML error
|
|
252
|
+
// page from a proxy in front of the API), and casting either would report
|
|
253
|
+
// "returned no images" with the body — the only evidence of what actually
|
|
254
|
+
// happened — thrown away.
|
|
255
|
+
if (typeof body !== 'object' || body === null) {
|
|
256
|
+
throw bytePlusArkError(
|
|
257
|
+
200,
|
|
258
|
+
body,
|
|
259
|
+
'image generation returned a non-object body',
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
const payload = body as BytePlusImageGenerationResponse
|
|
263
|
+
|
|
264
|
+
const images: Array<GeneratedImage> = []
|
|
265
|
+
const failures: Array<BytePlusImageErrorObject> = []
|
|
266
|
+
// Items matching none of the three known shapes. Ark's OpenAPI document
|
|
267
|
+
// describes a second, nested item form, so this is a live possibility
|
|
268
|
+
// rather than a defensive branch — and an unrecognized item that is
|
|
269
|
+
// neither counted nor reported turns provider drift into an
|
|
270
|
+
// "returned no images" with no attribution at all.
|
|
271
|
+
let unrecognized = 0
|
|
272
|
+
for (const item of payload.data ?? []) {
|
|
273
|
+
if (item.b64_json) {
|
|
274
|
+
images.push({ b64Json: item.b64_json })
|
|
275
|
+
} else if (item.url) {
|
|
276
|
+
images.push({ url: item.url })
|
|
277
|
+
} else if (item.error) {
|
|
278
|
+
// Group-image mode reports per-image failures alongside successes;
|
|
279
|
+
// dropping them silently would make a short result look complete.
|
|
280
|
+
failures.push(item.error)
|
|
281
|
+
} else {
|
|
282
|
+
unrecognized += 1
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (payload.error) failures.push(payload.error)
|
|
286
|
+
|
|
287
|
+
if (unrecognized > 0) {
|
|
288
|
+
logger.errors(
|
|
289
|
+
`${this.name}.generateImages: ${unrecognized} response item(s) matched ` +
|
|
290
|
+
`none of b64_json / url / error — the response shape may have changed.`,
|
|
291
|
+
{
|
|
292
|
+
source: `${this.name}.generateImages`,
|
|
293
|
+
provider: this.name,
|
|
294
|
+
model: this.model,
|
|
295
|
+
body,
|
|
296
|
+
},
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (images.length === 0) {
|
|
301
|
+
const detail =
|
|
302
|
+
describeFailures(failures) ||
|
|
303
|
+
(unrecognized > 0
|
|
304
|
+
? `${unrecognized} unrecognized response item(s): ${describeBody(body) ?? ''}`
|
|
305
|
+
: '')
|
|
306
|
+
throw new Error(
|
|
307
|
+
`byteplus: image generation returned no images` +
|
|
308
|
+
(detail ? `: ${detail}` : '.'),
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (failures.length > 0) {
|
|
313
|
+
logger.errors(
|
|
314
|
+
`${this.name}.generateImages dropped ${failures.length} failed image(s): ${describeFailures(failures)}`,
|
|
315
|
+
{
|
|
316
|
+
source: `${this.name}.generateImages`,
|
|
317
|
+
provider: this.name,
|
|
318
|
+
model: this.model,
|
|
319
|
+
failures,
|
|
320
|
+
},
|
|
321
|
+
)
|
|
322
|
+
// The caller asked for a group and is getting a short array. Warn
|
|
323
|
+
// unconditionally: the `numberOfImages` warning below only fires when
|
|
324
|
+
// the count was set explicitly, so a partial failure would otherwise
|
|
325
|
+
// return successfully with no signal at all.
|
|
326
|
+
logger.warn(
|
|
327
|
+
`byteplus: ${failures.length} of ${failures.length + images.length} ` +
|
|
328
|
+
`images failed to generate; returning ${images.length}.`,
|
|
329
|
+
{ provider: this.name, model: this.model },
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (numberOfImages !== undefined && images.length < numberOfImages) {
|
|
334
|
+
logger.warn(
|
|
335
|
+
`byteplus: requested ${numberOfImages} images, received ${images.length}. ` +
|
|
336
|
+
`Seedream has no exact count — sequential_image_generation.max_images is ` +
|
|
337
|
+
`an upper bound and the model decides how many the prompt warrants.`,
|
|
338
|
+
{ provider: this.name, model: this.model },
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const usage = buildBytePlusImageUsage(payload.usage)
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
id: generateId(this.name),
|
|
346
|
+
model: this.model,
|
|
347
|
+
images,
|
|
348
|
+
...(usage ? { usage } : {}),
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Creates a BytePlus Seedream image adapter with an explicit API key.
|
|
355
|
+
* Type resolution happens here at the call site.
|
|
356
|
+
*
|
|
357
|
+
* @param model - The model name (e.g., 'seedream-4-0-250828')
|
|
358
|
+
* @param apiKey - Your BytePlus Ark API key
|
|
359
|
+
* @param config - Optional additional configuration
|
|
360
|
+
* @returns Configured BytePlus image adapter instance with resolved types
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* const adapter = createBytePlusImage('seedream-5-0-260128', 'ark-...')
|
|
365
|
+
*
|
|
366
|
+
* const result = await generateImage({
|
|
367
|
+
* adapter,
|
|
368
|
+
* prompt: 'A cute baby sea otter',
|
|
369
|
+
* size: '2K',
|
|
370
|
+
* })
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
export function createBytePlusImage<TModel extends BytePlusImageModel>(
|
|
374
|
+
model: TModel,
|
|
375
|
+
apiKey: string,
|
|
376
|
+
config?: Omit<BytePlusImageConfig, 'apiKey'>,
|
|
377
|
+
): BytePlusImageAdapter<TModel> {
|
|
378
|
+
return new BytePlusImageAdapter(model, { apiKey, ...config })
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Creates a BytePlus Seedream image adapter, reading `ARK_API_KEY` from the
|
|
383
|
+
* environment. Type resolution happens here at the call site.
|
|
384
|
+
*
|
|
385
|
+
* Note that Ark keys are region-isolated: a key issued for `ap-southeast`
|
|
386
|
+
* does not work against the EU host.
|
|
387
|
+
*
|
|
388
|
+
* @param model - The model name (e.g., 'seedream-4-0-250828')
|
|
389
|
+
* @param config - Optional configuration (excluding apiKey, auto-detected)
|
|
390
|
+
* @returns Configured BytePlus image adapter instance with resolved types
|
|
391
|
+
* @throws Error if ARK_API_KEY is not found in environment
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* ```typescript
|
|
395
|
+
* const adapter = byteplusImage('seedream-4-0-250828')
|
|
396
|
+
*
|
|
397
|
+
* const result = await generateImage({
|
|
398
|
+
* adapter,
|
|
399
|
+
* prompt: 'A beautiful sunset over mountains',
|
|
400
|
+
* modelOptions: { watermark: false },
|
|
401
|
+
* })
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
export function byteplusImage<TModel extends BytePlusImageModel>(
|
|
405
|
+
model: TModel,
|
|
406
|
+
config?: Omit<BytePlusImageConfig, 'apiKey'>,
|
|
407
|
+
): BytePlusImageAdapter<TModel> {
|
|
408
|
+
return createBytePlusImage(model, getBytePlusArkApiKeyFromEnv(), config)
|
|
409
|
+
}
|