@nexus-cortex/core 4.124.46 → 4.124.47
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 +49 -1
- package/dist/adapters/ChatCompletionsAPIAdapter.d.ts.map +1 -1
- package/dist/adapters/ChatCompletionsAPIAdapter.js +10 -0
- package/dist/adapters/ChatCompletionsAPIAdapter.js.map +1 -1
- package/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -1
- package/dist/adapters/GatewayTranslationLayer.js +10 -0
- 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/SettingsSchema.js +2 -2
- package/dist/config/SettingsSchema.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +5 -0
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/models/cards/xai/grok-4-3-chat.d.ts +15 -0
- package/dist/models/cards/xai/grok-4-3-chat.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-3-chat.js +28 -0
- package/dist/models/cards/xai/grok-4-3-chat.js.map +1 -0
- package/dist/models/cards/xai/grok-build-0-1-chat.d.ts +17 -0
- package/dist/models/cards/xai/grok-build-0-1-chat.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-build-0-1-chat.js +31 -0
- package/dist/models/cards/xai/grok-build-0-1-chat.js.map +1 -0
- package/dist/models/cards/xai/index.d.ts +2 -0
- package/dist/models/cards/xai/index.d.ts.map +1 -1
- package/dist/models/cards/xai/index.js +2 -0
- package/dist/models/cards/xai/index.js.map +1 -1
- package/dist/models/configurators/XAIConfigurator.d.ts +7 -1
- package/dist/models/configurators/XAIConfigurator.d.ts.map +1 -1
- package/dist/models/configurators/XAIConfigurator.js +15 -3
- package/dist/models/configurators/XAIConfigurator.js.map +1 -1
- package/dist/models/registry/ModularModelRegistry.d.ts.map +1 -1
- package/dist/models/registry/ModularModelRegistry.js +2 -0
- package/dist/models/registry/ModularModelRegistry.js.map +1 -1
- package/dist/orchestrator/APIClient.d.ts +4 -0
- package/dist/orchestrator/APIClient.d.ts.map +1 -1
- package/dist/orchestrator/APIClient.js +127 -10
- package/dist/orchestrator/APIClient.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +5 -0
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/orchestrator/geminiExplicitCache.d.ts +79 -0
- package/dist/orchestrator/geminiExplicitCache.d.ts.map +1 -0
- package/dist/orchestrator/geminiExplicitCache.js +324 -0
- package/dist/orchestrator/geminiExplicitCache.js.map +1 -0
- package/dist/orchestrator/xaiChatTransport.d.ts +27 -0
- package/dist/orchestrator/xaiChatTransport.d.ts.map +1 -0
- package/dist/orchestrator/xaiChatTransport.js +33 -0
- package/dist/orchestrator/xaiChatTransport.js.map +1 -0
- package/package.json +3 -3
|
@@ -22,6 +22,8 @@ import { parseHFCompletion, normalizeToolCallArguments } from '../models/hfSpace
|
|
|
22
22
|
import { isResponsesInlineRemindersEnabled, inlineRemindersForResponses, isResponsesStopReasonEnabled, isAnthropicHistoryCacheEnabled, applyAnthropicHistoryCache, countAnthropicBreakpoints, } from './transportFixes.js';
|
|
23
23
|
import { isGeminiSystemFixEnabled, isOpenAICacheKeyEnabled, isStreamUsageEnabled, applyAnthropicCacheLevers, streamFinalUsage, } from './transportCacheFixes.js';
|
|
24
24
|
import { isAnthropicEffortEnabled, isGeminiToolsThinkingEnabled, anthropicOutputEffort, anthropicMaxEffortBudget, } from './transportEffortFixes.js';
|
|
25
|
+
import { GeminiExplicitCacheManager, isGeminiExplicitCacheEnabled, isCacheUseFailure, geminiBaseFromEndpoint, } from './geminiExplicitCache.js';
|
|
26
|
+
import { isXAIChatRoute, xaiChatReasoningEffort } from './xaiChatTransport.js';
|
|
25
27
|
import { anthropicCredentialService, CredentialError } from '../config/AnthropicCredentialService.js';
|
|
26
28
|
/**
|
|
27
29
|
* Anthropic families on the adaptive-thinking-only request surface — sending
|
|
@@ -61,9 +63,18 @@ export class APIClient {
|
|
|
61
63
|
openaiClient;
|
|
62
64
|
googleClient;
|
|
63
65
|
googleGenAIClient;
|
|
66
|
+
/** HB-GEMINI-EXPLICIT-CACHE (DARK): per-client cachedContents snapshots, keyed by session + head. Inert when off. */
|
|
67
|
+
geminiCache = new GeminiExplicitCacheManager({ fetchImpl: (url, init) => cortexProxyFetch(url, init) });
|
|
64
68
|
constructor() {
|
|
65
69
|
// Initialize clients lazily when needed
|
|
66
70
|
}
|
|
71
|
+
/** HB-GEMINI-EXPLICIT-CACHE: best-effort DELETE of every live Gemini cachedContents snapshot (session end). */
|
|
72
|
+
async disposeGeminiCaches() {
|
|
73
|
+
try {
|
|
74
|
+
await this.geminiCache.disposeAll(process.env['GEMINI_API_KEY']);
|
|
75
|
+
}
|
|
76
|
+
catch { /* best-effort */ }
|
|
77
|
+
}
|
|
67
78
|
/**
|
|
68
79
|
* Decode HTML entities in an object (recursive)
|
|
69
80
|
*
|
|
@@ -685,6 +696,13 @@ export class APIClient {
|
|
|
685
696
|
.replace('/messages', '')
|
|
686
697
|
.replace('/responses', '');
|
|
687
698
|
const defaultHeaders = {};
|
|
699
|
+
// xAI chat/completions (third xAI transport, DARK — reachable only via an xAI 'chat' card / XAI_API_MODE=chat):
|
|
700
|
+
// `x-grok-conv-id` sticky routing, mirroring the xAI Messages path's header (xAI prompt-caching docs: the cached prefix
|
|
701
|
+
// lives on one backend instance). Header-only — the request body / cached prefix is unchanged.
|
|
702
|
+
const xaiChat = isXAIChatRoute(modelConfig);
|
|
703
|
+
if (xaiChat && request.conversationId) {
|
|
704
|
+
defaultHeaders['x-grok-conv-id'] = request.conversationId;
|
|
705
|
+
}
|
|
688
706
|
// Reuse cached openai client when possible (matches the streaming path's
|
|
689
707
|
// pre-refactor behavior; now applied uniformly).
|
|
690
708
|
const client = modelConfig.provider.toLowerCase() === 'openai' && this.openaiClient
|
|
@@ -717,6 +735,8 @@ export class APIClient {
|
|
|
717
735
|
const reasoningEffort = transformedParams.reasoningEffort
|
|
718
736
|
|| modelConfig.reasoning?.effort
|
|
719
737
|
|| 'medium';
|
|
738
|
+
// xAI chat: the REQUEST/action effort only (the card default is resolved by xaiChatReasoningEffort's capability gate).
|
|
739
|
+
const xaiRequestedEffort = transformedParams.reasoningEffort;
|
|
720
740
|
delete transformedParams.reasoningEffort;
|
|
721
741
|
if (process.env.DEBUG === 'true') {
|
|
722
742
|
console.log(`[Effort] outbound reasoning_effort=${reasoningEffort} (source: ${request.parameters?.reasoningEffort ? 'request/pulse' : (modelConfig.reasoning?.effort ? 'card' : 'default')})`);
|
|
@@ -745,6 +765,12 @@ export class APIClient {
|
|
|
745
765
|
...transformedParams,
|
|
746
766
|
...(opts.stream ? { stream: true } : {}),
|
|
747
767
|
};
|
|
768
|
+
// xAI chat: the gateway's params.model (= registry id, e.g. 'grok-4.3-chat') is spread AFTER `model:` above and would
|
|
769
|
+
// send the registry id; the chat cards carry the wire name in `modelId` (resolved into request.modelId). Scoped to xAI
|
|
770
|
+
// chat so every other chat/completions provider keeps its existing bytes (pre-existing for any chat card whose id ≠
|
|
771
|
+
// modelId, e.g. gpt-5.1-reasoning — reported, not changed here).
|
|
772
|
+
if (xaiChat)
|
|
773
|
+
chatRequest.model = request.modelId;
|
|
748
774
|
// R19b: OpenAI chat/completions REJECTS `reasoning_effort` + tools for
|
|
749
775
|
// some gpt-5 variants (`gpt-5.4`, possibly others) with
|
|
750
776
|
// "400 Function tools with reasoning_effort are not supported for ... in /v1/chat/completions".
|
|
@@ -761,8 +787,19 @@ export class APIClient {
|
|
|
761
787
|
// supported … in /v1/chat/completions. To use function tools, use /v1/responses" — CORTEX_OPENAI_TOOLS_REASONING is Responses-only.
|
|
762
788
|
const dropReasoningForToolCompat = (willAttachTools && isChatCompletionsRoute && modelConfig.provider === 'openai') ||
|
|
763
789
|
(hasForcedToolChoice && isChatCompletionsRoute && modelConfig.provider === 'deepseek');
|
|
764
|
-
//
|
|
765
|
-
|
|
790
|
+
// xAI chat/completions: per-card capability gate — reasoning_effort only for cards that declare an effort
|
|
791
|
+
// (grok-4.3-class); grok-build-0.1 400s on it and never gets it. Sampling params are kept (xAI accepts temperature/top_p
|
|
792
|
+
// with reasoning; only presence/frequency penalties + stop are rejected, and this builder never adds those).
|
|
793
|
+
if (xaiChat) {
|
|
794
|
+
const xaiEffort = disableThinking ? undefined : xaiChatReasoningEffort(xaiRequestedEffort, modelConfig);
|
|
795
|
+
if (xaiEffort)
|
|
796
|
+
chatRequest.reasoning_effort = xaiEffort;
|
|
797
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
798
|
+
console.log(`[DEBUG APIClient] xAI chat reasoning_effort=${xaiEffort ?? '(not sent)'} for ${modelConfig.id}`);
|
|
799
|
+
}
|
|
800
|
+
// Enable reasoning for OpenAI models that support it (GPT-5 family, o-series)
|
|
801
|
+
}
|
|
802
|
+
else if (modelConfig.reasoning?.supported &&
|
|
766
803
|
!disableThinking &&
|
|
767
804
|
reasoningEffort !== 'none' &&
|
|
768
805
|
!dropReasoningForToolCompat) {
|
|
@@ -1267,20 +1304,33 @@ export class APIClient {
|
|
|
1267
1304
|
console.log(`[DEBUG APIClient] Request body tools wrapper:`, JSON.stringify(requestBody.tools[0], null, 2).substring(0, 500));
|
|
1268
1305
|
}
|
|
1269
1306
|
}
|
|
1270
|
-
|
|
1271
|
-
const httpResponse = await cortexProxyFetch(url, {
|
|
1307
|
+
const postBody = (body) => cortexProxyFetch(url, {
|
|
1272
1308
|
method: 'POST',
|
|
1273
1309
|
headers: {
|
|
1274
1310
|
'Content-Type': 'application/json',
|
|
1275
1311
|
[modelConfig.api.authHeader]: apiKey
|
|
1276
1312
|
},
|
|
1277
|
-
body: JSON.stringify(
|
|
1313
|
+
body: JSON.stringify(body)
|
|
1278
1314
|
});
|
|
1315
|
+
// HB-GEMINI-EXPLICIT-CACHE (DARK, CORTEX_GEMINI_EXPLICIT_CACHE): off → no plan → the original body, unchanged.
|
|
1316
|
+
const cacheCtx = { sessionId: request.conversationId, modelId, apiKey, baseUrl: geminiBaseFromEndpoint(modelConfig.api.endpoint) };
|
|
1317
|
+
const cached = isGeminiExplicitCacheEnabled() ? await this.geminiCache.prepare(cacheCtx, 'rest', requestBody) : null;
|
|
1318
|
+
// Make HTTP request
|
|
1319
|
+
let httpResponse = await postBody(cached ? cached.body : requestBody);
|
|
1320
|
+
let usedCache = !!cached;
|
|
1321
|
+
if (cached && !httpResponse.ok && isCacheUseFailure(httpResponse.status)) {
|
|
1322
|
+
const errorText = await httpResponse.text();
|
|
1323
|
+
this.geminiCache.invalidate(cached.plan, `use_${httpResponse.status} ${errorText.replace(/\s+/g, ' ').slice(0, 160)}`, cacheCtx);
|
|
1324
|
+
usedCache = false;
|
|
1325
|
+
httpResponse = await postBody(requestBody); // the normal full request, once
|
|
1326
|
+
}
|
|
1279
1327
|
if (!httpResponse.ok) {
|
|
1280
1328
|
const errorText = await httpResponse.text();
|
|
1281
1329
|
throw new Error(`Google GenerateContent API error (${httpResponse.status}): ${errorText}`);
|
|
1282
1330
|
}
|
|
1283
1331
|
const data = await httpResponse.json();
|
|
1332
|
+
if (cached && usedCache)
|
|
1333
|
+
this.geminiCache.recordUsage(cached.plan, data?.usageMetadata);
|
|
1284
1334
|
// Return full response (same structure as SDK)
|
|
1285
1335
|
// The response has: candidates, usageMetadata, modelVersion, responseId
|
|
1286
1336
|
return {
|
|
@@ -2637,14 +2687,34 @@ export class APIClient {
|
|
|
2637
2687
|
// Write full request body to file for inspection
|
|
2638
2688
|
import('fs').then(fs => fs.writeFileSync('/tmp/gemini-request-' + modelId + '.json', JSON.stringify(requestBody, null, 2)));
|
|
2639
2689
|
}
|
|
2640
|
-
//
|
|
2641
|
-
const
|
|
2690
|
+
// HB-GEMINI-EXPLICIT-CACHE (DARK, CORTEX_GEMINI_EXPLICIT_CACHE): off → the original single fetch, unchanged.
|
|
2691
|
+
const geminiCacheOn = isGeminiExplicitCacheEnabled();
|
|
2692
|
+
const streamCache = {};
|
|
2693
|
+
let lastChunkUsage = undefined;
|
|
2694
|
+
const postStream = (body) => cortexProxyFetch(url, {
|
|
2642
2695
|
method: 'POST',
|
|
2643
2696
|
headers: {
|
|
2644
2697
|
'Content-Type': 'application/json'
|
|
2645
2698
|
},
|
|
2646
|
-
body: JSON.stringify(
|
|
2699
|
+
body: JSON.stringify(body)
|
|
2647
2700
|
});
|
|
2701
|
+
// Start the fetch request
|
|
2702
|
+
const fetchPromise = !geminiCacheOn
|
|
2703
|
+
? postStream(requestBody)
|
|
2704
|
+
: (async () => {
|
|
2705
|
+
const cacheCtx = { sessionId: request.conversationId, modelId, apiKey, baseUrl: geminiBaseFromEndpoint(modelConfig.api.endpoint) };
|
|
2706
|
+
const cached = await this.geminiCache.prepare(cacheCtx, 'rest', requestBody);
|
|
2707
|
+
let res = await postStream(cached ? cached.body : requestBody);
|
|
2708
|
+
if (cached && !res.ok && isCacheUseFailure(res.status)) {
|
|
2709
|
+
const errorText = await res.text();
|
|
2710
|
+
this.geminiCache.invalidate(cached.plan, `use_${res.status} ${errorText.replace(/\s+/g, ' ').slice(0, 160)}`, cacheCtx);
|
|
2711
|
+
res = await postStream(requestBody); // the normal full request, once
|
|
2712
|
+
}
|
|
2713
|
+
else if (cached) {
|
|
2714
|
+
streamCache.plan = cached.plan;
|
|
2715
|
+
}
|
|
2716
|
+
return res;
|
|
2717
|
+
})();
|
|
2648
2718
|
// Shared state for collecting all chunks
|
|
2649
2719
|
const allChunks = [];
|
|
2650
2720
|
let fullText = '';
|
|
@@ -2700,6 +2770,9 @@ export class APIClient {
|
|
|
2700
2770
|
if (chunk.candidates?.[0]) {
|
|
2701
2771
|
lastCandidate = chunk.candidates[0];
|
|
2702
2772
|
}
|
|
2773
|
+
// Gemini reports usageMetadata on the CHUNK, not the candidate (captured only with the cache lever on).
|
|
2774
|
+
if (geminiCacheOn && chunk.usageMetadata)
|
|
2775
|
+
lastChunkUsage = chunk.usageMetadata;
|
|
2703
2776
|
// Extract parts from candidates
|
|
2704
2777
|
const parts = chunk.candidates?.[0]?.content?.parts;
|
|
2705
2778
|
if (parts && Array.isArray(parts)) {
|
|
@@ -2785,11 +2858,13 @@ export class APIClient {
|
|
|
2785
2858
|
if (streamingError) {
|
|
2786
2859
|
throw streamingError;
|
|
2787
2860
|
}
|
|
2861
|
+
if (streamCache.plan)
|
|
2862
|
+
this.geminiCache.recordUsage(streamCache.plan, lastChunkUsage);
|
|
2788
2863
|
// Return final response structure matching Google SDK format
|
|
2789
2864
|
return {
|
|
2790
2865
|
text: () => fullText,
|
|
2791
2866
|
candidates: lastCandidate ? [lastCandidate] : [],
|
|
2792
|
-
usageMetadata: lastCandidate?.usageMetadata
|
|
2867
|
+
usageMetadata: lastCandidate?.usageMetadata ?? (geminiCacheOn ? lastChunkUsage : undefined)
|
|
2793
2868
|
};
|
|
2794
2869
|
})();
|
|
2795
2870
|
return {
|
|
@@ -2929,6 +3004,24 @@ export class APIClient {
|
|
|
2929
3004
|
if (process.env.DEBUG === 'true') {
|
|
2930
3005
|
console.log(`[DEBUG APIClient SDK] Non-streaming request for model: ${modelId}`);
|
|
2931
3006
|
}
|
|
3007
|
+
// HB-GEMINI-EXPLICIT-CACHE (DARK, CORTEX_GEMINI_EXPLICIT_CACHE): off → the original single call, unchanged.
|
|
3008
|
+
if (isGeminiExplicitCacheEnabled()) {
|
|
3009
|
+
const cacheCtx = { sessionId: request.conversationId, modelId, apiKey: process.env['GEMINI_API_KEY'] ?? '' };
|
|
3010
|
+
const cached = await this.geminiCache.prepare(cacheCtx, 'sdk', sdkRequest);
|
|
3011
|
+
if (cached) {
|
|
3012
|
+
try {
|
|
3013
|
+
const r = await this.googleGenAIClient.models.generateContent(cached.body);
|
|
3014
|
+
this.geminiCache.recordUsage(cached.plan, r?.usageMetadata);
|
|
3015
|
+
return { data: r, status: 200, headers: {} };
|
|
3016
|
+
}
|
|
3017
|
+
catch (e) {
|
|
3018
|
+
if (!isCacheUseFailure(e?.status))
|
|
3019
|
+
throw e;
|
|
3020
|
+
this.geminiCache.invalidate(cached.plan, `use_${e.status} ${String(e?.message ?? '').slice(0, 160)}`, cacheCtx);
|
|
3021
|
+
// fall through: the normal full request, once
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
2932
3025
|
const response = await this.googleGenAIClient.models.generateContent(sdkRequest);
|
|
2933
3026
|
return {
|
|
2934
3027
|
data: response,
|
|
@@ -3054,10 +3147,32 @@ export class APIClient {
|
|
|
3054
3147
|
const streamCompletePromise = new Promise((resolve) => {
|
|
3055
3148
|
resolveComplete = resolve;
|
|
3056
3149
|
});
|
|
3150
|
+
// HB-GEMINI-EXPLICIT-CACHE (DARK, CORTEX_GEMINI_EXPLICIT_CACHE): off → the original single call, unchanged.
|
|
3151
|
+
const sdkCacheOn = isGeminiExplicitCacheEnabled();
|
|
3152
|
+
const sdkStream = {};
|
|
3153
|
+
const openSdkStream = async (client) => {
|
|
3154
|
+
if (!sdkCacheOn)
|
|
3155
|
+
return client.models.generateContentStream(sdkRequest);
|
|
3156
|
+
const cacheCtx = { sessionId: request.conversationId, modelId, apiKey: process.env['GEMINI_API_KEY'] ?? '' };
|
|
3157
|
+
const cached = await this.geminiCache.prepare(cacheCtx, 'sdk', sdkRequest);
|
|
3158
|
+
if (cached) {
|
|
3159
|
+
try {
|
|
3160
|
+
const s = await client.models.generateContentStream(cached.body);
|
|
3161
|
+
sdkStream.plan = cached.plan;
|
|
3162
|
+
return s;
|
|
3163
|
+
}
|
|
3164
|
+
catch (e) {
|
|
3165
|
+
if (!isCacheUseFailure(e?.status))
|
|
3166
|
+
throw e;
|
|
3167
|
+
this.geminiCache.invalidate(cached.plan, `use_${e.status} ${String(e?.message ?? '').slice(0, 160)}`, cacheCtx);
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
return client.models.generateContentStream(sdkRequest); // the normal full request (once, on a cache failure)
|
|
3171
|
+
};
|
|
3057
3172
|
const chunks = async function* (client) {
|
|
3058
3173
|
try {
|
|
3059
3174
|
// Stream using new SDK (SINGLE API call)
|
|
3060
|
-
const stream = await client
|
|
3175
|
+
const stream = await openSdkStream(client);
|
|
3061
3176
|
if (process.env.DEBUG === 'true') {
|
|
3062
3177
|
console.log(`[DEBUG APIClient SDK] Stream started for model: ${modelId}`);
|
|
3063
3178
|
}
|
|
@@ -3215,6 +3330,8 @@ export class APIClient {
|
|
|
3215
3330
|
const finalMessage = (async () => {
|
|
3216
3331
|
// Wait for chunks generator to complete
|
|
3217
3332
|
await streamCompletePromise;
|
|
3333
|
+
if (sdkStream.plan)
|
|
3334
|
+
this.geminiCache.recordUsage(sdkStream.plan, lastChunk?.usageMetadata);
|
|
3218
3335
|
// Return final response structure matching Google SDK format
|
|
3219
3336
|
// This matches the structure expected by the orchestrator
|
|
3220
3337
|
return {
|