@fgv/ts-extras 5.1.0-48 → 5.1.0-49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packlets/ai-assist/completionClient.js +435 -0
- package/dist/packlets/ai-assist/completionClient.js.map +1 -0
- package/dist/packlets/ai-assist/embeddingClient.js +2 -2
- package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/dist/packlets/ai-assist/endpoint.js +29 -0
- package/dist/packlets/ai-assist/endpoint.js.map +1 -1
- package/dist/packlets/ai-assist/http.js +48 -19
- package/dist/packlets/ai-assist/http.js.map +1 -1
- package/dist/packlets/ai-assist/imageGenerationClient.js +454 -0
- package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -0
- package/dist/packlets/ai-assist/index.js +3 -1
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/jsonCompletion.js +1 -1
- package/dist/packlets/ai-assist/jsonCompletion.js.map +1 -1
- package/dist/packlets/ai-assist/listModelsClient.js +294 -0
- package/dist/packlets/ai-assist/listModelsClient.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +2 -5
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +2 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/dist/packlets/crypto-utils/encryptedFile.js +37 -0
- package/dist/packlets/crypto-utils/encryptedFile.js.map +1 -1
- package/dist/packlets/crypto-utils/index.js +1 -1
- package/dist/packlets/crypto-utils/index.js.map +1 -1
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +32 -0
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -1
- package/dist/ts-extras.d.ts +59 -4
- package/lib/packlets/ai-assist/completionClient.d.ts +74 -0
- package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/completionClient.js +439 -0
- package/lib/packlets/ai-assist/completionClient.js.map +1 -0
- package/lib/packlets/ai-assist/embeddingClient.js +1 -1
- package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/lib/packlets/ai-assist/endpoint.d.ts +21 -0
- package/lib/packlets/ai-assist/endpoint.d.ts.map +1 -1
- package/lib/packlets/ai-assist/endpoint.js +31 -0
- package/lib/packlets/ai-assist/endpoint.js.map +1 -1
- package/lib/packlets/ai-assist/http.d.ts +12 -2
- package/lib/packlets/ai-assist/http.d.ts.map +1 -1
- package/lib/packlets/ai-assist/http.js +49 -18
- package/lib/packlets/ai-assist/http.js.map +1 -1
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts +46 -0
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/imageGenerationClient.js +458 -0
- package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -0
- package/lib/packlets/ai-assist/index.d.ts +3 -1
- package/lib/packlets/ai-assist/index.d.ts.map +1 -1
- package/lib/packlets/ai-assist/index.js +9 -7
- package/lib/packlets/ai-assist/index.js.map +1 -1
- package/lib/packlets/ai-assist/jsonCompletion.d.ts +1 -1
- package/lib/packlets/ai-assist/jsonCompletion.d.ts.map +1 -1
- package/lib/packlets/ai-assist/jsonCompletion.js +2 -2
- package/lib/packlets/ai-assist/jsonCompletion.js.map +1 -1
- package/lib/packlets/ai-assist/listModelsClient.d.ts +46 -0
- package/lib/packlets/ai-assist/listModelsClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/listModelsClient.js +298 -0
- package/lib/packlets/ai-assist/listModelsClient.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +2 -5
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/lib/packlets/crypto-utils/encryptedFile.d.ts +25 -0
- package/lib/packlets/crypto-utils/encryptedFile.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/encryptedFile.js +38 -0
- package/lib/packlets/crypto-utils/encryptedFile.js.map +1 -1
- package/lib/packlets/crypto-utils/index.d.ts +1 -1
- package/lib/packlets/crypto-utils/index.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.js +2 -1
- package/lib/packlets/crypto-utils/index.js.map +1 -1
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +30 -2
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts.map +1 -1
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +32 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -1
- package/package.json +7 -7
- package/dist/packlets/ai-assist/apiClient.js +0 -1236
- package/dist/packlets/ai-assist/apiClient.js.map +0 -1
- package/lib/packlets/ai-assist/apiClient.d.ts +0 -152
- package/lib/packlets/ai-assist/apiClient.d.ts.map +0 -1
- package/lib/packlets/ai-assist/apiClient.js +0 -1244
- package/lib/packlets/ai-assist/apiClient.js.map +0 -1
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
import { fail, mapResults, succeed, Validators } from '@fgv/ts-utils';
|
|
21
|
+
import { resolveProviderModel } from './model';
|
|
22
|
+
import { bearerAuthHeader, geminiAuthHeader, resolveEffectiveBaseUrl } from './endpoint';
|
|
23
|
+
import { fetchJson, fetchMultipart } from './http';
|
|
24
|
+
import { resolveImageCapability, supportsImageGeneration } from './registry';
|
|
25
|
+
import { resolveImageOptions, validateResolvedOptions } from './imageOptionsResolver';
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Image attachment helpers
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Decodes a base64-encoded image attachment into a `Blob` suitable for use as
|
|
31
|
+
* a multipart file field. On Node hands the `Buffer` straight to `Blob`
|
|
32
|
+
* (Buffer extends Uint8Array) to skip an intermediate copy; falls back to
|
|
33
|
+
* `atob` in browsers. Inputs come from `FileReader` or prior provider
|
|
34
|
+
* responses, which are trusted to be valid. Note that Node's
|
|
35
|
+
* `Buffer.from(..., 'base64')` silently strips invalid characters rather
|
|
36
|
+
* than throwing, so failures are only observable in the browser path.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
function attachmentToBlob(attachment) {
|
|
40
|
+
if (typeof Buffer !== 'undefined') {
|
|
41
|
+
return succeed(new Blob([Buffer.from(attachment.base64, 'base64')], { type: attachment.mimeType }));
|
|
42
|
+
}
|
|
43
|
+
/* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */
|
|
44
|
+
try {
|
|
45
|
+
const binary = atob(attachment.base64);
|
|
46
|
+
const bytes = new Uint8Array(binary.length);
|
|
47
|
+
for (let i = 0; i < binary.length; i++) {
|
|
48
|
+
bytes[i] = binary.charCodeAt(i);
|
|
49
|
+
}
|
|
50
|
+
return succeed(new Blob([bytes], { type: attachment.mimeType }));
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
54
|
+
return fail(`Invalid base64: ${message}`);
|
|
55
|
+
}
|
|
56
|
+
/* c8 ignore stop */
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Maps a MIME type to a sensible file extension for multipart filenames.
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
function extensionForMimeType(mimeType) {
|
|
63
|
+
switch (mimeType) {
|
|
64
|
+
case 'image/png':
|
|
65
|
+
return 'png';
|
|
66
|
+
case 'image/jpeg':
|
|
67
|
+
case 'image/jpg':
|
|
68
|
+
return 'jpg';
|
|
69
|
+
case 'image/webp':
|
|
70
|
+
return 'webp';
|
|
71
|
+
case 'image/gif':
|
|
72
|
+
return 'gif';
|
|
73
|
+
default:
|
|
74
|
+
return 'bin';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const openAiImageItem = Validators.object({
|
|
78
|
+
b64_json: Validators.string,
|
|
79
|
+
revised_prompt: Validators.string.optional()
|
|
80
|
+
});
|
|
81
|
+
const openAiImageResponse = Validators.object({
|
|
82
|
+
data: Validators.arrayOf(openAiImageItem).withConstraint((arr) => arr.length > 0)
|
|
83
|
+
});
|
|
84
|
+
const geminiImageInlineData = Validators.object({
|
|
85
|
+
mimeType: Validators.string,
|
|
86
|
+
data: Validators.string
|
|
87
|
+
});
|
|
88
|
+
const geminiImageOutPart = Validators.object({
|
|
89
|
+
text: Validators.string.optional(),
|
|
90
|
+
inlineData: geminiImageInlineData.optional()
|
|
91
|
+
});
|
|
92
|
+
const geminiImageOutContent = Validators.object({
|
|
93
|
+
parts: Validators.arrayOf(geminiImageOutPart)
|
|
94
|
+
});
|
|
95
|
+
const geminiImageOutCandidate = Validators.object({
|
|
96
|
+
content: geminiImageOutContent.optional(),
|
|
97
|
+
finishReason: Validators.string.optional(),
|
|
98
|
+
finishMessage: Validators.string.optional()
|
|
99
|
+
});
|
|
100
|
+
const geminiImageOutResponse = Validators.object({
|
|
101
|
+
candidates: Validators.arrayOf(geminiImageOutCandidate).withConstraint((arr) => arr.length > 0)
|
|
102
|
+
});
|
|
103
|
+
// ---- Proxied image generation response ----
|
|
104
|
+
const proxiedGeneratedImage = Validators.object({
|
|
105
|
+
mimeType: Validators.string,
|
|
106
|
+
base64: Validators.string,
|
|
107
|
+
revisedPrompt: Validators.string.optional()
|
|
108
|
+
});
|
|
109
|
+
const proxiedImageGenerationResponse = Validators.object({
|
|
110
|
+
images: Validators.arrayOf(proxiedGeneratedImage).withConstraint((arr) => arr.length > 0)
|
|
111
|
+
});
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Image generation — adapters
|
|
114
|
+
// ============================================================================
|
|
115
|
+
/** Routes to /images/generations or /images/edits; handles outputParamStyle. @internal */
|
|
116
|
+
async function callOpenAiImageGeneration(config, request, capability, resolved, logger, signal) {
|
|
117
|
+
var _a, _b;
|
|
118
|
+
const refs = (_a = request.referenceImages) !== null && _a !== void 0 ? _a : [];
|
|
119
|
+
const headers = bearerAuthHeader(config.apiKey);
|
|
120
|
+
const effectiveMimeType = resolved.outputFormat !== undefined
|
|
121
|
+
? `image/${resolved.outputFormat}`
|
|
122
|
+
: (_b = capability.defaultOutputMimeType) !== null && _b !== void 0 ? _b : 'image/png';
|
|
123
|
+
const fetched = refs.length > 0
|
|
124
|
+
? await callOpenAiImagesEdits(config, capability, request, headers, resolved, logger, signal)
|
|
125
|
+
: await callOpenAiImagesGenerations(config, request, headers, resolved, capability, logger, signal);
|
|
126
|
+
return fetched.onSuccess((json) => openAiImageResponse
|
|
127
|
+
.validate(json)
|
|
128
|
+
.withErrorFormat((msg) => `OpenAI images API response: ${msg}`)
|
|
129
|
+
.onSuccess((response) => succeed({
|
|
130
|
+
images: response.data.map((item) => (Object.assign({ mimeType: effectiveMimeType, base64: item.b64_json }, (item.revised_prompt !== undefined ? { revisedPrompt: item.revised_prompt } : {}))))
|
|
131
|
+
})));
|
|
132
|
+
}
|
|
133
|
+
/** Builds the JSON /images/generations request; handles outputParamStyle. @internal */
|
|
134
|
+
function callOpenAiImagesGenerations(config, request, headers, resolved, capability, logger, signal) {
|
|
135
|
+
var _a;
|
|
136
|
+
const body = {
|
|
137
|
+
model: config.model,
|
|
138
|
+
prompt: request.prompt,
|
|
139
|
+
n: resolved.n
|
|
140
|
+
};
|
|
141
|
+
// Output format param — conditional on model capability
|
|
142
|
+
if (capability.outputParamStyle === 'response-format') {
|
|
143
|
+
body.response_format = 'b64_json';
|
|
144
|
+
}
|
|
145
|
+
else if (capability.outputParamStyle === 'output-format') {
|
|
146
|
+
body.output_format = (_a = resolved.outputFormat) !== null && _a !== void 0 ? _a : 'png';
|
|
147
|
+
}
|
|
148
|
+
if (resolved.size !== undefined) {
|
|
149
|
+
body.size = resolved.size;
|
|
150
|
+
}
|
|
151
|
+
if (capability.supportsQualityParam && resolved.quality !== undefined) {
|
|
152
|
+
body.quality = resolved.quality;
|
|
153
|
+
}
|
|
154
|
+
if (resolved.seed !== undefined) {
|
|
155
|
+
body.seed = resolved.seed;
|
|
156
|
+
}
|
|
157
|
+
if (resolved.background !== undefined) {
|
|
158
|
+
body.background = resolved.background;
|
|
159
|
+
}
|
|
160
|
+
if (resolved.moderation !== undefined) {
|
|
161
|
+
body.moderation = resolved.moderation;
|
|
162
|
+
}
|
|
163
|
+
if (resolved.outputCompression !== undefined) {
|
|
164
|
+
body.output_compression = resolved.outputCompression;
|
|
165
|
+
}
|
|
166
|
+
if (resolved.otherParams !== undefined) {
|
|
167
|
+
Object.assign(body, resolved.otherParams);
|
|
168
|
+
}
|
|
169
|
+
/* c8 ignore next 1 - optional logger */
|
|
170
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`Image generation: model=${config.model}, n=${resolved.n}`);
|
|
171
|
+
return fetchJson(`${config.baseUrl}/images/generations`, headers, body, logger, signal);
|
|
172
|
+
}
|
|
173
|
+
/** Builds the multipart /images/edits request with ref images. @internal */
|
|
174
|
+
async function callOpenAiImagesEdits(config, capability, request, headers, resolved, logger, signal) {
|
|
175
|
+
const refs = request.referenceImages; // callers verify refs.length > 0 before calling this function
|
|
176
|
+
const blobsResult = mapResults(refs.map((ref, i) => attachmentToBlob(ref).withErrorFormat((msg) => `reference image ${i}: ${msg}`)));
|
|
177
|
+
/* c8 ignore next 3 - decode failure unreachable via Node's Buffer.from (silently strips invalid input) */
|
|
178
|
+
if (blobsResult.isFailure()) {
|
|
179
|
+
return fail(blobsResult.message);
|
|
180
|
+
}
|
|
181
|
+
const form = new FormData();
|
|
182
|
+
form.append('model', config.model);
|
|
183
|
+
form.append('prompt', request.prompt);
|
|
184
|
+
form.append('n', String(resolved.n));
|
|
185
|
+
if (capability.outputParamStyle !== 'output-format') {
|
|
186
|
+
form.append('response_format', 'b64_json');
|
|
187
|
+
}
|
|
188
|
+
if (resolved.size !== undefined) {
|
|
189
|
+
form.append('size', resolved.size);
|
|
190
|
+
}
|
|
191
|
+
blobsResult.value.forEach((blob, i) => {
|
|
192
|
+
form.append('image[]', blob, `ref-${i}.${extensionForMimeType(refs[i].mimeType)}`);
|
|
193
|
+
});
|
|
194
|
+
/* c8 ignore next 1 - optional logger */
|
|
195
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`Image edit: model=${config.model}, n=${resolved.n}, refs=${refs.length}`);
|
|
196
|
+
return fetchMultipart(`${config.baseUrl}/images/edits`, headers, form, logger, signal);
|
|
197
|
+
}
|
|
198
|
+
/** Calls xAI /images/edits with JSON body (not multipart); up to 3 source images. @internal */
|
|
199
|
+
async function callXaiImagesEdits(config, request, resolved, logger, signal) {
|
|
200
|
+
var _a;
|
|
201
|
+
/* c8 ignore next 1 - defensive: referenceImages always defined when this function is called */
|
|
202
|
+
const refs = (_a = request.referenceImages) !== null && _a !== void 0 ? _a : [];
|
|
203
|
+
if (refs.length > 3) {
|
|
204
|
+
return fail(`xAI image edits supports at most 3 reference images; got ${refs.length}`);
|
|
205
|
+
}
|
|
206
|
+
const images = refs.map((ref) => ({
|
|
207
|
+
type: 'image_url',
|
|
208
|
+
url: `data:${ref.mimeType};base64,${ref.base64}`
|
|
209
|
+
}));
|
|
210
|
+
const body = {
|
|
211
|
+
model: config.model,
|
|
212
|
+
prompt: request.prompt,
|
|
213
|
+
n: resolved.n,
|
|
214
|
+
response_format: 'b64_json',
|
|
215
|
+
image: images
|
|
216
|
+
};
|
|
217
|
+
if (resolved.aspectRatio !== undefined) {
|
|
218
|
+
body.aspect_ratio = resolved.aspectRatio;
|
|
219
|
+
}
|
|
220
|
+
if (resolved.resolution !== undefined) {
|
|
221
|
+
body.resolution = resolved.resolution;
|
|
222
|
+
}
|
|
223
|
+
if (resolved.otherParams !== undefined) {
|
|
224
|
+
Object.assign(body, resolved.otherParams);
|
|
225
|
+
}
|
|
226
|
+
/* c8 ignore next 1 - optional logger */
|
|
227
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`xAI image edit: model=${config.model}, n=${resolved.n}, refs=${refs.length}`);
|
|
228
|
+
return fetchJson(`${config.baseUrl}/images/edits`, bearerAuthHeader(config.apiKey), body, logger, signal);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Normalizes an xAI images API response (OpenAI-shaped) into the provider-neutral
|
|
232
|
+
* generated-image result.
|
|
233
|
+
*
|
|
234
|
+
* @remarks
|
|
235
|
+
* Shared by the generations path and the edits path. These were two independent
|
|
236
|
+
* copies 160 lines apart — identical but for the receiver name — so the
|
|
237
|
+
* `defaultOutputMimeType` fallback and the error prefix each existed twice, and a
|
|
238
|
+
* fix to one would silently have missed the other.
|
|
239
|
+
* @internal
|
|
240
|
+
*/
|
|
241
|
+
function normalizeXaiImageResponse(json, capability) {
|
|
242
|
+
return openAiImageResponse
|
|
243
|
+
.validate(json)
|
|
244
|
+
.withErrorFormat((msg) => `xAI images API response: ${msg}`)
|
|
245
|
+
.onSuccess((response) => succeed({
|
|
246
|
+
images: response.data.map((item) => {
|
|
247
|
+
var _a;
|
|
248
|
+
return ({
|
|
249
|
+
mimeType: (_a = capability.defaultOutputMimeType) !== null && _a !== void 0 ? _a : 'image/jpeg',
|
|
250
|
+
base64: item.b64_json
|
|
251
|
+
});
|
|
252
|
+
})
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
/** Calls xAI /images/generations; uses aspect_ratio instead of size. @internal */
|
|
256
|
+
async function callXaiImageGeneration(config, request, capability, resolved, logger, signal) {
|
|
257
|
+
const headers = bearerAuthHeader(config.apiKey);
|
|
258
|
+
const body = {
|
|
259
|
+
model: config.model,
|
|
260
|
+
prompt: request.prompt,
|
|
261
|
+
n: resolved.n,
|
|
262
|
+
response_format: 'b64_json'
|
|
263
|
+
};
|
|
264
|
+
if (resolved.aspectRatio !== undefined) {
|
|
265
|
+
body.aspect_ratio = resolved.aspectRatio;
|
|
266
|
+
}
|
|
267
|
+
if (resolved.resolution !== undefined) {
|
|
268
|
+
body.resolution = resolved.resolution;
|
|
269
|
+
}
|
|
270
|
+
if (resolved.otherParams !== undefined) {
|
|
271
|
+
Object.assign(body, resolved.otherParams);
|
|
272
|
+
}
|
|
273
|
+
/* c8 ignore next 1 - optional logger */
|
|
274
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`xAI image generation: model=${config.model}, n=${resolved.n}`);
|
|
275
|
+
const fetched = await fetchJson(`${config.baseUrl}/images/generations`, headers, body, logger, signal);
|
|
276
|
+
return fetched.onSuccess((json) => normalizeXaiImageResponse(json, capability));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Gemini `finishReason` values that indicate a normal terminal completion rather
|
|
280
|
+
* than a refusal. `STOP` is set on every successful generation (and on completions
|
|
281
|
+
* that return a text part instead of an image); `MAX_TOKENS` is a benign truncation.
|
|
282
|
+
* A candidate carrying only one of these is NOT a decline — treating it as one would
|
|
283
|
+
* mislabel an ordinary no-image outcome as a policy refusal. @internal
|
|
284
|
+
*/
|
|
285
|
+
const benignGeminiImageFinishReasons = new Set(['STOP', 'MAX_TOKENS']);
|
|
286
|
+
/** Calls Gemini :generateContent for image output; accepts ref images as inlineData. @internal */
|
|
287
|
+
async function callGeminiImageOutGeneration(config, request, resolved, logger, signal) {
|
|
288
|
+
var _a;
|
|
289
|
+
const url = `${config.baseUrl}/models/${config.model}:generateContent`;
|
|
290
|
+
const refs = (_a = request.referenceImages) !== null && _a !== void 0 ? _a : [];
|
|
291
|
+
const parts = [{ text: request.prompt }];
|
|
292
|
+
for (const ref of refs) {
|
|
293
|
+
parts.push({ inlineData: { mimeType: ref.mimeType, data: ref.base64 } });
|
|
294
|
+
}
|
|
295
|
+
const generationConfig = {};
|
|
296
|
+
if (resolved.geminiAspectRatio !== undefined) {
|
|
297
|
+
generationConfig.imageConfig = { aspectRatio: resolved.geminiAspectRatio };
|
|
298
|
+
}
|
|
299
|
+
if (resolved.otherParams !== undefined) {
|
|
300
|
+
Object.assign(generationConfig, resolved.otherParams);
|
|
301
|
+
}
|
|
302
|
+
const body = { contents: [{ role: 'user', parts }] };
|
|
303
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
304
|
+
body.generationConfig = generationConfig;
|
|
305
|
+
}
|
|
306
|
+
const headers = geminiAuthHeader(config.apiKey);
|
|
307
|
+
/* c8 ignore next 1 - optional logger */
|
|
308
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`Gemini image-out: model=${config.model}, refs=${refs.length}`);
|
|
309
|
+
return (await fetchJson(url, headers, body, logger, signal)).onSuccess((json) => geminiImageOutResponse
|
|
310
|
+
.validate(json)
|
|
311
|
+
.withErrorFormat((msg) => `Gemini image API response: ${msg}`)
|
|
312
|
+
.onSuccess((response) => {
|
|
313
|
+
var _a, _b;
|
|
314
|
+
const images = [];
|
|
315
|
+
for (const candidate of response.candidates) {
|
|
316
|
+
for (const part of (_b = (_a = candidate.content) === null || _a === void 0 ? void 0 : _a.parts) !== null && _b !== void 0 ? _b : []) {
|
|
317
|
+
if (part.inlineData) {
|
|
318
|
+
images.push({
|
|
319
|
+
mimeType: part.inlineData.mimeType,
|
|
320
|
+
base64: part.inlineData.data
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (images.length === 0) {
|
|
326
|
+
// A candidate with no image parts is a *decline* only when it carries a
|
|
327
|
+
// refusal-shaped finishReason — i.e. present and not a benign terminal reason
|
|
328
|
+
// (`STOP`/`MAX_TOKENS`). A normal completion that emitted text-instead-of-image
|
|
329
|
+
// carries `finishReason: 'STOP'` and must fall through to the no-image message.
|
|
330
|
+
const declined = response.candidates.find((candidate) => candidate.finishReason !== undefined &&
|
|
331
|
+
!benignGeminiImageFinishReasons.has(candidate.finishReason));
|
|
332
|
+
if ((declined === null || declined === void 0 ? void 0 : declined.finishReason) !== undefined) {
|
|
333
|
+
// Truthiness (not `!== undefined`) so an empty-string finishMessage is treated
|
|
334
|
+
// as "no message" and produces no dangling ` — ` separator.
|
|
335
|
+
const suffix = declined.finishMessage ? ` — ${declined.finishMessage}` : '';
|
|
336
|
+
return fail(`Gemini image generation declined: ${declined.finishReason}${suffix}`);
|
|
337
|
+
}
|
|
338
|
+
return fail('Gemini image API response: no image parts in response');
|
|
339
|
+
}
|
|
340
|
+
return succeed({ images });
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// Image generation — dispatcher
|
|
345
|
+
// ============================================================================
|
|
346
|
+
/**
|
|
347
|
+
* Calls the appropriate image-generation API for a given provider. Routes by the
|
|
348
|
+
* `format` field of the resolved {@link IAiImageModelCapability}:
|
|
349
|
+
* `'openai-images'`, `'xai-images'`, `'xai-images-edits'`, or
|
|
350
|
+
* `'gemini-image-out'`. Rejects up front if `referenceImages` is set but the
|
|
351
|
+
* capability does not declare `acceptsImageReferenceInput`.
|
|
352
|
+
* @param params - Request parameters including descriptor, API key, and prompt
|
|
353
|
+
* @public
|
|
354
|
+
*/
|
|
355
|
+
export async function callProviderImageGeneration(params) {
|
|
356
|
+
var _a, _b, _c;
|
|
357
|
+
const { descriptor, apiKey, params: request, modelOverride, logger, signal, endpoint } = params;
|
|
358
|
+
if (!supportsImageGeneration(descriptor)) {
|
|
359
|
+
return fail(`provider "${descriptor.id}" does not support image generation`);
|
|
360
|
+
}
|
|
361
|
+
const baseUrlResult = resolveEffectiveBaseUrl(descriptor, endpoint);
|
|
362
|
+
if (baseUrlResult.isFailure()) {
|
|
363
|
+
return fail(baseUrlResult.message);
|
|
364
|
+
}
|
|
365
|
+
const modelResult = resolveProviderModel(descriptor, modelOverride, 'image');
|
|
366
|
+
if (modelResult.isFailure()) {
|
|
367
|
+
return fail(modelResult.message);
|
|
368
|
+
}
|
|
369
|
+
const model = modelResult.value;
|
|
370
|
+
const capability = resolveImageCapability(descriptor, model);
|
|
371
|
+
if (capability === undefined) {
|
|
372
|
+
return fail(`provider "${descriptor.id}" does not support image generation for model "${model}"`);
|
|
373
|
+
}
|
|
374
|
+
if (((_b = (_a = request.referenceImages) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 && !capability.acceptsImageReferenceInput) {
|
|
375
|
+
return fail(`model "${model}" does not support reference images`);
|
|
376
|
+
}
|
|
377
|
+
const resolved = resolveImageOptions(model, capability, request.options);
|
|
378
|
+
const validationResult = validateResolvedOptions(model, capability, resolved);
|
|
379
|
+
if (validationResult.isFailure()) {
|
|
380
|
+
return fail(validationResult.message);
|
|
381
|
+
}
|
|
382
|
+
const config = {
|
|
383
|
+
baseUrl: baseUrlResult.value,
|
|
384
|
+
apiKey,
|
|
385
|
+
model
|
|
386
|
+
};
|
|
387
|
+
/* c8 ignore next 6 - optional logger diagnostic output */
|
|
388
|
+
if (logger) {
|
|
389
|
+
logger.info(`AI image generation: provider=${descriptor.id}, format=${capability.format}, ` +
|
|
390
|
+
`model=${config.model}`);
|
|
391
|
+
}
|
|
392
|
+
switch (capability.format) {
|
|
393
|
+
case 'openai-images':
|
|
394
|
+
return callOpenAiImageGeneration(config, request, capability, resolved, logger, signal);
|
|
395
|
+
case 'xai-images':
|
|
396
|
+
return callXaiImageGeneration(config, request, capability, resolved, logger, signal);
|
|
397
|
+
case 'xai-images-edits': {
|
|
398
|
+
const refs = (_c = request.referenceImages) !== null && _c !== void 0 ? _c : [];
|
|
399
|
+
if (refs.length > 0) {
|
|
400
|
+
const editsResult = await callXaiImagesEdits(config, request, resolved, logger, signal);
|
|
401
|
+
return editsResult.onSuccess((json) => normalizeXaiImageResponse(json, capability));
|
|
402
|
+
}
|
|
403
|
+
return callXaiImageGeneration(config, request, capability, resolved, logger, signal);
|
|
404
|
+
}
|
|
405
|
+
case 'gemini-image-out':
|
|
406
|
+
return callGeminiImageOutGeneration(config, request, resolved, logger, signal);
|
|
407
|
+
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
408
|
+
default: {
|
|
409
|
+
const _exhaustive = capability.format;
|
|
410
|
+
return fail(`unsupported image API format: ${String(_exhaustive)}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// ============================================================================
|
|
415
|
+
// Proxied image generation
|
|
416
|
+
// ============================================================================
|
|
417
|
+
/**
|
|
418
|
+
* Calls the image-generation endpoint on a proxy server instead of calling
|
|
419
|
+
* the provider API directly from the browser.
|
|
420
|
+
* Endpoint: `POST ${proxyUrl}/api/ai/image-generation`. Request body:
|
|
421
|
+
* `{providerId, apiKey, params, modelOverride?}`. The proxy handles descriptor
|
|
422
|
+
* lookup, model resolution, provider dispatch, and response normalization
|
|
423
|
+
* (including repackaging `referenceImages` for the upstream wire format).
|
|
424
|
+
* Error body `{error: string}` is surfaced as `proxy: ${error}`.
|
|
425
|
+
* @param proxyUrl - Base URL of the proxy server
|
|
426
|
+
* @param params - Same parameters as {@link callProviderImageGeneration}
|
|
427
|
+
* @public
|
|
428
|
+
*/
|
|
429
|
+
export async function callProxiedImageGeneration(proxyUrl, params) {
|
|
430
|
+
const { descriptor, apiKey, params: request, modelOverride, logger, signal } = params;
|
|
431
|
+
const body = {
|
|
432
|
+
providerId: descriptor.id,
|
|
433
|
+
apiKey,
|
|
434
|
+
params: request
|
|
435
|
+
};
|
|
436
|
+
if (modelOverride !== undefined) {
|
|
437
|
+
body.modelOverride = modelOverride;
|
|
438
|
+
}
|
|
439
|
+
/* c8 ignore next 1 - optional logger */
|
|
440
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`AI image proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);
|
|
441
|
+
const url = `${proxyUrl}/api/ai/image-generation`;
|
|
442
|
+
const jsonResult = await fetchJson(url, {}, body, logger, signal);
|
|
443
|
+
if (jsonResult.isFailure()) {
|
|
444
|
+
return fail(jsonResult.message);
|
|
445
|
+
}
|
|
446
|
+
const response = jsonResult.value;
|
|
447
|
+
if (typeof response.error === 'string') {
|
|
448
|
+
return fail(`proxy: ${response.error}`);
|
|
449
|
+
}
|
|
450
|
+
return proxiedImageGenerationResponse
|
|
451
|
+
.validate(response)
|
|
452
|
+
.withErrorFormat((msg) => `proxy returned invalid response: ${msg}`);
|
|
453
|
+
}
|
|
454
|
+
//# sourceMappingURL=imageGenerationClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageGenerationClient.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/imageGenerationClient.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Image generation clients for AI assist. One dispatcher over the resolved\n * `IAiImageModelCapability.format`, the adapters it routes to (OpenAI images,\n * xAI generations and edits, Gemini image-out), their response validators, the\n * base64 attachment helpers only this modality uses, and the proxied variant.\n *\n * @packageDocumentation\n */\n\nimport { type JsonObject } from '@fgv/ts-json-base';\nimport { fail, type Logging, mapResults, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\n\nimport {\n type IAiGeneratedImage,\n type IAiImageAttachment,\n type IAiImageGenerationParams,\n type IAiImageGenerationResponse,\n type IAiImageModelCapability,\n type IAiProviderDescriptor,\n type ModelSpec,\n resolveProviderModel\n} from './model';\nimport { bearerAuthHeader, geminiAuthHeader, resolveEffectiveBaseUrl } from './endpoint';\nimport { type IAiApiConfig, fetchJson, fetchMultipart } from './http';\nimport { resolveImageCapability, supportsImageGeneration } from './registry';\nimport {\n resolveImageOptions,\n validateResolvedOptions,\n type IResolvedImageOptions\n} from './imageOptionsResolver';\n\n// ============================================================================\n// Image attachment helpers\n// ============================================================================\n\n/**\n * Decodes a base64-encoded image attachment into a `Blob` suitable for use as\n * a multipart file field. On Node hands the `Buffer` straight to `Blob`\n * (Buffer extends Uint8Array) to skip an intermediate copy; falls back to\n * `atob` in browsers. Inputs come from `FileReader` or prior provider\n * responses, which are trusted to be valid. Note that Node's\n * `Buffer.from(..., 'base64')` silently strips invalid characters rather\n * than throwing, so failures are only observable in the browser path.\n * @internal\n */\nfunction attachmentToBlob(attachment: IAiImageAttachment): Result<Blob> {\n if (typeof Buffer !== 'undefined') {\n return succeed(new Blob([Buffer.from(attachment.base64, 'base64')], { type: attachment.mimeType }));\n }\n /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */\n try {\n const binary = atob(attachment.base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return succeed(new Blob([bytes], { type: attachment.mimeType }));\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return fail(`Invalid base64: ${message}`);\n }\n /* c8 ignore stop */\n}\n\n/**\n * Maps a MIME type to a sensible file extension for multipart filenames.\n * @internal\n */\nfunction extensionForMimeType(mimeType: string): string {\n switch (mimeType) {\n case 'image/png':\n return 'png';\n case 'image/jpeg':\n case 'image/jpg':\n return 'jpg';\n case 'image/webp':\n return 'webp';\n case 'image/gif':\n return 'gif';\n default:\n return 'bin';\n }\n}\n\n// ============================================================================\n// Image generation — request types\n// ============================================================================\n\n/**\n * Parameters for an image-generation request.\n * @public\n */\nexport interface IProviderImageGenerationParams {\n /** The provider descriptor */\n readonly descriptor: IAiProviderDescriptor;\n /** API key for authentication */\n readonly apiKey: string;\n /** The image-generation request */\n readonly params: IAiImageGenerationParams;\n /** Optional model override — string or context-aware map (uses descriptor.defaultModel.image otherwise) */\n readonly modelOverride?: ModelSpec;\n /** Optional logger for request/response observability. */\n readonly logger?: Logging.ILogger;\n /** Optional abort signal for cancelling the in-flight request. */\n readonly signal?: AbortSignal;\n /** Optional override of the descriptor's base URL; per-route suffix is appended unchanged. */\n readonly endpoint?: string;\n}\n\n// ============================================================================\n// Image generation — response validators\n// ============================================================================\n\n// ---- OpenAI / xAI images format ----\n\n/** @internal */\ninterface IOpenAiImageItem {\n b64_json: string;\n revised_prompt?: string;\n}\n/** @internal */\ninterface IOpenAiImageResponse {\n data: IOpenAiImageItem[];\n}\n\nconst openAiImageItem: Validator<IOpenAiImageItem> = Validators.object<IOpenAiImageItem>({\n b64_json: Validators.string,\n revised_prompt: Validators.string.optional()\n});\nconst openAiImageResponse: Validator<IOpenAiImageResponse> = Validators.object<IOpenAiImageResponse>({\n data: Validators.arrayOf(openAiImageItem).withConstraint((arr) => arr.length > 0)\n});\n\n// ---- Gemini image-out (`:generateContent` returning image parts) format ----\n\n/** @internal */\ninterface IGeminiImageInlineData {\n mimeType: string;\n data: string;\n}\n/** @internal */\ninterface IGeminiImageOutPart {\n text?: string;\n inlineData?: IGeminiImageInlineData;\n}\n/** @internal */\ninterface IGeminiImageOutContent {\n parts: IGeminiImageOutPart[];\n}\n/** @internal */\ninterface IGeminiImageOutCandidate {\n content?: IGeminiImageOutContent;\n finishReason?: string;\n finishMessage?: string;\n}\n/** @internal */\ninterface IGeminiImageOutResponse {\n candidates: IGeminiImageOutCandidate[];\n}\n\nconst geminiImageInlineData: Validator<IGeminiImageInlineData> = Validators.object<IGeminiImageInlineData>({\n mimeType: Validators.string,\n data: Validators.string\n});\nconst geminiImageOutPart: Validator<IGeminiImageOutPart> = Validators.object<IGeminiImageOutPart>({\n text: Validators.string.optional(),\n inlineData: geminiImageInlineData.optional()\n});\nconst geminiImageOutContent: Validator<IGeminiImageOutContent> = Validators.object<IGeminiImageOutContent>({\n parts: Validators.arrayOf(geminiImageOutPart)\n});\nconst geminiImageOutCandidate: Validator<IGeminiImageOutCandidate> =\n Validators.object<IGeminiImageOutCandidate>({\n content: geminiImageOutContent.optional(),\n finishReason: Validators.string.optional(),\n finishMessage: Validators.string.optional()\n });\nconst geminiImageOutResponse: Validator<IGeminiImageOutResponse> = Validators.object<IGeminiImageOutResponse>(\n {\n candidates: Validators.arrayOf(geminiImageOutCandidate).withConstraint((arr) => arr.length > 0)\n }\n);\n\n// ---- Proxied image generation response ----\n\nconst proxiedGeneratedImage: Validator<IAiGeneratedImage> = Validators.object<IAiGeneratedImage>({\n mimeType: Validators.string,\n base64: Validators.string,\n revisedPrompt: Validators.string.optional()\n});\nconst proxiedImageGenerationResponse: Validator<IAiImageGenerationResponse> =\n Validators.object<IAiImageGenerationResponse>({\n images: Validators.arrayOf(proxiedGeneratedImage).withConstraint((arr) => arr.length > 0)\n });\n\n// ============================================================================\n// Image generation — adapters\n// ============================================================================\n\n/** Routes to /images/generations or /images/edits; handles outputParamStyle. @internal */\nasync function callOpenAiImageGeneration(\n config: IAiApiConfig,\n request: IAiImageGenerationParams,\n capability: IAiImageModelCapability,\n resolved: IResolvedImageOptions,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<IAiImageGenerationResponse>> {\n const refs = request.referenceImages ?? [];\n const headers: Record<string, string> = bearerAuthHeader(config.apiKey);\n\n const effectiveMimeType =\n resolved.outputFormat !== undefined\n ? `image/${resolved.outputFormat}`\n : capability.defaultOutputMimeType ?? 'image/png';\n\n const fetched =\n refs.length > 0\n ? await callOpenAiImagesEdits(config, capability, request, headers, resolved, logger, signal)\n : await callOpenAiImagesGenerations(config, request, headers, resolved, capability, logger, signal);\n\n return fetched.onSuccess((json) =>\n openAiImageResponse\n .validate(json)\n .withErrorFormat((msg) => `OpenAI images API response: ${msg}`)\n .onSuccess((response) =>\n succeed({\n images: response.data.map((item) => ({\n mimeType: effectiveMimeType,\n base64: item.b64_json,\n ...(item.revised_prompt !== undefined ? { revisedPrompt: item.revised_prompt } : {})\n }))\n })\n )\n );\n}\n\n/** Builds the JSON /images/generations request; handles outputParamStyle. @internal */\nfunction callOpenAiImagesGenerations(\n config: IAiApiConfig,\n request: IAiImageGenerationParams,\n headers: Record<string, string>,\n resolved: IResolvedImageOptions,\n capability: IAiImageModelCapability,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<JsonObject>> {\n const body: Record<string, unknown> = {\n model: config.model,\n prompt: request.prompt,\n n: resolved.n\n };\n\n // Output format param — conditional on model capability\n if (capability.outputParamStyle === 'response-format') {\n body.response_format = 'b64_json';\n } else if (capability.outputParamStyle === 'output-format') {\n body.output_format = resolved.outputFormat ?? 'png';\n }\n\n if (resolved.size !== undefined) {\n body.size = resolved.size;\n }\n if (capability.supportsQualityParam && resolved.quality !== undefined) {\n body.quality = resolved.quality;\n }\n if (resolved.seed !== undefined) {\n body.seed = resolved.seed;\n }\n if (resolved.background !== undefined) {\n body.background = resolved.background;\n }\n if (resolved.moderation !== undefined) {\n body.moderation = resolved.moderation;\n }\n if (resolved.outputCompression !== undefined) {\n body.output_compression = resolved.outputCompression;\n }\n if (resolved.otherParams !== undefined) {\n Object.assign(body, resolved.otherParams);\n }\n /* c8 ignore next 1 - optional logger */\n logger?.info(`Image generation: model=${config.model}, n=${resolved.n}`);\n return fetchJson(`${config.baseUrl}/images/generations`, headers, body, logger, signal);\n}\n\n/** Builds the multipart /images/edits request with ref images. @internal */\nasync function callOpenAiImagesEdits(\n config: IAiApiConfig,\n capability: IAiImageModelCapability,\n request: IAiImageGenerationParams,\n headers: Record<string, string>,\n resolved: IResolvedImageOptions,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<JsonObject>> {\n const refs = request.referenceImages!; // callers verify refs.length > 0 before calling this function\n const blobsResult = mapResults(\n refs.map((ref, i) => attachmentToBlob(ref).withErrorFormat((msg) => `reference image ${i}: ${msg}`))\n );\n /* c8 ignore next 3 - decode failure unreachable via Node's Buffer.from (silently strips invalid input) */\n if (blobsResult.isFailure()) {\n return fail(blobsResult.message);\n }\n\n const form = new FormData();\n form.append('model', config.model);\n form.append('prompt', request.prompt);\n form.append('n', String(resolved.n));\n if (capability.outputParamStyle !== 'output-format') {\n form.append('response_format', 'b64_json');\n }\n if (resolved.size !== undefined) {\n form.append('size', resolved.size);\n }\n blobsResult.value.forEach((blob, i) => {\n form.append('image[]', blob, `ref-${i}.${extensionForMimeType(refs[i].mimeType)}`);\n });\n /* c8 ignore next 1 - optional logger */\n logger?.info(`Image edit: model=${config.model}, n=${resolved.n}, refs=${refs.length}`);\n return fetchMultipart(`${config.baseUrl}/images/edits`, headers, form, logger, signal);\n}\n\n/** Calls xAI /images/edits with JSON body (not multipart); up to 3 source images. @internal */\nasync function callXaiImagesEdits(\n config: IAiApiConfig,\n request: IAiImageGenerationParams,\n resolved: IResolvedImageOptions,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<JsonObject>> {\n /* c8 ignore next 1 - defensive: referenceImages always defined when this function is called */\n const refs = request.referenceImages ?? [];\n if (refs.length > 3) {\n return fail(`xAI image edits supports at most 3 reference images; got ${refs.length}`);\n }\n const images = refs.map((ref) => ({\n type: 'image_url',\n url: `data:${ref.mimeType};base64,${ref.base64}`\n }));\n\n const body: Record<string, unknown> = {\n model: config.model,\n prompt: request.prompt,\n n: resolved.n,\n response_format: 'b64_json',\n image: images\n };\n if (resolved.aspectRatio !== undefined) {\n body.aspect_ratio = resolved.aspectRatio;\n }\n if (resolved.resolution !== undefined) {\n body.resolution = resolved.resolution;\n }\n if (resolved.otherParams !== undefined) {\n Object.assign(body, resolved.otherParams);\n }\n /* c8 ignore next 1 - optional logger */\n logger?.info(`xAI image edit: model=${config.model}, n=${resolved.n}, refs=${refs.length}`);\n return fetchJson(`${config.baseUrl}/images/edits`, bearerAuthHeader(config.apiKey), body, logger, signal);\n}\n\n/**\n * Normalizes an xAI images API response (OpenAI-shaped) into the provider-neutral\n * generated-image result.\n *\n * @remarks\n * Shared by the generations path and the edits path. These were two independent\n * copies 160 lines apart — identical but for the receiver name — so the\n * `defaultOutputMimeType` fallback and the error prefix each existed twice, and a\n * fix to one would silently have missed the other.\n * @internal\n */\nfunction normalizeXaiImageResponse(\n json: JsonObject,\n capability: IAiImageModelCapability\n): Result<IAiImageGenerationResponse> {\n return openAiImageResponse\n .validate(json)\n .withErrorFormat((msg) => `xAI images API response: ${msg}`)\n .onSuccess((response) =>\n succeed({\n images: response.data.map((item) => ({\n mimeType: capability.defaultOutputMimeType ?? 'image/jpeg',\n base64: item.b64_json\n }))\n })\n );\n}\n\n/** Calls xAI /images/generations; uses aspect_ratio instead of size. @internal */\nasync function callXaiImageGeneration(\n config: IAiApiConfig,\n request: IAiImageGenerationParams,\n capability: IAiImageModelCapability,\n resolved: IResolvedImageOptions,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<IAiImageGenerationResponse>> {\n const headers: Record<string, string> = bearerAuthHeader(config.apiKey);\n const body: Record<string, unknown> = {\n model: config.model,\n prompt: request.prompt,\n n: resolved.n,\n response_format: 'b64_json'\n };\n if (resolved.aspectRatio !== undefined) {\n body.aspect_ratio = resolved.aspectRatio;\n }\n if (resolved.resolution !== undefined) {\n body.resolution = resolved.resolution;\n }\n if (resolved.otherParams !== undefined) {\n Object.assign(body, resolved.otherParams);\n }\n /* c8 ignore next 1 - optional logger */\n logger?.info(`xAI image generation: model=${config.model}, n=${resolved.n}`);\n const fetched = await fetchJson(`${config.baseUrl}/images/generations`, headers, body, logger, signal);\n return fetched.onSuccess((json) => normalizeXaiImageResponse(json, capability));\n}\n\n/**\n * Gemini `finishReason` values that indicate a normal terminal completion rather\n * than a refusal. `STOP` is set on every successful generation (and on completions\n * that return a text part instead of an image); `MAX_TOKENS` is a benign truncation.\n * A candidate carrying only one of these is NOT a decline — treating it as one would\n * mislabel an ordinary no-image outcome as a policy refusal. @internal\n */\nconst benignGeminiImageFinishReasons: ReadonlySet<string> = new Set(['STOP', 'MAX_TOKENS']);\n\n/** Calls Gemini :generateContent for image output; accepts ref images as inlineData. @internal */\nasync function callGeminiImageOutGeneration(\n config: IAiApiConfig,\n request: IAiImageGenerationParams,\n resolved: IResolvedImageOptions,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<IAiImageGenerationResponse>> {\n const url = `${config.baseUrl}/models/${config.model}:generateContent`;\n const refs = request.referenceImages ?? [];\n const parts: Array<Record<string, unknown>> = [{ text: request.prompt }];\n for (const ref of refs) {\n parts.push({ inlineData: { mimeType: ref.mimeType, data: ref.base64 } });\n }\n\n const generationConfig: Record<string, unknown> = {};\n if (resolved.geminiAspectRatio !== undefined) {\n generationConfig.imageConfig = { aspectRatio: resolved.geminiAspectRatio };\n }\n if (resolved.otherParams !== undefined) {\n Object.assign(generationConfig, resolved.otherParams);\n }\n\n const body: Record<string, unknown> = { contents: [{ role: 'user', parts }] };\n if (Object.keys(generationConfig).length > 0) {\n body.generationConfig = generationConfig;\n }\n const headers: Record<string, string> = geminiAuthHeader(config.apiKey);\n\n /* c8 ignore next 1 - optional logger */\n logger?.info(`Gemini image-out: model=${config.model}, refs=${refs.length}`);\n return (await fetchJson(url, headers, body, logger, signal)).onSuccess((json) =>\n geminiImageOutResponse\n .validate(json)\n .withErrorFormat((msg) => `Gemini image API response: ${msg}`)\n .onSuccess((response) => {\n const images: IAiGeneratedImage[] = [];\n for (const candidate of response.candidates) {\n for (const part of candidate.content?.parts ?? []) {\n if (part.inlineData) {\n images.push({\n mimeType: part.inlineData.mimeType,\n base64: part.inlineData.data\n });\n }\n }\n }\n if (images.length === 0) {\n // A candidate with no image parts is a *decline* only when it carries a\n // refusal-shaped finishReason — i.e. present and not a benign terminal reason\n // (`STOP`/`MAX_TOKENS`). A normal completion that emitted text-instead-of-image\n // carries `finishReason: 'STOP'` and must fall through to the no-image message.\n const declined = response.candidates.find(\n (candidate) =>\n candidate.finishReason !== undefined &&\n !benignGeminiImageFinishReasons.has(candidate.finishReason)\n );\n if (declined?.finishReason !== undefined) {\n // Truthiness (not `!== undefined`) so an empty-string finishMessage is treated\n // as \"no message\" and produces no dangling ` — ` separator.\n const suffix = declined.finishMessage ? ` — ${declined.finishMessage}` : '';\n return fail(`Gemini image generation declined: ${declined.finishReason}${suffix}`);\n }\n return fail('Gemini image API response: no image parts in response');\n }\n return succeed({ images });\n })\n );\n}\n\n// ============================================================================\n// Image generation — dispatcher\n// ============================================================================\n\n/**\n * Calls the appropriate image-generation API for a given provider. Routes by the\n * `format` field of the resolved {@link IAiImageModelCapability}:\n * `'openai-images'`, `'xai-images'`, `'xai-images-edits'`, or\n * `'gemini-image-out'`. Rejects up front if `referenceImages` is set but the\n * capability does not declare `acceptsImageReferenceInput`.\n * @param params - Request parameters including descriptor, API key, and prompt\n * @public\n */\nexport async function callProviderImageGeneration(\n params: IProviderImageGenerationParams\n): Promise<Result<IAiImageGenerationResponse>> {\n const { descriptor, apiKey, params: request, modelOverride, logger, signal, endpoint } = params;\n\n if (!supportsImageGeneration(descriptor)) {\n return fail(`provider \"${descriptor.id}\" does not support image generation`);\n }\n const baseUrlResult = resolveEffectiveBaseUrl(descriptor, endpoint);\n if (baseUrlResult.isFailure()) {\n return fail(baseUrlResult.message);\n }\n\n const modelResult = resolveProviderModel(descriptor, modelOverride, 'image');\n if (modelResult.isFailure()) {\n return fail(modelResult.message);\n }\n const model = modelResult.value;\n const capability = resolveImageCapability(descriptor, model);\n if (capability === undefined) {\n return fail(`provider \"${descriptor.id}\" does not support image generation for model \"${model}\"`);\n }\n if ((request.referenceImages?.length ?? 0) > 0 && !capability.acceptsImageReferenceInput) {\n return fail(`model \"${model}\" does not support reference images`);\n }\n\n const resolved = resolveImageOptions(model, capability, request.options);\n const validationResult = validateResolvedOptions(model, capability, resolved);\n if (validationResult.isFailure()) {\n return fail<IAiImageGenerationResponse>(validationResult.message);\n }\n const config: IAiApiConfig = {\n baseUrl: baseUrlResult.value,\n apiKey,\n model\n };\n /* c8 ignore next 6 - optional logger diagnostic output */\n if (logger) {\n logger.info(\n `AI image generation: provider=${descriptor.id}, format=${capability.format}, ` +\n `model=${config.model}`\n );\n }\n\n switch (capability.format) {\n case 'openai-images':\n return callOpenAiImageGeneration(config, request, capability, resolved, logger, signal);\n case 'xai-images':\n return callXaiImageGeneration(config, request, capability, resolved, logger, signal);\n case 'xai-images-edits': {\n const refs = request.referenceImages ?? [];\n if (refs.length > 0) {\n const editsResult = await callXaiImagesEdits(config, request, resolved, logger, signal);\n return editsResult.onSuccess((json) => normalizeXaiImageResponse(json, capability));\n }\n return callXaiImageGeneration(config, request, capability, resolved, logger, signal);\n }\n case 'gemini-image-out':\n return callGeminiImageOutGeneration(config, request, resolved, logger, signal);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = capability.format;\n return fail(`unsupported image API format: ${String(_exhaustive)}`);\n }\n }\n}\n\n// ============================================================================\n// Proxied image generation\n// ============================================================================\n\n/**\n * Calls the image-generation endpoint on a proxy server instead of calling\n * the provider API directly from the browser.\n * Endpoint: `POST ${proxyUrl}/api/ai/image-generation`. Request body:\n * `{providerId, apiKey, params, modelOverride?}`. The proxy handles descriptor\n * lookup, model resolution, provider dispatch, and response normalization\n * (including repackaging `referenceImages` for the upstream wire format).\n * Error body `{error: string}` is surfaced as `proxy: ${error}`.\n * @param proxyUrl - Base URL of the proxy server\n * @param params - Same parameters as {@link callProviderImageGeneration}\n * @public\n */\nexport async function callProxiedImageGeneration(\n proxyUrl: string,\n params: IProviderImageGenerationParams\n): Promise<Result<IAiImageGenerationResponse>> {\n const { descriptor, apiKey, params: request, modelOverride, logger, signal } = params;\n\n const body: Record<string, unknown> = {\n providerId: descriptor.id,\n apiKey,\n params: request\n };\n if (modelOverride !== undefined) {\n body.modelOverride = modelOverride;\n }\n\n /* c8 ignore next 1 - optional logger */\n logger?.info(`AI image proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);\n\n const url = `${proxyUrl}/api/ai/image-generation`;\n const jsonResult = await fetchJson(url, {}, body, logger, signal);\n if (jsonResult.isFailure()) {\n return fail(jsonResult.message);\n }\n\n const response = jsonResult.value as Record<string, unknown>;\n if (typeof response.error === 'string') {\n return fail(`proxy: ${response.error}`);\n }\n\n return proxiedImageGenerationResponse\n .validate(response)\n .withErrorFormat((msg) => `proxy returned invalid response: ${msg}`);\n}\n"]}
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
export { AiPrompt, allModelCapabilities, DEFAULT_ANTHROPIC_MAX_TOKENS, DEFAULT_AI_ASSIST, providerApiKeySecretName, allModelSpecKeys, MODEL_SPEC_BASE_KEY, resolveModel, MODEL_ALIAS_SIGIL, resolveModelAlias, resolveProviderModel, isResponsesOnlyModel, isAdaptiveThinkingModel, usesMaxCompletionTokensField, toDataUrl } from './model';
|
|
6
6
|
export { resolveImageOptions, validateResolvedOptions } from './imageOptionsResolver';
|
|
7
7
|
export { allProviderIds, getProviderDescriptors, getProviderDescriptor, resolveImageCapability, supportsImageGeneration, resolveEmbeddingCapability, supportsEmbedding, DEFAULT_MODEL_CAPABILITY_CONFIG } from './registry';
|
|
8
|
-
export { callProviderCompletion, callProxiedCompletion
|
|
8
|
+
export { callProviderCompletion, callProxiedCompletion } from './completionClient';
|
|
9
|
+
export { callProviderImageGeneration, callProxiedImageGeneration } from './imageGenerationClient';
|
|
10
|
+
export { callProviderListModels, callProxiedListModels } from './listModelsClient';
|
|
9
11
|
export { callProviderEmbedding, callProxiedEmbedding } from './embeddingClient';
|
|
10
12
|
export { callProviderCompletionStream, callProxiedCompletionStream, executeClientToolTurn } from './streamingClient';
|
|
11
13
|
export { aiProviderId, aiServerToolType, aiWebSearchToolConfig, aiServerToolConfig, aiToolAnnotations, aiClientToolConfig, aiToolEnablement, aiAssistProviderConfig, aiAssistSettings, modelSpecKey, modelSpec } from './converters';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,EAER,oBAAoB,EAcpB,4BAA4B,EAe5B,iBAAiB,EAEjB,wBAAwB,EAoCxB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EAEZ,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,SAAS,EAiBV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAEL,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,0BAA0B,EAC1B,iBAAiB,EACjB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,EAER,oBAAoB,EAcpB,4BAA4B,EAe5B,iBAAiB,EAEjB,wBAAwB,EAoCxB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EAEZ,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,SAAS,EAiBV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAEL,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,0BAA0B,EAC1B,iBAAiB,EACjB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EAEtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAE3B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EAEtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EAErB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,EAE3B,qBAAqB,EAItB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,qBAAqB,EAKtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EAIvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,6BAA6B,EAAgC,MAAM,2BAA2B,CAAC","sourcesContent":["/**\n * AI assist packlet - provider registry, prompt class, settings, and API client.\n * @packageDocumentation\n */\n\nexport {\n AiPrompt,\n type AiModelCapability,\n allModelCapabilities,\n type AiProviderId,\n type AiServerToolType,\n type AiServerToolConfig,\n type AiToolConfig,\n type IAiWebSearchToolConfig,\n type IAiClientToolConfig,\n type IAiToolAnnotations,\n type IAiClientTool,\n type IAiClientToolCallSummary,\n type IAiClientToolContinuation,\n type IAiClientToolTurnResult,\n type IAiToolEnablement,\n type IAiCompletionResponse,\n DEFAULT_ANTHROPIC_MAX_TOKENS,\n type IChatMessage,\n type IChatRequest,\n type AiApiFormat,\n type AiImageApiFormat,\n type AiEmbeddingApiFormat,\n type AiEmbeddingTaskType,\n type IAiEmbeddingModelCapability,\n type IAiEmbeddingParams,\n type IAiEmbeddingUsage,\n type IAiEmbeddingResult,\n type IAiImageModelCapability,\n type IAiProviderDescriptor,\n type IAiAssistProviderConfig,\n type IAiAssistSettings,\n DEFAULT_AI_ASSIST,\n type IAiAssistKeyStore,\n providerApiKeySecretName,\n type IAiImageAttachment,\n type IAiImageData,\n type AiImageSize,\n type AiImageQuality,\n type GptImageSize,\n type GptImageQuality,\n type GptImageModelNames,\n type GrokImagineModelNames,\n type GeminiFlashImageModelNames,\n type IGptImageGenerationConfig,\n type IGrokImagineImageGenerationConfig,\n type IGeminiFlashImageGenerationConfig,\n type IGptImageModelOptions,\n type IGrokImagineModelOptions,\n type IGeminiFlashImageModelOptions,\n type IOtherModelOptions,\n type IModelFamilyConfig,\n type IAiImageGenerationOptions,\n type IAiImageGenerationParams,\n type IAiGeneratedImage,\n type IAiImageGenerationResponse,\n type IAiModelCapabilityRule,\n type IAiModelCapabilityConfig,\n type IAiModelInfo,\n type IAiStreamEvent,\n type IAiStreamTextDelta,\n type IAiStreamToolEvent,\n type IAiStreamToolUseStart,\n type IAiStreamToolUseDelta,\n type IAiStreamToolUseComplete,\n type IAiStreamDone,\n type IAiStreamError,\n type ModelSpec,\n type ModelSpecKey,\n type IModelSpecMap,\n allModelSpecKeys,\n MODEL_SPEC_BASE_KEY,\n resolveModel,\n type IModelAliasMap,\n MODEL_ALIAS_SIGIL,\n resolveModelAlias,\n resolveProviderModel,\n isResponsesOnlyModel,\n isAdaptiveThinkingModel,\n usesMaxCompletionTokensField,\n toDataUrl,\n type AiThinkingMode,\n type IThinkingConfig,\n type IThinkingProviderConfig,\n type IAnthropicThinkingOptions,\n type IOpenAiThinkingOptions,\n type IGeminiThinkingOptions,\n type IXAiThinkingOptions,\n type IOtherThinkingOptions,\n type IAnthropicThinkingConfig,\n type IOpenAiThinkingConfig,\n type IGeminiThinkingConfig,\n type IXAiThinkingConfig,\n type AnthropicThinkingModelNames,\n type OpenAiThinkingModelNames,\n type GeminiThinkingModelNames,\n type XAiThinkingModelNames\n} from './model';\n\nexport {\n type IResolvedImageOptions,\n resolveImageOptions,\n validateResolvedOptions\n} from './imageOptionsResolver';\n\nexport {\n allProviderIds,\n getProviderDescriptors,\n getProviderDescriptor,\n resolveImageCapability,\n supportsImageGeneration,\n resolveEmbeddingCapability,\n supportsEmbedding,\n DEFAULT_MODEL_CAPABILITY_CONFIG\n} from './registry';\n\nexport {\n callProviderCompletion,\n callProxiedCompletion,\n type IProviderCompletionParams\n} from './completionClient';\n\nexport {\n callProviderImageGeneration,\n callProxiedImageGeneration,\n type IProviderImageGenerationParams\n} from './imageGenerationClient';\n\nexport {\n callProviderListModels,\n callProxiedListModels,\n type IProviderListModelsParams\n} from './listModelsClient';\n\nexport {\n callProviderEmbedding,\n callProxiedEmbedding,\n type IProviderEmbeddingParams\n} from './embeddingClient';\n\nexport {\n callProviderCompletionStream,\n callProxiedCompletionStream,\n type IProviderCompletionStreamParams,\n executeClientToolTurn,\n type IExecuteClientToolTurnParams,\n type IExecuteClientToolTurnResult,\n type IToolExecutionDecision\n} from './streamingClient';\n\nexport {\n aiProviderId,\n aiServerToolType,\n aiWebSearchToolConfig,\n aiServerToolConfig,\n aiToolAnnotations,\n aiClientToolConfig,\n aiToolEnablement,\n aiAssistProviderConfig,\n aiAssistSettings,\n modelSpecKey,\n modelSpec\n} from './converters';\n\nexport { resolveEffectiveTools } from './toolFormats';\n\nexport {\n classifyJsonParseFailure,\n extractJsonText,\n fencedStringifiedJson,\n type IFencedStringifiedJsonExtractorOptions,\n type IFencedStringifiedJsonOptions,\n type JsonParseFailureReason,\n type JsonTextExtractor\n} from './jsonResponse';\n\nexport {\n generateJsonCompletion,\n SMART_JSON_PROMPT_HINT,\n type IGenerateJsonCompletionParams,\n type IGenerateJsonCompletionResult,\n type JsonPromptHint\n} from './jsonCompletion';\n\nexport { anthropicEffortToBudgetTokens, type IResolvedThinkingConfig } from './thinkingOptionsResolver';\n"]}
|
|
@@ -37,7 +37,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
37
37
|
* @packageDocumentation
|
|
38
38
|
*/
|
|
39
39
|
import { fail, succeed } from '@fgv/ts-utils';
|
|
40
|
-
import { callProviderCompletion } from './
|
|
40
|
+
import { callProviderCompletion } from './completionClient';
|
|
41
41
|
import { fencedStringifiedJson } from './jsonResponse';
|
|
42
42
|
/**
|
|
43
43
|
* Default system-prompt suffix appended when {@link AiAssist.IGenerateJsonCompletionParams.promptHint}
|