@fgv/ts-extras 5.1.0-43 → 5.1.0-45
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 +38 -14
- package/dist/packlets/ai-assist/apiClient.js.map +1 -1
- package/dist/packlets/ai-assist/index.js +1 -1
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/jsonResponse.js +16 -5
- package/dist/packlets/ai-assist/jsonResponse.js.map +1 -1
- package/dist/packlets/ai-assist/model.js +94 -0
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/registry.js +40 -21
- package/dist/packlets/ai-assist/registry.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +17 -6
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +5 -5
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +4 -1
- 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 +5 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +6 -1
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/dist/packlets/ai-assist/streamingClient.js +6 -6
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
- package/dist/ts-extras.d.ts +152 -9
- package/lib/packlets/ai-assist/apiClient.d.ts +9 -0
- package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/apiClient.js +37 -13
- package/lib/packlets/ai-assist/apiClient.js.map +1 -1
- package/lib/packlets/ai-assist/index.d.ts +1 -1
- 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/jsonResponse.d.ts +7 -0
- package/lib/packlets/ai-assist/jsonResponse.d.ts.map +1 -1
- package/lib/packlets/ai-assist/jsonResponse.js +16 -5
- package/lib/packlets/ai-assist/jsonResponse.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +110 -9
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js +98 -1
- 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 +40 -21
- 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 +17 -6
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +9 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +4 -4
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +9 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +4 -1
- 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 +5 -1
- 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 +6 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.js +5 -5
- package/lib/packlets/ai-assist/streamingClient.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, isResponsesOnlyModel, resolveProviderModel } from './model';
|
|
31
|
+
import { allModelCapabilities, DEFAULT_ANTHROPIC_MAX_TOKENS, isAdaptiveThinkingModel, isResponsesOnlyModel, resolveProviderModel, usesMaxCompletionTokensField } 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,15 @@ 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, logger, signal, resolvedThinking) {
|
|
224
|
+
async function callOpenAiCompletion(config, prompt, head, temperature, logger, signal, resolvedThinking, maxTokens, useMaxCompletionTokensField = false) {
|
|
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
|
|
231
|
+
const maxTokensField = useMaxCompletionTokensField ? 'max_completion_tokens' : 'max_tokens';
|
|
232
|
+
const body = Object.assign(Object.assign(Object.assign({ model: config.model, messages }, (temperature !== undefined ? { temperature } : {})), (effort !== undefined && config.model !== 'grok-4' ? { reasoning_effort: effort } : {})), (maxTokens !== undefined ? { [maxTokensField]: maxTokens } : {}));
|
|
232
233
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
|
|
233
234
|
Object.assign(body, resolvedThinking.otherParams);
|
|
234
235
|
}
|
|
@@ -274,7 +275,7 @@ function extractResponsesApiText(output) {
|
|
|
274
275
|
* Used when tools are configured for an openai-format provider.
|
|
275
276
|
* @internal
|
|
276
277
|
*/
|
|
277
|
-
async function callOpenAiResponsesCompletion(config, prompt, tools = [], head, temperature, logger, signal, resolvedThinking) {
|
|
278
|
+
async function callOpenAiResponsesCompletion(config, prompt, tools = [], head, temperature, logger, signal, resolvedThinking, maxTokens) {
|
|
278
279
|
var _a;
|
|
279
280
|
const url = `${config.baseUrl}/responses`;
|
|
280
281
|
const input = buildMessages(prompt.system, buildOpenAiResponsesUserContent(prompt), {
|
|
@@ -282,6 +283,10 @@ async function callOpenAiResponsesCompletion(config, prompt, tools = [], head, t
|
|
|
282
283
|
});
|
|
283
284
|
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
285
|
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 } } : {}));
|
|
286
|
+
// Shared by OpenAI and xAI — both route through the Responses API with the same field name.
|
|
287
|
+
if (maxTokens !== undefined) {
|
|
288
|
+
body.max_output_tokens = maxTokens;
|
|
289
|
+
}
|
|
285
290
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
|
|
286
291
|
Object.assign(body, resolvedThinking.otherParams);
|
|
287
292
|
}
|
|
@@ -328,13 +333,24 @@ function extractAnthropicText(content) {
|
|
|
328
333
|
return succeed(textParts.join(''));
|
|
329
334
|
}
|
|
330
335
|
/** Calls the Anthropic Messages API with optional tool support. @internal */
|
|
331
|
-
async function callAnthropicCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking) {
|
|
336
|
+
async function callAnthropicCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking, useAdaptiveThinking = false, maxTokens) {
|
|
332
337
|
const url = `${config.baseUrl}/messages`;
|
|
333
338
|
const messages = buildAnthropicMessages(prompt, { head });
|
|
334
|
-
const body = Object.assign({ model: config.model, system: prompt.system, messages,
|
|
339
|
+
const body = Object.assign({ model: config.model, system: prompt.system, messages,
|
|
340
|
+
// Anthropic's Messages API requires max_tokens on every request — see
|
|
341
|
+
// AiAssist.DEFAULT_ANTHROPIC_MAX_TOKENS for why only this provider defaults it.
|
|
342
|
+
max_tokens: maxTokens !== null && maxTokens !== void 0 ? maxTokens : DEFAULT_ANTHROPIC_MAX_TOKENS }, (temperature !== undefined ? { temperature } : {}));
|
|
335
343
|
const effort = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.anthropicEffort;
|
|
336
344
|
if (effort !== undefined) {
|
|
337
|
-
|
|
345
|
+
if (useAdaptiveThinking) {
|
|
346
|
+
// Claude 5 family: adaptive thinking — no budget_tokens; effort moves to the
|
|
347
|
+
// top-level output_config block. See AiAssist.isAdaptiveThinkingModel.
|
|
348
|
+
body.thinking = { type: 'adaptive' };
|
|
349
|
+
body.output_config = { effort };
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
body.thinking = { type: 'enabled', budget_tokens: anthropicEffortToBudgetTokens(effort) };
|
|
353
|
+
}
|
|
338
354
|
}
|
|
339
355
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
|
|
340
356
|
Object.assign(body, resolvedThinking.otherParams);
|
|
@@ -378,7 +394,7 @@ async function callAnthropicCompletion(config, prompt, head, temperature, logger
|
|
|
378
394
|
* When tools are configured, includes Google Search grounding.
|
|
379
395
|
* @internal
|
|
380
396
|
*/
|
|
381
|
-
async function callGeminiCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking) {
|
|
397
|
+
async function callGeminiCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking, maxTokens) {
|
|
382
398
|
const url = `${config.baseUrl}/models/${config.model}:generateContent`;
|
|
383
399
|
const contents = buildGeminiContents(prompt, { head });
|
|
384
400
|
// Temperature is sent only when explicitly provided; otherwise Gemini's default applies.
|
|
@@ -386,6 +402,9 @@ async function callGeminiCompletion(config, prompt, head, temperature, logger, t
|
|
|
386
402
|
if (temperature !== undefined) {
|
|
387
403
|
generationConfig.temperature = temperature;
|
|
388
404
|
}
|
|
405
|
+
if (maxTokens !== undefined) {
|
|
406
|
+
generationConfig.maxOutputTokens = maxTokens;
|
|
407
|
+
}
|
|
389
408
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.geminiThinkingBudget) !== undefined) {
|
|
390
409
|
generationConfig.thinkingConfig = { thinkingBudget: resolvedThinking.geminiThinkingBudget };
|
|
391
410
|
}
|
|
@@ -434,7 +453,7 @@ async function callGeminiCompletion(config, prompt, head, temperature, logger, t
|
|
|
434
453
|
* @public
|
|
435
454
|
*/
|
|
436
455
|
export async function callProviderCompletion(params) {
|
|
437
|
-
const { descriptor, apiKey, system, messages, temperature, modelOverride, tier, logger, tools, signal, endpoint, thinking } = params;
|
|
456
|
+
const { descriptor, apiKey, system, messages, temperature, modelOverride, tier, logger, tools, signal, endpoint, thinking, maxTokens } = params;
|
|
438
457
|
const splitResult = splitChatRequest(system, messages);
|
|
439
458
|
if (splitResult.isFailure()) {
|
|
440
459
|
return fail(splitResult.message);
|
|
@@ -489,13 +508,13 @@ export async function callProviderCompletion(params) {
|
|
|
489
508
|
// Responses-API-only models (e.g. gpt-5.5-pro) 400 on /chat/completions, so they route
|
|
490
509
|
// to the Responses path even with no tools requested — same path the tools case uses.
|
|
491
510
|
if (hasTools || isResponsesOnlyModel(descriptor, config.model)) {
|
|
492
|
-
return callOpenAiResponsesCompletion(config, prompt, tools, head, temperature, logger, signal, resolvedThinking);
|
|
511
|
+
return callOpenAiResponsesCompletion(config, prompt, tools, head, temperature, logger, signal, resolvedThinking, maxTokens);
|
|
493
512
|
}
|
|
494
|
-
return callOpenAiCompletion(config, prompt, head, temperature, logger, signal, resolvedThinking);
|
|
513
|
+
return callOpenAiCompletion(config, prompt, head, temperature, logger, signal, resolvedThinking, maxTokens, usesMaxCompletionTokensField(descriptor));
|
|
495
514
|
case 'anthropic':
|
|
496
|
-
return callAnthropicCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking);
|
|
515
|
+
return callAnthropicCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking, isAdaptiveThinkingModel(descriptor, config.model), maxTokens);
|
|
497
516
|
case 'gemini':
|
|
498
|
-
return callGeminiCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking);
|
|
517
|
+
return callGeminiCompletion(config, prompt, head, temperature, logger, tools, signal, resolvedThinking, maxTokens);
|
|
499
518
|
/* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
|
|
500
519
|
default: {
|
|
501
520
|
const _exhaustive = descriptor.apiFormat;
|
|
@@ -1120,7 +1139,7 @@ export async function callProxiedListModels(proxyUrl, params) {
|
|
|
1120
1139
|
* @public
|
|
1121
1140
|
*/
|
|
1122
1141
|
export async function callProxiedCompletion(proxyUrl, params) {
|
|
1123
|
-
const { descriptor, apiKey, system, messages, temperature, modelOverride, logger, tools, signal, thinking } = params;
|
|
1142
|
+
const { descriptor, apiKey, system, messages, temperature, modelOverride, logger, tools, signal, thinking, maxTokens } = params;
|
|
1124
1143
|
const splitResult = splitChatRequest(system, messages);
|
|
1125
1144
|
if (splitResult.isFailure()) {
|
|
1126
1145
|
return fail(splitResult.message);
|
|
@@ -1150,6 +1169,11 @@ export async function callProxiedCompletion(proxyUrl, params) {
|
|
|
1150
1169
|
if (thinking !== undefined) {
|
|
1151
1170
|
body.thinking = thinking;
|
|
1152
1171
|
}
|
|
1172
|
+
// Forwarded only when explicitly provided; the proxy is responsible for mapping it to the
|
|
1173
|
+
// correct upstream provider field (see AiAssist.usesMaxCompletionTokensField).
|
|
1174
|
+
if (maxTokens !== undefined) {
|
|
1175
|
+
body.maxTokens = maxTokens;
|
|
1176
|
+
}
|
|
1153
1177
|
/* c8 ignore next 1 - optional logger */
|
|
1154
1178
|
logger === null || logger === void 0 ? void 0 : logger.info(`AI proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);
|
|
1155
1179
|
const url = `${proxyUrl}/api/ai/completion`;
|