@fgv/ts-extras 5.1.0-37 → 5.1.0-39
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/apiClient.js +66 -111
- package/dist/packlets/ai-assist/apiClient.js.map +1 -1
- package/dist/packlets/ai-assist/converters.js +16 -3
- package/dist/packlets/ai-assist/converters.js.map +1 -1
- package/dist/packlets/ai-assist/embeddingClient.js +5 -6
- package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/dist/packlets/ai-assist/imageOptionsResolver.js +0 -19
- package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -1
- package/dist/packlets/ai-assist/index.js +2 -2
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/model.js +139 -7
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/registry.js +58 -51
- package/dist/packlets/ai-assist/registry.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +38 -11
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +187 -36
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/common.js +13 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +12 -4
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +6 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -3
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +5 -2
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/dist/packlets/ai-assist/streamingClient.js +16 -16
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
- package/dist/packlets/crypto-utils/converters.js +18 -0
- package/dist/packlets/crypto-utils/converters.js.map +1 -1
- package/dist/packlets/crypto-utils/index.browser.js +2 -2
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
- package/dist/packlets/crypto-utils/index.js +2 -2
- package/dist/packlets/crypto-utils/index.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/keyStore.js +19 -4
- package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/model.js.map +1 -1
- package/dist/packlets/crypto-utils/model.js.map +1 -1
- package/dist/packlets/crypto-utils/spkiHelpers.js +104 -25
- package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -1
- package/dist/ts-extras.d.ts +377 -132
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/packlets/ai-assist/apiClient.d.ts +12 -2
- package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/apiClient.js +65 -110
- package/lib/packlets/ai-assist/apiClient.js.map +1 -1
- package/lib/packlets/ai-assist/converters.d.ts +8 -2
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
- package/lib/packlets/ai-assist/converters.js +17 -4
- package/lib/packlets/ai-assist/converters.js.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.js +4 -5
- package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +0 -8
- package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -1
- package/lib/packlets/ai-assist/imageOptionsResolver.js +0 -19
- package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -1
- package/lib/packlets/ai-assist/index.d.ts +3 -3
- package/lib/packlets/ai-assist/index.d.ts.map +1 -1
- package/lib/packlets/ai-assist/index.js +6 -1
- package/lib/packlets/ai-assist/index.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +181 -98
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js +143 -8
- package/lib/packlets/ai-assist/model.js.map +1 -1
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
- package/lib/packlets/ai-assist/registry.js +58 -51
- package/lib/packlets/ai-assist/registry.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +37 -10
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +82 -3
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +187 -35
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +24 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js +14 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +10 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +12 -4
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +6 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -3
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +5 -2
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.js +15 -15
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
- package/lib/packlets/crypto-utils/converters.d.ts +9 -1
- package/lib/packlets/crypto-utils/converters.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/converters.js +19 -1
- package/lib/packlets/crypto-utils/converters.js.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.js +6 -2
- package/lib/packlets/crypto-utils/index.browser.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 +6 -2
- package/lib/packlets/crypto-utils/index.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +9 -2
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/keyStore.js +19 -4
- package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/model.d.ts +7 -0
- package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/model.js.map +1 -1
- package/lib/packlets/crypto-utils/model.d.ts +11 -1
- package/lib/packlets/crypto-utils/model.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/model.js.map +1 -1
- package/lib/packlets/crypto-utils/spkiHelpers.d.ts +59 -2
- package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/spkiHelpers.js +108 -25
- package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -1
- package/package.json +7 -7
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { isJsonObject } from '@fgv/ts-json-base';
|
|
30
30
|
import { fail, mapResults, succeed, Validators } from '@fgv/ts-utils';
|
|
31
|
-
import { allModelCapabilities,
|
|
31
|
+
import { allModelCapabilities, isResponsesOnlyModel, resolveProviderModel } from './model';
|
|
32
32
|
import { anthropicEffortToBudgetTokens, checkTemperatureConflict, mergeThinkingConfig, providerDiscriminatorForId } from './thinkingOptionsResolver';
|
|
33
33
|
import { buildAnthropicMessages, buildGeminiContents, buildMessages, buildOpenAiChatUserContent, buildOpenAiResponsesUserContent, normalizeOutboundMessages, splitChatRequest } from './chatRequestBuilders';
|
|
34
34
|
import { bearerAuthHeader, resolveEffectiveBaseUrl } from './endpoint';
|
|
@@ -221,14 +221,14 @@ const geminiResponse = Validators.object({
|
|
|
221
221
|
* Works for xAI Grok, OpenAI, Groq, and Mistral.
|
|
222
222
|
* @internal
|
|
223
223
|
*/
|
|
224
|
-
async function callOpenAiCompletion(config, prompt, head, temperature
|
|
224
|
+
async function callOpenAiCompletion(config, prompt, head, temperature, logger, signal, resolvedThinking) {
|
|
225
225
|
var _a;
|
|
226
226
|
const url = `${config.baseUrl}/chat/completions`;
|
|
227
227
|
const messages = buildMessages(prompt.system, buildOpenAiChatUserContent(prompt), {
|
|
228
228
|
head
|
|
229
229
|
});
|
|
230
230
|
const effort = (_a = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.openAiEffort) !== null && _a !== void 0 ? _a : resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.xaiEffort;
|
|
231
|
-
const body = Object.assign(Object.assign({ model: config.model, messages }, (
|
|
231
|
+
const body = Object.assign(Object.assign({ model: config.model, messages }, (temperature !== undefined ? { temperature } : {})), (effort !== undefined && config.model !== 'grok-4' ? { reasoning_effort: effort } : {}));
|
|
232
232
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
|
|
233
233
|
Object.assign(body, resolvedThinking.otherParams);
|
|
234
234
|
}
|
|
@@ -274,14 +274,14 @@ function extractResponsesApiText(output) {
|
|
|
274
274
|
* Used when tools are configured for an openai-format provider.
|
|
275
275
|
* @internal
|
|
276
276
|
*/
|
|
277
|
-
async function callOpenAiResponsesCompletion(config, prompt, tools, head, temperature
|
|
277
|
+
async function callOpenAiResponsesCompletion(config, prompt, tools = [], head, temperature, logger, signal, resolvedThinking) {
|
|
278
278
|
var _a;
|
|
279
279
|
const url = `${config.baseUrl}/responses`;
|
|
280
280
|
const input = buildMessages(prompt.system, buildOpenAiResponsesUserContent(prompt), {
|
|
281
281
|
head
|
|
282
282
|
});
|
|
283
283
|
const effort = (_a = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.openAiEffort) !== null && _a !== void 0 ? _a : resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.xaiEffort;
|
|
284
|
-
const body = Object.assign(Object.assign({ model: config.model, input, tools: toResponsesApiTools(tools) }, (
|
|
284
|
+
const body = Object.assign(Object.assign(Object.assign({ model: config.model, input }, (tools.length > 0 ? { tools: toResponsesApiTools(tools) } : {})), (temperature !== undefined ? { temperature } : {})), (effort !== undefined && config.model !== 'grok-4' ? { reasoning: { effort } } : {}));
|
|
285
285
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
|
|
286
286
|
Object.assign(body, resolvedThinking.otherParams);
|
|
287
287
|
}
|
|
@@ -328,10 +328,10 @@ function extractAnthropicText(content) {
|
|
|
328
328
|
return succeed(textParts.join(''));
|
|
329
329
|
}
|
|
330
330
|
/** Calls the Anthropic Messages API with optional tool support. @internal */
|
|
331
|
-
async function callAnthropicCompletion(config, prompt, head, temperature
|
|
331
|
+
async function callAnthropicCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking) {
|
|
332
332
|
const url = `${config.baseUrl}/messages`;
|
|
333
333
|
const messages = buildAnthropicMessages(prompt, { head });
|
|
334
|
-
const body = Object.assign({ model: config.model, system: prompt.system, messages, max_tokens: 4096 }, (
|
|
334
|
+
const body = Object.assign({ model: config.model, system: prompt.system, messages, max_tokens: 4096 }, (temperature !== undefined ? { temperature } : {}));
|
|
335
335
|
const effort = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.anthropicEffort;
|
|
336
336
|
if (effort !== undefined) {
|
|
337
337
|
body.thinking = { type: 'enabled', budget_tokens: anthropicEffortToBudgetTokens(effort) };
|
|
@@ -378,10 +378,14 @@ async function callAnthropicCompletion(config, prompt, head, temperature = 0.7,
|
|
|
378
378
|
* When tools are configured, includes Google Search grounding.
|
|
379
379
|
* @internal
|
|
380
380
|
*/
|
|
381
|
-
async function callGeminiCompletion(config, prompt, head, temperature
|
|
381
|
+
async function callGeminiCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking) {
|
|
382
382
|
const url = `${config.baseUrl}/models/${config.model}:generateContent`;
|
|
383
383
|
const contents = buildGeminiContents(prompt, { head });
|
|
384
|
-
|
|
384
|
+
// Temperature is sent only when explicitly provided; otherwise Gemini's default applies.
|
|
385
|
+
const generationConfig = {};
|
|
386
|
+
if (temperature !== undefined) {
|
|
387
|
+
generationConfig.temperature = temperature;
|
|
388
|
+
}
|
|
385
389
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.geminiThinkingBudget) !== undefined) {
|
|
386
390
|
generationConfig.thinkingConfig = { thinkingBudget: resolvedThinking.geminiThinkingBudget };
|
|
387
391
|
}
|
|
@@ -430,8 +434,7 @@ async function callGeminiCompletion(config, prompt, head, temperature = 0.7, log
|
|
|
430
434
|
* @public
|
|
431
435
|
*/
|
|
432
436
|
export async function callProviderCompletion(params) {
|
|
433
|
-
|
|
434
|
-
const { descriptor, apiKey, system, messages, temperature, modelOverride, logger, tools, signal, endpoint, thinking } = params;
|
|
437
|
+
const { descriptor, apiKey, system, messages, temperature, modelOverride, tier, logger, tools, signal, endpoint, thinking } = params;
|
|
435
438
|
const splitResult = splitChatRequest(system, messages);
|
|
436
439
|
if (splitResult.isFailure()) {
|
|
437
440
|
return fail(splitResult.message);
|
|
@@ -446,14 +449,14 @@ export async function callProviderCompletion(params) {
|
|
|
446
449
|
}
|
|
447
450
|
const hasTools = tools !== undefined && tools.length > 0;
|
|
448
451
|
const discriminator = providerDiscriminatorForId(descriptor.id);
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
452
|
+
// The quality tier is the only completion-model selector; thinking and tools
|
|
453
|
+
// are orthogonal request params/capabilities and never pick a model.
|
|
454
|
+
const modelContext = tier;
|
|
455
|
+
const modelResult = resolveProviderModel(descriptor, modelOverride, modelContext);
|
|
456
|
+
if (modelResult.isFailure()) {
|
|
457
|
+
return fail(modelResult.message);
|
|
458
|
+
}
|
|
459
|
+
const model = modelResult.value;
|
|
457
460
|
let resolvedThinking;
|
|
458
461
|
if (thinking !== undefined) {
|
|
459
462
|
if (discriminator !== undefined) {
|
|
@@ -469,7 +472,6 @@ export async function callProviderCompletion(params) {
|
|
|
469
472
|
}
|
|
470
473
|
}
|
|
471
474
|
}
|
|
472
|
-
const effectiveTemperature = temperature !== null && temperature !== void 0 ? temperature : 0.7;
|
|
473
475
|
const config = {
|
|
474
476
|
baseUrl: baseUrlResult.value,
|
|
475
477
|
apiKey,
|
|
@@ -484,14 +486,16 @@ export async function callProviderCompletion(params) {
|
|
|
484
486
|
}
|
|
485
487
|
switch (descriptor.apiFormat) {
|
|
486
488
|
case 'openai':
|
|
487
|
-
|
|
488
|
-
|
|
489
|
+
// Responses-API-only models (e.g. gpt-5.5-pro) 400 on /chat/completions, so they route
|
|
490
|
+
// to the Responses path even with no tools requested — same path the tools case uses.
|
|
491
|
+
if (hasTools || isResponsesOnlyModel(descriptor, config.model)) {
|
|
492
|
+
return callOpenAiResponsesCompletion(config, prompt, tools, head, temperature, logger, signal, resolvedThinking);
|
|
489
493
|
}
|
|
490
|
-
return callOpenAiCompletion(config, prompt, head,
|
|
494
|
+
return callOpenAiCompletion(config, prompt, head, temperature, logger, signal, resolvedThinking);
|
|
491
495
|
case 'anthropic':
|
|
492
|
-
return callAnthropicCompletion(config, prompt, head,
|
|
496
|
+
return callAnthropicCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking);
|
|
493
497
|
case 'gemini':
|
|
494
|
-
return callGeminiCompletion(config, prompt, head,
|
|
498
|
+
return callGeminiCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking);
|
|
495
499
|
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
496
500
|
default: {
|
|
497
501
|
const _exhaustive = descriptor.apiFormat;
|
|
@@ -506,13 +510,6 @@ const openAiImageItem = Validators.object({
|
|
|
506
510
|
const openAiImageResponse = Validators.object({
|
|
507
511
|
data: Validators.arrayOf(openAiImageItem).withConstraint((arr) => arr.length > 0)
|
|
508
512
|
});
|
|
509
|
-
const imagenPrediction = Validators.object({
|
|
510
|
-
bytesBase64Encoded: Validators.string,
|
|
511
|
-
mimeType: Validators.string.optional()
|
|
512
|
-
});
|
|
513
|
-
const imagenResponse = Validators.object({
|
|
514
|
-
predictions: Validators.arrayOf(imagenPrediction).withConstraint((arr) => arr.length > 0)
|
|
515
|
-
});
|
|
516
513
|
const geminiImageInlineData = Validators.object({
|
|
517
514
|
mimeType: Validators.string,
|
|
518
515
|
data: Validators.string
|
|
@@ -522,11 +519,12 @@ const geminiImageOutPart = Validators.object({
|
|
|
522
519
|
inlineData: geminiImageInlineData.optional()
|
|
523
520
|
});
|
|
524
521
|
const geminiImageOutContent = Validators.object({
|
|
525
|
-
parts: Validators.arrayOf(geminiImageOutPart)
|
|
522
|
+
parts: Validators.arrayOf(geminiImageOutPart)
|
|
526
523
|
});
|
|
527
524
|
const geminiImageOutCandidate = Validators.object({
|
|
528
|
-
content: geminiImageOutContent,
|
|
529
|
-
finishReason: Validators.string.optional()
|
|
525
|
+
content: geminiImageOutContent.optional(),
|
|
526
|
+
finishReason: Validators.string.optional(),
|
|
527
|
+
finishMessage: Validators.string.optional()
|
|
530
528
|
});
|
|
531
529
|
const geminiImageOutResponse = Validators.object({
|
|
532
530
|
candidates: Validators.arrayOf(geminiImageOutCandidate).withConstraint((arr) => arr.length > 0)
|
|
@@ -593,9 +591,6 @@ function callOpenAiImagesGenerations(config, request, headers, resolved, capabil
|
|
|
593
591
|
if (resolved.seed !== undefined) {
|
|
594
592
|
body.seed = resolved.seed;
|
|
595
593
|
}
|
|
596
|
-
if (resolved.style !== undefined) {
|
|
597
|
-
body.style = resolved.style;
|
|
598
|
-
}
|
|
599
594
|
if (resolved.background !== undefined) {
|
|
600
595
|
body.background = resolved.background;
|
|
601
596
|
}
|
|
@@ -703,6 +698,14 @@ async function callXaiImageGeneration(config, request, capability, resolved, log
|
|
|
703
698
|
})
|
|
704
699
|
})));
|
|
705
700
|
}
|
|
701
|
+
/**
|
|
702
|
+
* Gemini `finishReason` values that indicate a normal terminal completion rather
|
|
703
|
+
* than a refusal. `STOP` is set on every successful generation (and on completions
|
|
704
|
+
* that return a text part instead of an image); `MAX_TOKENS` is a benign truncation.
|
|
705
|
+
* A candidate carrying only one of these is NOT a decline — treating it as one would
|
|
706
|
+
* mislabel an ordinary no-image outcome as a policy refusal. @internal
|
|
707
|
+
*/
|
|
708
|
+
const benignGeminiImageFinishReasons = new Set(['STOP', 'MAX_TOKENS']);
|
|
706
709
|
/** Calls Gemini :generateContent for image output; accepts ref images as inlineData. @internal */
|
|
707
710
|
async function callGeminiImageOutGeneration(config, request, resolved, logger, signal) {
|
|
708
711
|
var _a;
|
|
@@ -732,9 +735,10 @@ async function callGeminiImageOutGeneration(config, request, resolved, logger, s
|
|
|
732
735
|
.validate(json)
|
|
733
736
|
.withErrorFormat((msg) => `Gemini image API response: ${msg}`)
|
|
734
737
|
.onSuccess((response) => {
|
|
738
|
+
var _a, _b;
|
|
735
739
|
const images = [];
|
|
736
740
|
for (const candidate of response.candidates) {
|
|
737
|
-
for (const part of candidate.content.parts) {
|
|
741
|
+
for (const part of (_b = (_a = candidate.content) === null || _a === void 0 ? void 0 : _a.parts) !== null && _b !== void 0 ? _b : []) {
|
|
738
742
|
if (part.inlineData) {
|
|
739
743
|
images.push({
|
|
740
744
|
mimeType: part.inlineData.mimeType,
|
|
@@ -744,80 +748,30 @@ async function callGeminiImageOutGeneration(config, request, resolved, logger, s
|
|
|
744
748
|
}
|
|
745
749
|
}
|
|
746
750
|
if (images.length === 0) {
|
|
751
|
+
// A candidate with no image parts is a *decline* only when it carries a
|
|
752
|
+
// refusal-shaped finishReason — i.e. present and not a benign terminal reason
|
|
753
|
+
// (`STOP`/`MAX_TOKENS`). A normal completion that emitted text-instead-of-image
|
|
754
|
+
// carries `finishReason: 'STOP'` and must fall through to the no-image message.
|
|
755
|
+
const declined = response.candidates.find((candidate) => candidate.finishReason !== undefined &&
|
|
756
|
+
!benignGeminiImageFinishReasons.has(candidate.finishReason));
|
|
757
|
+
if ((declined === null || declined === void 0 ? void 0 : declined.finishReason) !== undefined) {
|
|
758
|
+
// Truthiness (not `!== undefined`) so an empty-string finishMessage is treated
|
|
759
|
+
// as "no message" and produces no dangling ` — ` separator.
|
|
760
|
+
const suffix = declined.finishMessage ? ` — ${declined.finishMessage}` : '';
|
|
761
|
+
return fail(`Gemini image generation declined: ${declined.finishReason}${suffix}`);
|
|
762
|
+
}
|
|
747
763
|
return fail('Gemini image API response: no image parts in response');
|
|
748
764
|
}
|
|
749
765
|
return succeed({ images });
|
|
750
766
|
}));
|
|
751
767
|
}
|
|
752
|
-
/** Calls the Gemini Imagen :predict endpoint with Imagen 4 params. @internal */
|
|
753
|
-
async function callImagenGeneration(config, request, resolved, logger, signal) {
|
|
754
|
-
const url = `${config.baseUrl}/models/${config.model}:predict`;
|
|
755
|
-
const parameters = {
|
|
756
|
-
sampleCount: resolved.n
|
|
757
|
-
};
|
|
758
|
-
if (resolved.imagenAspectRatio !== undefined) {
|
|
759
|
-
parameters.aspectRatio = resolved.imagenAspectRatio;
|
|
760
|
-
}
|
|
761
|
-
if (resolved.imageSize !== undefined) {
|
|
762
|
-
parameters.imageSize = resolved.imageSize;
|
|
763
|
-
}
|
|
764
|
-
if (resolved.addWatermark !== undefined) {
|
|
765
|
-
parameters.addWatermark = resolved.addWatermark;
|
|
766
|
-
}
|
|
767
|
-
if (resolved.enhancePrompt !== undefined) {
|
|
768
|
-
parameters.enhancePrompt = resolved.enhancePrompt;
|
|
769
|
-
}
|
|
770
|
-
if (resolved.imagenOutputMimeType !== undefined || resolved.imagenOutputCompressionQuality !== undefined) {
|
|
771
|
-
const outputOptions = {};
|
|
772
|
-
if (resolved.imagenOutputMimeType !== undefined) {
|
|
773
|
-
outputOptions.mimeType = resolved.imagenOutputMimeType;
|
|
774
|
-
}
|
|
775
|
-
if (resolved.imagenOutputCompressionQuality !== undefined) {
|
|
776
|
-
outputOptions.compressionQuality = resolved.imagenOutputCompressionQuality;
|
|
777
|
-
}
|
|
778
|
-
parameters.outputOptions = outputOptions;
|
|
779
|
-
}
|
|
780
|
-
if (resolved.personGeneration !== undefined) {
|
|
781
|
-
parameters.personGeneration = resolved.personGeneration;
|
|
782
|
-
}
|
|
783
|
-
if (resolved.seed !== undefined) {
|
|
784
|
-
parameters.seed = resolved.seed;
|
|
785
|
-
}
|
|
786
|
-
if (resolved.otherParams !== undefined) {
|
|
787
|
-
Object.assign(parameters, resolved.otherParams);
|
|
788
|
-
}
|
|
789
|
-
const body = {
|
|
790
|
-
instances: [{ prompt: request.prompt }],
|
|
791
|
-
parameters
|
|
792
|
-
};
|
|
793
|
-
const headers = { 'x-goog-api-key': config.apiKey };
|
|
794
|
-
/* c8 ignore next 1 - optional logger */
|
|
795
|
-
logger === null || logger === void 0 ? void 0 : logger.info(`Imagen generation: model=${config.model}, n=${parameters.sampleCount}`);
|
|
796
|
-
const jsonResult = await fetchJson(url, headers, body, logger, signal);
|
|
797
|
-
if (jsonResult.isFailure()) {
|
|
798
|
-
return fail(jsonResult.message);
|
|
799
|
-
}
|
|
800
|
-
return imagenResponse
|
|
801
|
-
.validate(jsonResult.value)
|
|
802
|
-
.withErrorFormat((msg) => `Imagen API response: ${msg}`)
|
|
803
|
-
.onSuccess((response) => {
|
|
804
|
-
const images = response.predictions.map((p) => {
|
|
805
|
-
var _a;
|
|
806
|
-
return ({
|
|
807
|
-
mimeType: (_a = p.mimeType) !== null && _a !== void 0 ? _a : 'image/png',
|
|
808
|
-
base64: p.bytesBase64Encoded
|
|
809
|
-
});
|
|
810
|
-
});
|
|
811
|
-
return succeed({ images });
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
768
|
// ============================================================================
|
|
815
769
|
// Image generation — dispatcher
|
|
816
770
|
// ============================================================================
|
|
817
771
|
/**
|
|
818
772
|
* Calls the appropriate image-generation API for a given provider. Routes by the
|
|
819
773
|
* `format` field of the resolved {@link IAiImageModelCapability}:
|
|
820
|
-
* `'openai-images'`, `'xai-images'`, `'xai-images-edits'`,
|
|
774
|
+
* `'openai-images'`, `'xai-images'`, `'xai-images-edits'`, or
|
|
821
775
|
* `'gemini-image-out'`. Rejects up front if `referenceImages` is set but the
|
|
822
776
|
* capability does not declare `acceptsImageReferenceInput`.
|
|
823
777
|
* @param params - Request parameters including descriptor, API key, and prompt
|
|
@@ -833,12 +787,11 @@ export async function callProviderImageGeneration(params) {
|
|
|
833
787
|
if (baseUrlResult.isFailure()) {
|
|
834
788
|
return fail(baseUrlResult.message);
|
|
835
789
|
}
|
|
836
|
-
const
|
|
837
|
-
if (
|
|
838
|
-
return fail(
|
|
839
|
-
`pass modelOverride or set descriptor.defaultModel ` +
|
|
840
|
-
`(a plain string, or an object with an "image" entry)`);
|
|
790
|
+
const modelResult = resolveProviderModel(descriptor, modelOverride, 'image');
|
|
791
|
+
if (modelResult.isFailure()) {
|
|
792
|
+
return fail(modelResult.message);
|
|
841
793
|
}
|
|
794
|
+
const model = modelResult.value;
|
|
842
795
|
const capability = resolveImageCapability(descriptor, model);
|
|
843
796
|
if (capability === undefined) {
|
|
844
797
|
return fail(`provider "${descriptor.id}" does not support image generation for model "${model}"`);
|
|
@@ -885,8 +838,6 @@ export async function callProviderImageGeneration(params) {
|
|
|
885
838
|
}
|
|
886
839
|
return callXaiImageGeneration(config, request, capability, resolved, logger, signal);
|
|
887
840
|
}
|
|
888
|
-
case 'gemini-imagen':
|
|
889
|
-
return callImagenGeneration(config, request, resolved, logger, signal);
|
|
890
841
|
case 'gemini-image-out':
|
|
891
842
|
return callGeminiImageOutGeneration(config, request, resolved, logger, signal);
|
|
892
843
|
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
@@ -1180,9 +1131,13 @@ export async function callProxiedCompletion(proxyUrl, params) {
|
|
|
1180
1131
|
const body = {
|
|
1181
1132
|
providerId: descriptor.id,
|
|
1182
1133
|
apiKey,
|
|
1183
|
-
messages: normalizeOutboundMessages(splitResult.value)
|
|
1184
|
-
temperature: temperature !== null && temperature !== void 0 ? temperature : 0.7
|
|
1134
|
+
messages: normalizeOutboundMessages(splitResult.value)
|
|
1185
1135
|
};
|
|
1136
|
+
// Temperature is forwarded only when explicitly provided, matching the direct path — the proxy
|
|
1137
|
+
// omits it from the upstream request so the provider default applies.
|
|
1138
|
+
if (temperature !== undefined) {
|
|
1139
|
+
body.temperature = temperature;
|
|
1140
|
+
}
|
|
1186
1141
|
if (system !== undefined) {
|
|
1187
1142
|
body.system = system;
|
|
1188
1143
|
}
|