@nexus-cortex/core 4.124.44 → 4.124.46
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/.env.defaults +144 -0
- package/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -1
- package/dist/adapters/GatewayTranslationLayer.js +27 -1
- package/dist/adapters/GatewayTranslationLayer.js.map +1 -1
- package/dist/adapters/ServerSideToolDetection.d.ts.map +1 -1
- package/dist/adapters/ServerSideToolDetection.js +5 -1
- package/dist/adapters/ServerSideToolDetection.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +16 -0
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/orchestrator/APIClient.d.ts.map +1 -1
- package/dist/orchestrator/APIClient.js +120 -25
- package/dist/orchestrator/APIClient.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts +22 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +210 -83
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/orchestrator/appendOnlyHistory.d.ts +39 -0
- package/dist/orchestrator/appendOnlyHistory.d.ts.map +1 -0
- package/dist/orchestrator/appendOnlyHistory.js +79 -0
- package/dist/orchestrator/appendOnlyHistory.js.map +1 -0
- package/dist/orchestrator/transportCacheFixes.d.ts +22 -0
- package/dist/orchestrator/transportCacheFixes.d.ts.map +1 -0
- package/dist/orchestrator/transportCacheFixes.js +114 -0
- package/dist/orchestrator/transportCacheFixes.js.map +1 -0
- package/dist/orchestrator/transportEffortFixes.d.ts +62 -0
- package/dist/orchestrator/transportEffortFixes.d.ts.map +1 -0
- package/dist/orchestrator/transportEffortFixes.js +114 -0
- package/dist/orchestrator/transportEffortFixes.js.map +1 -0
- package/dist/orchestrator/wallGuard.d.ts +3 -1
- package/dist/orchestrator/wallGuard.d.ts.map +1 -1
- package/dist/orchestrator/wallGuard.js +15 -2
- package/dist/orchestrator/wallGuard.js.map +1 -1
- package/dist/tools/ClientSideToolFilter.d.ts +9 -2
- package/dist/tools/ClientSideToolFilter.d.ts.map +1 -1
- package/dist/tools/ClientSideToolFilter.js +26 -3
- package/dist/tools/ClientSideToolFilter.js.map +1 -1
- package/dist/training/DecisionStore.d.ts +1 -1
- package/dist/training/DecisionStore.d.ts.map +1 -1
- package/dist/training/DecisionStore.js.map +1 -1
- package/package.json +3 -3
|
@@ -20,6 +20,8 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
20
20
|
import { assertCompletionBody } from './completionBodyGuard.js';
|
|
21
21
|
import { parseHFCompletion, normalizeToolCallArguments } from '../models/hfSpace/normalize.js';
|
|
22
22
|
import { isResponsesInlineRemindersEnabled, inlineRemindersForResponses, isResponsesStopReasonEnabled, isAnthropicHistoryCacheEnabled, applyAnthropicHistoryCache, countAnthropicBreakpoints, } from './transportFixes.js';
|
|
23
|
+
import { isGeminiSystemFixEnabled, isOpenAICacheKeyEnabled, isStreamUsageEnabled, applyAnthropicCacheLevers, streamFinalUsage, } from './transportCacheFixes.js';
|
|
24
|
+
import { isAnthropicEffortEnabled, isGeminiToolsThinkingEnabled, anthropicOutputEffort, anthropicMaxEffortBudget, } from './transportEffortFixes.js';
|
|
23
25
|
import { anthropicCredentialService, CredentialError } from '../config/AnthropicCredentialService.js';
|
|
24
26
|
/**
|
|
25
27
|
* Anthropic families on the adaptive-thinking-only request surface — sending
|
|
@@ -359,6 +361,10 @@ export class APIClient {
|
|
|
359
361
|
if (enableCaching && isAnthropicHistoryCacheEnabled()) {
|
|
360
362
|
anthropicRequest.messages = applyAnthropicHistoryCache(anthropicRequest.messages, countAnthropicBreakpoints({ system: anthropicRequest.system, tools: anthropicRequest.tools })).messages;
|
|
361
363
|
}
|
|
364
|
+
// R225 (DARK): CORTEX_ANTHROPIC_AUTO_CACHE top-level automatic cache_control (free slot only) +
|
|
365
|
+
// CORTEX_ANTHROPIC_CACHE_TTL=1h on every marker. Anthropic only; no-op with both flags off.
|
|
366
|
+
if (enableCaching)
|
|
367
|
+
applyAnthropicCacheLevers(anthropicRequest);
|
|
362
368
|
// Phase 2.8: Enable extended thinking for Claude 4+ models with reasoning support
|
|
363
369
|
// Requires beta header: anthropic-beta: interleaved-thinking-2025-05-14
|
|
364
370
|
// Skip if disableThinking option is set (used for continuation requests)
|
|
@@ -377,6 +383,11 @@ export class APIClient {
|
|
|
377
383
|
process.env.DEBUG_THINKING === 'true'
|
|
378
384
|
? { type: 'adaptive', display: 'summarized' }
|
|
379
385
|
: { type: 'adaptive' };
|
|
386
|
+
// DARK (CORTEX_ANTHROPIC_EFFORT): the action/request effort reaches adaptive families as output_config.effort.
|
|
387
|
+
const outputEffort = isAnthropicEffortEnabled() ? anthropicOutputEffort(reasoningEffort) : undefined;
|
|
388
|
+
if (outputEffort) {
|
|
389
|
+
anthropicRequest.output_config = { ...(anthropicRequest.output_config || {}), effort: outputEffort };
|
|
390
|
+
}
|
|
380
391
|
}
|
|
381
392
|
else {
|
|
382
393
|
const budgetMap = {
|
|
@@ -385,7 +396,11 @@ export class APIClient {
|
|
|
385
396
|
medium: 10000,
|
|
386
397
|
high: 50000,
|
|
387
398
|
};
|
|
388
|
-
|
|
399
|
+
// DARK (CORTEX_ANTHROPIC_EFFORT): 'max' gets a large budget clamped below max_tokens (was the 10000 fallback).
|
|
400
|
+
const maxBudget = isAnthropicEffortEnabled() && reasoningEffort === 'max'
|
|
401
|
+
? anthropicMaxEffortBudget(anthropicRequest.max_tokens)
|
|
402
|
+
: undefined;
|
|
403
|
+
const budgetTokens = maxBudget ?? (reasoningEffort ? (budgetMap[reasoningEffort] || 10000) : 10000);
|
|
389
404
|
anthropicRequest.thinking = {
|
|
390
405
|
type: 'enabled',
|
|
391
406
|
budget_tokens: budgetTokens
|
|
@@ -742,6 +757,8 @@ export class APIClient {
|
|
|
742
757
|
// deepseek runs (reasoning + tools, no forced choice) are fine — so drop reasoning ONLY when a
|
|
743
758
|
// tool_choice is actually forced. OpenAI's R19b case (tools alone) stays as-is.
|
|
744
759
|
const hasForcedToolChoice = !!request.toolChoice;
|
|
760
|
+
// R19b STAYS (live probe 2026-10-03): gpt-5.4-nano AND gpt-5.6-luna still 400 "Function tools with reasoning_effort are not
|
|
761
|
+
// supported … in /v1/chat/completions. To use function tools, use /v1/responses" — CORTEX_OPENAI_TOOLS_REASONING is Responses-only.
|
|
745
762
|
const dropReasoningForToolCompat = (willAttachTools && isChatCompletionsRoute && modelConfig.provider === 'openai') ||
|
|
746
763
|
(hasForcedToolChoice && isChatCompletionsRoute && modelConfig.provider === 'deepseek');
|
|
747
764
|
// Enable reasoning for OpenAI models that support it (GPT-5 family, o-series)
|
|
@@ -784,6 +801,16 @@ export class APIClient {
|
|
|
784
801
|
chatRequest.tools = request.tools;
|
|
785
802
|
this.applyToolChoice(chatRequest, request, 'chat/completions'); // §13-B1 forced tool_choice
|
|
786
803
|
}
|
|
804
|
+
// R225 (DARK, CORTEX_OPENAI_CACHE_KEY): OpenAI routes same-key requests to the same prompt cache.
|
|
805
|
+
// OpenAI provider only (other chat/completions providers may reject the unknown field).
|
|
806
|
+
if (isOpenAICacheKeyEnabled() && modelConfig.provider === 'openai' && request.conversationId) {
|
|
807
|
+
chatRequest.prompt_cache_key = request.conversationId;
|
|
808
|
+
}
|
|
809
|
+
// R224a (DARK, CORTEX_STREAM_USAGE): ask for the final usage chunk on streaming requests (otherwise the
|
|
810
|
+
// reassembled message reports zeros). Body-level, after the prefix — cache-neutral.
|
|
811
|
+
if (opts.stream && isStreamUsageEnabled()) {
|
|
812
|
+
chatRequest.stream_options = { include_usage: true };
|
|
813
|
+
}
|
|
787
814
|
this.logChatCompletionsPayload(chatRequest, request.modelId, opts.stream);
|
|
788
815
|
return { client, chatRequest };
|
|
789
816
|
}
|
|
@@ -1054,6 +1081,10 @@ export class APIClient {
|
|
|
1054
1081
|
// opt-in ZDR mode: "DESIGN NOTE — stateless ZDR mode" in
|
|
1055
1082
|
// ResponsesAPIAdapter.ts (adapters/).
|
|
1056
1083
|
}
|
|
1084
|
+
// R225 (DARK, CORTEX_OPENAI_CACHE_KEY): the same session-scoped cache routing key for OpenAI Responses.
|
|
1085
|
+
if (isOpenAICacheKeyEnabled() && modelConfig.provider?.toLowerCase() === 'openai' && request.conversationId) {
|
|
1086
|
+
responsesRequest.prompt_cache_key = request.conversationId;
|
|
1087
|
+
}
|
|
1057
1088
|
// Set system context as instructions (enables provider-side caching).
|
|
1058
1089
|
// R63: request.systemMessage (the R28 static system prompt) was previously
|
|
1059
1090
|
// dropped on this path — only <system-reminder> text mined from user
|
|
@@ -1165,6 +1196,12 @@ export class APIClient {
|
|
|
1165
1196
|
if (request.tools && request.tools.length > 0) {
|
|
1166
1197
|
return this.sendGenerateContentHTTP(request, modelConfig, apiKey);
|
|
1167
1198
|
}
|
|
1199
|
+
// R229 (DARK, CORTEX_GEMINI_SYSTEM_FIX): the @google/generative-ai SDK (v0.2.1, v1 endpoint) has no
|
|
1200
|
+
// systemInstruction support, so a request carrying a system prompt goes through the v1beta REST builder,
|
|
1201
|
+
// which sends it as system_instruction.
|
|
1202
|
+
if (isGeminiSystemFixEnabled() && request.systemMessage) {
|
|
1203
|
+
return this.sendGenerateContentHTTP(request, modelConfig, apiKey);
|
|
1204
|
+
}
|
|
1168
1205
|
// For non-tool requests, use SDK as before
|
|
1169
1206
|
if (!this.googleClient) {
|
|
1170
1207
|
this.googleClient = new GoogleGenerativeAI(apiKey);
|
|
@@ -1212,6 +1249,11 @@ export class APIClient {
|
|
|
1212
1249
|
contents: request.messages,
|
|
1213
1250
|
generation_config: generationConfig // Use the nested object directly
|
|
1214
1251
|
};
|
|
1252
|
+
// R229 (DARK, CORTEX_GEMINI_SYSTEM_FIX): deliver the system prompt (the streaming HTTP builder always did;
|
|
1253
|
+
// this non-streaming builder never did). Same snake_case shape as streamGenerateContentHTTP.
|
|
1254
|
+
if (isGeminiSystemFixEnabled() && request.systemMessage) {
|
|
1255
|
+
requestBody.system_instruction = { parts: [{ text: request.systemMessage }] };
|
|
1256
|
+
}
|
|
1215
1257
|
// Add tools if present
|
|
1216
1258
|
if (request.tools && request.tools.length > 0) {
|
|
1217
1259
|
// REST API uses snake_case "function_declarations", not camelCase "functionDeclarations"
|
|
@@ -1353,6 +1395,9 @@ export class APIClient {
|
|
|
1353
1395
|
if (enableCaching && isAnthropicHistoryCacheEnabled()) {
|
|
1354
1396
|
anthropicRequest.messages = applyAnthropicHistoryCache(anthropicRequest.messages, countAnthropicBreakpoints({ system: anthropicRequest.system, tools: anthropicRequest.tools })).messages;
|
|
1355
1397
|
}
|
|
1398
|
+
// R225 (DARK): automatic cache_control + 1h TTL — mirrors sendAnthropicMessagesAPI.
|
|
1399
|
+
if (enableCaching)
|
|
1400
|
+
applyAnthropicCacheLevers(anthropicRequest);
|
|
1356
1401
|
// Phase 2.8: Enable extended thinking for Claude 4+ models with reasoning support
|
|
1357
1402
|
// Requires beta header: anthropic-beta: interleaved-thinking-2025-05-14
|
|
1358
1403
|
// Skip if disableThinking option is set (used for continuation requests)
|
|
@@ -1370,6 +1415,11 @@ export class APIClient {
|
|
|
1370
1415
|
process.env.DEBUG_THINKING === 'true'
|
|
1371
1416
|
? { type: 'adaptive', display: 'summarized' }
|
|
1372
1417
|
: { type: 'adaptive' };
|
|
1418
|
+
// DARK (CORTEX_ANTHROPIC_EFFORT): parity with the non-streaming path.
|
|
1419
|
+
const outputEffort = isAnthropicEffortEnabled() ? anthropicOutputEffort(reasoningEffort) : undefined;
|
|
1420
|
+
if (outputEffort) {
|
|
1421
|
+
anthropicRequest.output_config = { ...(anthropicRequest.output_config || {}), effort: outputEffort };
|
|
1422
|
+
}
|
|
1373
1423
|
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1374
1424
|
console.log(`[DEBUG APIClient] Adaptive thinking for ${modelConfig.id}${process.env.DEBUG_THINKING === 'true' ? ' (display: summarized)' : ''}`);
|
|
1375
1425
|
}
|
|
@@ -1381,7 +1431,11 @@ export class APIClient {
|
|
|
1381
1431
|
medium: 10000,
|
|
1382
1432
|
high: 50000,
|
|
1383
1433
|
};
|
|
1384
|
-
|
|
1434
|
+
// DARK (CORTEX_ANTHROPIC_EFFORT): parity with the non-streaming path ('max' → clamped large budget).
|
|
1435
|
+
const maxBudget = isAnthropicEffortEnabled() && reasoningEffort === 'max'
|
|
1436
|
+
? anthropicMaxEffortBudget(anthropicRequest.max_tokens)
|
|
1437
|
+
: undefined;
|
|
1438
|
+
const budgetTokens = maxBudget ?? (reasoningEffort ? (budgetMap[reasoningEffort] || 10000) : 10000);
|
|
1385
1439
|
anthropicRequest.thinking = {
|
|
1386
1440
|
type: 'enabled',
|
|
1387
1441
|
budget_tokens: budgetTokens
|
|
@@ -1981,6 +2035,9 @@ export class APIClient {
|
|
|
1981
2035
|
// R153 (2026-09-16): keep the provider's LAST non-null finish_reason so a `length` cutoff survives reassembly
|
|
1982
2036
|
// (the empty-response classifier keys `truncated` on it; the old hardcode reported every text turn as `stop`).
|
|
1983
2037
|
let lastFinishReason = null;
|
|
2038
|
+
// R224a (DARK, CORTEX_STREAM_USAGE): the provider's final usage chunk (choices: []) when requested.
|
|
2039
|
+
const captureStreamUsage = isStreamUsageEnabled();
|
|
2040
|
+
let streamUsage = null;
|
|
1984
2041
|
// Capture this context before entering generator
|
|
1985
2042
|
const self = this;
|
|
1986
2043
|
// Create async generator that both yields chunks AND accumulates final message
|
|
@@ -1994,7 +2051,9 @@ export class APIClient {
|
|
|
1994
2051
|
if (!firstChunk)
|
|
1995
2052
|
firstChunk = chunk;
|
|
1996
2053
|
chunkCount++;
|
|
1997
|
-
|
|
2054
|
+
if (captureStreamUsage && chunk?.usage)
|
|
2055
|
+
streamUsage = chunk.usage;
|
|
2056
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
1998
2057
|
// Debug: Log first few chunks to see what fields are present
|
|
1999
2058
|
if ((process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') && chunkCount <= 3) {
|
|
2000
2059
|
console.log(`[DEBUG APIClient] Chunk ${chunkCount} delta keys:`, delta ? Object.keys(delta) : 'null');
|
|
@@ -2045,7 +2104,7 @@ export class APIClient {
|
|
|
2045
2104
|
}
|
|
2046
2105
|
}
|
|
2047
2106
|
// Check if finish_reason indicates tool_calls completion (or length-truncated tool call)
|
|
2048
|
-
const finishReason = chunk.choices[0]?.finish_reason;
|
|
2107
|
+
const finishReason = chunk.choices?.[0]?.finish_reason;
|
|
2049
2108
|
if (finishReason)
|
|
2050
2109
|
lastFinishReason = String(finishReason);
|
|
2051
2110
|
if ((finishReason === 'tool_calls' || finishReason === 'length') && accumulatedToolCalls.size > 0) {
|
|
@@ -2111,11 +2170,7 @@ export class APIClient {
|
|
|
2111
2170
|
// ('length' = output cap hit) instead of a synthetic 'stop'.
|
|
2112
2171
|
finish_reason: finalToolCalls.length > 0 ? 'tool_calls' : (lastFinishReason ?? 'stop')
|
|
2113
2172
|
}],
|
|
2114
|
-
usage:
|
|
2115
|
-
prompt_tokens: 0,
|
|
2116
|
-
completion_tokens: 0,
|
|
2117
|
-
total_tokens: 0
|
|
2118
|
-
}
|
|
2173
|
+
usage: streamFinalUsage(streamUsage)
|
|
2119
2174
|
});
|
|
2120
2175
|
}();
|
|
2121
2176
|
return {
|
|
@@ -2215,6 +2270,10 @@ export class APIClient {
|
|
|
2215
2270
|
responsesRequest.prompt_cache_key = request.conversationId;
|
|
2216
2271
|
}
|
|
2217
2272
|
}
|
|
2273
|
+
// R225 (DARK, CORTEX_OPENAI_CACHE_KEY): mirrors sendResponsesAPI.
|
|
2274
|
+
if (isOpenAICacheKeyEnabled() && modelConfig.provider?.toLowerCase() === 'openai' && request.conversationId) {
|
|
2275
|
+
responsesRequest.prompt_cache_key = request.conversationId;
|
|
2276
|
+
}
|
|
2218
2277
|
// Set system context as instructions (enables provider-side caching).
|
|
2219
2278
|
// R63: merge the R28 static system prompt (previously dropped on this
|
|
2220
2279
|
// path) ahead of reminders mined from user messages; XAI gets it as a
|
|
@@ -2475,6 +2534,11 @@ export class APIClient {
|
|
|
2475
2534
|
if (request.tools && request.tools.length > 0) {
|
|
2476
2535
|
return this.streamGenerateContentHTTP(request, modelConfig, apiKey);
|
|
2477
2536
|
}
|
|
2537
|
+
// R229 (DARK, CORTEX_GEMINI_SYSTEM_FIX): the old SDK cannot send a system prompt — use the v1beta REST
|
|
2538
|
+
// streaming builder (already sends system_instruction) whenever one is present.
|
|
2539
|
+
if (isGeminiSystemFixEnabled() && request.systemMessage) {
|
|
2540
|
+
return this.streamGenerateContentHTTP(request, modelConfig, apiKey);
|
|
2541
|
+
}
|
|
2478
2542
|
// For non-tool requests, use SDK streaming
|
|
2479
2543
|
if (!this.googleClient) {
|
|
2480
2544
|
this.googleClient = new GoogleGenerativeAI(apiKey);
|
|
@@ -2819,12 +2883,22 @@ export class APIClient {
|
|
|
2819
2883
|
Object.assign(config, generationConfig);
|
|
2820
2884
|
}
|
|
2821
2885
|
// CRITICAL: thinkingConfig and tools CANNOT coexist
|
|
2886
|
+
// (DARK CORTEX_GEMINI_TOOLS_THINKING: a Gemini 2.0-era observation — on, thinking is sent WITH tools.)
|
|
2822
2887
|
const hasTools = request.tools && request.tools.length > 0;
|
|
2888
|
+
const geminiToolsThinking = isGeminiToolsThinkingEnabled();
|
|
2823
2889
|
if (_modelConfig.reasoning?.supported && _modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking && !hasTools) {
|
|
2824
|
-
config.thinkingConfig =
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2890
|
+
config.thinkingConfig = geminiToolsThinking && config.thinkingConfig
|
|
2891
|
+
// effort-mapped thinkingLevel/thinkingBudget from the gateway; keep the thought summaries this path always asked for
|
|
2892
|
+
? { includeThoughts: true, ...config.thinkingConfig }
|
|
2893
|
+
: {
|
|
2894
|
+
includeThoughts: true,
|
|
2895
|
+
thinkingBudget: 10000
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
else if (_modelConfig.reasoning?.supported && _modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking && hasTools && geminiToolsThinking) {
|
|
2899
|
+
// Non-streaming: no includeThoughts — this response is converted by the message-level adapter, which would turn
|
|
2900
|
+
// thought-summary parts into visible text. Effort-mapped config from the gateway, else the no-tools default budget.
|
|
2901
|
+
config.thinkingConfig = config.thinkingConfig ?? { thinkingBudget: 10000 };
|
|
2828
2902
|
}
|
|
2829
2903
|
// Add tools to config if present
|
|
2830
2904
|
if (hasTools && request.tools) {
|
|
@@ -2843,7 +2917,14 @@ export class APIClient {
|
|
|
2843
2917
|
config
|
|
2844
2918
|
};
|
|
2845
2919
|
if (request.systemMessage) {
|
|
2846
|
-
|
|
2920
|
+
if (isGeminiSystemFixEnabled()) {
|
|
2921
|
+
// R229 (DARK): @google/genai reads the system prompt ONLY from config.systemInstruction
|
|
2922
|
+
// (GenerateContentParameters = {model, contents, config}); a top-level field is dropped.
|
|
2923
|
+
config.systemInstruction = request.systemMessage;
|
|
2924
|
+
}
|
|
2925
|
+
else {
|
|
2926
|
+
sdkRequest.systemInstruction = request.systemMessage;
|
|
2927
|
+
}
|
|
2847
2928
|
}
|
|
2848
2929
|
if (process.env.DEBUG === 'true') {
|
|
2849
2930
|
console.log(`[DEBUG APIClient SDK] Non-streaming request for model: ${modelId}`);
|
|
@@ -2904,16 +2985,24 @@ export class APIClient {
|
|
|
2904
2985
|
// Following Claude/Grok pattern: Skip if disableThinking flag is set (used for continuation requests)
|
|
2905
2986
|
// CRITICAL: thinkingConfig and tools CANNOT coexist — Google API returns empty responses when both present
|
|
2906
2987
|
const hasTools = request.tools && request.tools.length > 0;
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
// -
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2988
|
+
// DARK (CORTEX_GEMINI_TOOLS_THINKING): on, thinking is sent WITH tools (effort-mapped by the gateway).
|
|
2989
|
+
const geminiToolsThinking = isGeminiToolsThinkingEnabled();
|
|
2990
|
+
if (_modelConfig.reasoning?.supported && _modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking && (!hasTools || geminiToolsThinking)) {
|
|
2991
|
+
if (geminiToolsThinking && config.thinkingConfig) {
|
|
2992
|
+
// effort-mapped thinkingLevel/thinkingBudget from the gateway (+ thought summaries; the stream parser filters part.thought)
|
|
2993
|
+
config.thinkingConfig = { includeThoughts: true, ...config.thinkingConfig };
|
|
2994
|
+
}
|
|
2995
|
+
else {
|
|
2996
|
+
config.thinkingConfig = {
|
|
2997
|
+
includeThoughts: true, // Enable thought summaries in response
|
|
2998
|
+
// Gemini 2.5 Flash supports thinkingBudget (0-24576)
|
|
2999
|
+
// -1 = dynamic (model decides), or set explicit budget like Claude's 10000
|
|
3000
|
+
// Set to 10000 tokens to encourage verbose reasoning like Claude
|
|
3001
|
+
thinkingBudget: 10000
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
2915
3004
|
if (process.env.DEBUG === 'true') {
|
|
2916
|
-
console.log(`[DEBUG APIClient SDK] Extended thinking enabled for ${modelId}
|
|
3005
|
+
console.log(`[DEBUG APIClient SDK] Extended thinking enabled for ${modelId}: ${JSON.stringify(config.thinkingConfig)}`);
|
|
2917
3006
|
}
|
|
2918
3007
|
}
|
|
2919
3008
|
else if (hasTools && process.env.DEBUG === 'true') {
|
|
@@ -2940,9 +3029,15 @@ export class APIClient {
|
|
|
2940
3029
|
contents: request.messages,
|
|
2941
3030
|
config // Config contains both generation params and tools
|
|
2942
3031
|
};
|
|
2943
|
-
// Add system instruction if present
|
|
3032
|
+
// Add system instruction if present. Legacy: top level (the SDK DROPS it — GenerateContentParameters =
|
|
3033
|
+
// {model, contents, config}). R229 (DARK, CORTEX_GEMINI_SYSTEM_FIX): config.systemInstruction.
|
|
2944
3034
|
if (request.systemMessage) {
|
|
2945
|
-
|
|
3035
|
+
if (isGeminiSystemFixEnabled()) {
|
|
3036
|
+
config.systemInstruction = request.systemMessage;
|
|
3037
|
+
}
|
|
3038
|
+
else {
|
|
3039
|
+
sdkRequest.systemInstruction = request.systemMessage;
|
|
3040
|
+
}
|
|
2946
3041
|
}
|
|
2947
3042
|
if (process.env.DEBUG === 'true') {
|
|
2948
3043
|
console.log(`[DEBUG APIClient SDK] Using model: ${modelId}`);
|