@juspay/neurolink 12.14.7 → 12.14.9
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/CHANGELOG.md +2 -2
- package/dist/adapters/providerImageAdapter.js +1 -0
- package/dist/browser/neurolink.min.js +379 -379
- package/dist/cli/commands/proxy.js +7 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +11 -0
- package/dist/mcp/servers/aiProviders/aiAnalysisTools.js +43 -30
- package/dist/neurolink.d.ts +4 -1
- package/dist/neurolink.js +41 -6
- package/dist/providers/azureOpenai.js +1 -1
- package/dist/providers/catalog/loader.d.ts +1 -1
- package/dist/providers/catalog/loader.js +30 -1
- package/dist/providers/cohere.js +1 -1
- package/dist/providers/deepseek.js +1 -1
- package/dist/providers/googleAiStudio/client.js +6 -5
- package/dist/providers/googleVertex/client.d.ts +11 -30
- package/dist/providers/googleVertex/client.js +49 -131
- package/dist/providers/huggingFace/client.js +1 -1
- package/dist/providers/llamaCpp.js +1 -1
- package/dist/providers/lmStudio.js +1 -1
- package/dist/providers/nvidiaNim/client.js +1 -1
- package/dist/providers/ollama/client.js +1 -1
- package/dist/providers/openRouter/client.js +1 -1
- package/dist/providers/openaiCompatible/client.js +1 -1
- package/dist/proxy/bodyCaptureProcessing.js +47 -3
- package/dist/proxy/bodyCaptureWorker.js +14 -10
- package/dist/proxy/claudeOutputObservation.d.ts +9 -0
- package/dist/proxy/claudeOutputObservation.js +85 -0
- package/dist/proxy/codexUsage.js +12 -10
- package/dist/proxy/otelLogSink.js +1 -1
- package/dist/proxy/processTelemetry.d.ts +3 -0
- package/dist/proxy/processTelemetry.js +25 -0
- package/dist/proxy/rawStreamCapture.js +17 -26
- package/dist/proxy/requestLogger.d.ts +2 -0
- package/dist/proxy/requestLogger.js +5 -1
- package/dist/proxy/restartControl.d.ts +3 -1
- package/dist/proxy/restartControl.js +118 -1
- package/dist/proxy/sseInterceptor.js +71 -3
- package/dist/server/routes/claudeProxyRoutes.d.ts +0 -11
- package/dist/server/routes/claudeProxyRoutes.js +27 -12
- package/dist/server/routes/codexProxyRoutes.js +83 -2
- package/dist/types/proxy.d.ts +14 -0
- package/dist/types/proxyRestart.d.ts +25 -0
- package/dist/utils/logSanitize.js +1 -1
- package/dist/utils/timeout.d.ts +13 -2
- package/dist/utils/timeout.js +14 -3
- package/docs-site/static/search-index.json +1 -1
- package/package.json +1 -1
- package/scripts/observability/proxy-telemetry-check.mjs +39 -5
|
@@ -25,7 +25,8 @@ import { describeInstallFailure, getGlobalInstallArgs, isTransientInstallFailure
|
|
|
25
25
|
import { startUpdaterWorkerSupervisor } from "../../proxy/updaterSupervisor.js";
|
|
26
26
|
import { openProxyWorkerLog } from "../../proxy/workerLog.js";
|
|
27
27
|
import { startRollingProxyServer } from "../../proxy/rollingProxyServer.js";
|
|
28
|
-
import { startProxyRestartControl } from "../../proxy/restartControl.js";
|
|
28
|
+
import { startProxyRestartControl, requestProxySupervisorTelemetry, } from "../../proxy/restartControl.js";
|
|
29
|
+
import { getProxyProcessTelemetry } from "../../proxy/processTelemetry.js";
|
|
29
30
|
import { isProxyAuxiliaryRequest } from "../../proxy/proxyRequestKind.js";
|
|
30
31
|
import { spawnProxySocketWorker } from "../../proxy/rollingWorkerProcess.js";
|
|
31
32
|
import { PROXY_ROLLING_SUPERVISOR_ENV, PROXY_SOCKET_WORKER_ENV, } from "../../proxy/rollingWorkerProtocol.js";
|
|
@@ -2076,6 +2077,10 @@ export async function createProxyStartApp(params) {
|
|
|
2076
2077
|
};
|
|
2077
2078
|
})(),
|
|
2078
2079
|
observability: {
|
|
2080
|
+
process: getProxyProcessTelemetry(),
|
|
2081
|
+
supervisor: supervisorState?.pid
|
|
2082
|
+
? await requestProxySupervisorTelemetry(supervisorState.restartControl, supervisorState.pid)
|
|
2083
|
+
: { status: "not_applicable" },
|
|
2079
2084
|
lifecycle: getProxyLifecycleLoggerSnapshot(),
|
|
2080
2085
|
requestLogs: getRequestLoggerSnapshot(),
|
|
2081
2086
|
},
|
|
@@ -2893,6 +2898,7 @@ async function runLaunchdProxySupervisor(argv, spinner) {
|
|
|
2893
2898
|
restartControl = await startProxyRestartControl({
|
|
2894
2899
|
stateDir: join(homedir(), ".neurolink"),
|
|
2895
2900
|
server: rollingServer,
|
|
2901
|
+
getTelemetry: getProxyProcessTelemetry,
|
|
2896
2902
|
log: (message) => logger.warn(message),
|
|
2897
2903
|
isUpdatePending: () => !!rollingReplacement || !!loadUpdateState()?.pendingRestartVersion,
|
|
2898
2904
|
getInstalledVersion: async () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export { createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamic
|
|
|
46
46
|
export { SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, } from "./client/streamingClient.js";
|
|
47
47
|
export { OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthenticationError as OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, } from "./client/auth.js";
|
|
48
48
|
export { ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/errors.js";
|
|
49
|
+
export { BaseError, ProviderError, AuthenticationError, AuthorizationError, NetworkError, RateLimitError, InvalidModelError, } from "./types/index.js";
|
|
49
50
|
export { AIProviderName, BedrockModels, OpenAIModels, VertexModels, } from "./constants/enums.js";
|
|
50
51
|
export { dynamicModelProvider } from "./core/dynamicModels.js";
|
|
51
52
|
export { validateTool } from "./sdk/toolRegistration.js";
|
package/dist/index.js
CHANGED
|
@@ -73,6 +73,17 @@ OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMi
|
|
|
73
73
|
export {
|
|
74
74
|
// Errors
|
|
75
75
|
ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/errors.js";
|
|
76
|
+
// The SDK-side error hierarchy `classifyProviderError()` actually throws
|
|
77
|
+
// (as opposed to the unrelated `Client*` REST-client errors above). These
|
|
78
|
+
// already reach consumers transitively through `export * from
|
|
79
|
+
// "./types/index.js"` below, but that makes them public by accident of the
|
|
80
|
+
// types barrel's shape — a future reshuffle of that barrel could drop them
|
|
81
|
+
// silently. Exporting them here, explicitly, by name, makes the guarantee
|
|
82
|
+
// intentional: `catch (e) { if (e instanceof RateLimitError) ... }` against
|
|
83
|
+
// `@juspay/neurolink` is a supported, stable pattern, not an implementation
|
|
84
|
+
// detail a caller happened to be able to reach. Imported from the barrel
|
|
85
|
+
// (never the specific `types/errors.js` file) per Critical Rule 13.
|
|
86
|
+
export { BaseError, ProviderError, AuthenticationError, AuthorizationError, NetworkError, RateLimitError, InvalidModelError, } from "./types/index.js";
|
|
76
87
|
export { AIProviderName, BedrockModels, OpenAIModels, VertexModels, } from "./constants/enums.js";
|
|
77
88
|
// Dynamic Models exports
|
|
78
89
|
export { dynamicModelProvider } from "./core/dynamicModels.js";
|
|
@@ -185,41 +185,54 @@ const _benchmarkProviderPerformanceTool = {
|
|
|
185
185
|
];
|
|
186
186
|
const benchmarkResults = [];
|
|
187
187
|
for (const providerName of providersToTest) {
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
try {
|
|
189
|
+
const provider = await AIProviderFactory.createProvider(providerName);
|
|
190
|
+
if (!provider) {
|
|
191
|
+
benchmarkResults.push({
|
|
192
|
+
provider: providerName,
|
|
193
|
+
error: "Failed to create provider.",
|
|
194
|
+
});
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
let totalLatency = 0, totalTokens = 0, successfulTests = 0;
|
|
198
|
+
for (const prompt of testPrompts) {
|
|
199
|
+
for (let i = 0; i < typedParams.iterations; i++) {
|
|
200
|
+
const testStartTime = Date.now();
|
|
201
|
+
const result = await provider.generate({
|
|
202
|
+
prompt: prompt,
|
|
203
|
+
maxTokens: typedParams.maxTokens,
|
|
204
|
+
});
|
|
205
|
+
if (result && result.usage) {
|
|
206
|
+
totalLatency += Date.now() - testStartTime;
|
|
207
|
+
totalTokens += result.usage.total || 0;
|
|
208
|
+
successfulTests++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
190
212
|
benchmarkResults.push({
|
|
191
213
|
provider: providerName,
|
|
192
|
-
|
|
214
|
+
metrics: {
|
|
215
|
+
avgLatency: successfulTests > 0
|
|
216
|
+
? Math.round(totalLatency / successfulTests)
|
|
217
|
+
: 0,
|
|
218
|
+
total: totalTokens,
|
|
219
|
+
successRate: (successfulTests /
|
|
220
|
+
(testPrompts.length * typedParams.iterations)) *
|
|
221
|
+
100,
|
|
222
|
+
},
|
|
193
223
|
});
|
|
194
|
-
continue;
|
|
195
224
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
totalTokens += result.usage.total || 0;
|
|
207
|
-
successfulTests++;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
225
|
+
catch (providerError) {
|
|
226
|
+
// A single provider's failure (network, auth, rate-limit, etc.)
|
|
227
|
+
// must not abort the whole benchmark — record it and keep testing
|
|
228
|
+
// the remaining providers, mirroring the null-provider branch above.
|
|
229
|
+
benchmarkResults.push({
|
|
230
|
+
provider: providerName,
|
|
231
|
+
error: providerError instanceof Error
|
|
232
|
+
? providerError.message
|
|
233
|
+
: String(providerError),
|
|
234
|
+
});
|
|
210
235
|
}
|
|
211
|
-
benchmarkResults.push({
|
|
212
|
-
provider: providerName,
|
|
213
|
-
metrics: {
|
|
214
|
-
avgLatency: successfulTests > 0
|
|
215
|
-
? Math.round(totalLatency / successfulTests)
|
|
216
|
-
: 0,
|
|
217
|
-
total: totalTokens,
|
|
218
|
-
successRate: (successfulTests /
|
|
219
|
-
(testPrompts.length * typedParams.iterations)) *
|
|
220
|
-
100,
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
236
|
}
|
|
224
237
|
const executionTime = Date.now() - startTime;
|
|
225
238
|
return {
|
package/dist/neurolink.d.ts
CHANGED
|
@@ -1648,7 +1648,10 @@ export declare class NeuroLink {
|
|
|
1648
1648
|
*/
|
|
1649
1649
|
testProvider(providerName: string): Promise<boolean>;
|
|
1650
1650
|
/**
|
|
1651
|
-
* Internal method to test provider connection with minimal generation call
|
|
1651
|
+
* Internal method to test provider connection with minimal generation call.
|
|
1652
|
+
* `signal` lets a caller (getProviderStatus's per-provider race) cancel the
|
|
1653
|
+
* underlying request once it has already lost the race against its
|
|
1654
|
+
* timeout, instead of leaving it running unobserved.
|
|
1652
1655
|
*/
|
|
1653
1656
|
private testProviderConnection;
|
|
1654
1657
|
/**
|
package/dist/neurolink.js
CHANGED
|
@@ -6490,6 +6490,14 @@ Current user's request: ${currentInput}`;
|
|
|
6490
6490
|
if (lastError instanceof ContextBudgetExceededError) {
|
|
6491
6491
|
throw lastError;
|
|
6492
6492
|
}
|
|
6493
|
+
// A single-candidate list (an explicit provider, or
|
|
6494
|
+
// disableInternalFallback) never actually tried "all providers" —
|
|
6495
|
+
// naming the one provider that failed is clearer than the generic
|
|
6496
|
+
// multi-provider wording. The multi-provider prefix is left untouched
|
|
6497
|
+
// so existing message-matching suites keep passing.
|
|
6498
|
+
if (providersToTry.length === 1) {
|
|
6499
|
+
throw new Error(`Provider ${providersToTry[0]} failed: ${lastError?.message || "Unknown error"}`);
|
|
6500
|
+
}
|
|
6493
6501
|
throw new Error(`Failed to generate text with all providers. Last error: ${lastError?.message || "Unknown error"}`);
|
|
6494
6502
|
}
|
|
6495
6503
|
/**
|
|
@@ -10863,13 +10871,36 @@ Current user's request: ${currentInput}`;
|
|
|
10863
10871
|
};
|
|
10864
10872
|
}
|
|
10865
10873
|
}
|
|
10866
|
-
// Test other providers with actual generation call
|
|
10874
|
+
// Test other providers with actual generation call. The losing
|
|
10875
|
+
// side of the race is aborted in `finally` below — previously the
|
|
10876
|
+
// timeout promise alone rejected while the still-running
|
|
10877
|
+
// testProviderConnection() call (and the provider request inside
|
|
10878
|
+
// it) kept executing to completion unobserved, wasting quota and
|
|
10879
|
+
// holding the connection open past the 5s status check.
|
|
10867
10880
|
const testTimeout = 5000;
|
|
10868
|
-
const
|
|
10881
|
+
const abortController = new AbortController();
|
|
10882
|
+
const testPromise = this.testProviderConnection(providerName, abortController.signal);
|
|
10883
|
+
let timeoutHandle;
|
|
10869
10884
|
const timeoutPromise = new Promise((_, reject) => {
|
|
10870
|
-
setTimeout(() =>
|
|
10885
|
+
timeoutHandle = setTimeout(() => {
|
|
10886
|
+
reject(new Error("Provider test timeout (5s)"));
|
|
10887
|
+
}, testTimeout);
|
|
10871
10888
|
});
|
|
10872
|
-
|
|
10889
|
+
try {
|
|
10890
|
+
await Promise.race([testPromise, timeoutPromise]);
|
|
10891
|
+
}
|
|
10892
|
+
finally {
|
|
10893
|
+
// Runs on both outcomes: clears the timer so it can't fire after
|
|
10894
|
+
// a fast success, and aborts the signal so a timed-out
|
|
10895
|
+
// generate() call stops instead of finishing unobserved.
|
|
10896
|
+
clearTimeout(timeoutHandle);
|
|
10897
|
+
// The aborted generate() resolves/rejects after the race is
|
|
10898
|
+
// decided; swallow that settlement so it can't surface as an
|
|
10899
|
+
// unhandledRejection, mirroring raceWithAbort's handling of the
|
|
10900
|
+
// losing side.
|
|
10901
|
+
testPromise.catch(() => undefined);
|
|
10902
|
+
abortController.abort();
|
|
10903
|
+
}
|
|
10873
10904
|
return {
|
|
10874
10905
|
provider: providerName,
|
|
10875
10906
|
status: "working",
|
|
@@ -10919,15 +10950,19 @@ Current user's request: ${currentInput}`;
|
|
|
10919
10950
|
}
|
|
10920
10951
|
}
|
|
10921
10952
|
/**
|
|
10922
|
-
* Internal method to test provider connection with minimal generation call
|
|
10953
|
+
* Internal method to test provider connection with minimal generation call.
|
|
10954
|
+
* `signal` lets a caller (getProviderStatus's per-provider race) cancel the
|
|
10955
|
+
* underlying request once it has already lost the race against its
|
|
10956
|
+
* timeout, instead of leaving it running unobserved.
|
|
10923
10957
|
*/
|
|
10924
|
-
async testProviderConnection(providerName) {
|
|
10958
|
+
async testProviderConnection(providerName, signal) {
|
|
10925
10959
|
const { AIProviderFactory } = await import("./core/factory.js");
|
|
10926
10960
|
const provider = await AIProviderFactory.createProvider(providerName, null);
|
|
10927
10961
|
await provider.generate({
|
|
10928
10962
|
prompt: "test",
|
|
10929
10963
|
maxTokens: 1,
|
|
10930
10964
|
disableTools: true,
|
|
10965
|
+
abortSignal: signal,
|
|
10931
10966
|
});
|
|
10932
10967
|
}
|
|
10933
10968
|
/**
|
|
@@ -172,7 +172,7 @@ export class AzureOpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
|
172
172
|
},
|
|
173
173
|
...DEFAULT_ERROR_RULES,
|
|
174
174
|
];
|
|
175
|
-
return classifyProviderError(error, rules,
|
|
175
|
+
return classifyProviderError(error, rules, this.providerName, this.modelName);
|
|
176
176
|
}
|
|
177
177
|
// ===========================================================================
|
|
178
178
|
// New overridable hooks (provided by the base-enhancement branch)
|
|
@@ -2,5 +2,5 @@ import type { OpenAICompatCatalogEntry, ProviderCatalogJson, ProviderConfigOptio
|
|
|
2
2
|
export declare function catalogCredentialsKey(entry: ProviderCatalogJson): string;
|
|
3
3
|
export declare function catalogEnvVar(entry: ProviderCatalogJson, kind: "apiKey" | "baseURL" | "model"): string;
|
|
4
4
|
export declare function buildCatalogConfigOptions(entry: ProviderCatalogJson): ProviderConfigOptions;
|
|
5
|
-
export declare function getCatalogJsonEntries(): ProviderCatalogJson[];
|
|
5
|
+
export declare function getCatalogJsonEntries(): readonly ProviderCatalogJson[];
|
|
6
6
|
export declare function buildCatalogEntries(): OpenAICompatCatalogEntry[];
|
|
@@ -70,8 +70,37 @@ function buildErrorRules(entry) {
|
|
|
70
70
|
});
|
|
71
71
|
return [...bespoke, ...DEFAULT_ERROR_RULES];
|
|
72
72
|
}
|
|
73
|
+
// Recursively freezes a value in place and returns it. Used only on a
|
|
74
|
+
// structuredClone()d graph below — never on CATALOG_JSON_ENTRIES itself —
|
|
75
|
+
// so this never touches the module-level singleton.
|
|
76
|
+
function deepFreeze(value) {
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
for (const item of value) {
|
|
79
|
+
deepFreeze(item);
|
|
80
|
+
}
|
|
81
|
+
return Object.freeze(value);
|
|
82
|
+
}
|
|
83
|
+
if (value !== null && typeof value === "object") {
|
|
84
|
+
for (const key of Object.keys(value)) {
|
|
85
|
+
deepFreeze(value[key]);
|
|
86
|
+
}
|
|
87
|
+
return Object.freeze(value);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
// Returns a deep clone of the catalog graph, frozen at every level, never
|
|
92
|
+
// the module-level singleton by reference or anything sharing its nested
|
|
93
|
+
// objects/arrays — CATALOG_JSON_ENTRIES is reused on every call, so handing
|
|
94
|
+
// out (a shallow copy of) the live array would let any one caller's
|
|
95
|
+
// mutation corrupt catalog state for the rest of the process, and freezing
|
|
96
|
+
// only the outer array left `entry.aliases`, `entry.models.fallbacks`,
|
|
97
|
+
// `entry.models.catalog` etc. shared and mutable despite the `readonly`
|
|
98
|
+
// return type. structuredClone() first (so the frozen graph never aliases
|
|
99
|
+
// CATALOG_JSON_ENTRIES's own objects), then deepFreeze() the clone. All
|
|
100
|
+
// current callers only .find/.map/.flatMap/.filter/iterate, so this is
|
|
101
|
+
// behavior-preserving for them.
|
|
73
102
|
export function getCatalogJsonEntries() {
|
|
74
|
-
return CATALOG_JSON_ENTRIES;
|
|
103
|
+
return deepFreeze(structuredClone(CATALOG_JSON_ENTRIES));
|
|
75
104
|
}
|
|
76
105
|
export function buildCatalogEntries() {
|
|
77
106
|
return CATALOG_JSON_ENTRIES.map((entry) => {
|
package/dist/providers/cohere.js
CHANGED
|
@@ -87,7 +87,7 @@ export class CohereProvider extends OpenAIChatCompletionsProvider {
|
|
|
87
87
|
},
|
|
88
88
|
...DEFAULT_ERROR_RULES,
|
|
89
89
|
];
|
|
90
|
-
return classifyProviderError(error, rules,
|
|
90
|
+
return classifyProviderError(error, rules, this.providerName, this.modelName);
|
|
91
91
|
}
|
|
92
92
|
async validateConfiguration() {
|
|
93
93
|
return (typeof this.config.apiKey === "string" &&
|
|
@@ -89,7 +89,7 @@ export class DeepSeekProvider extends OpenAIChatCompletionsProvider {
|
|
|
89
89
|
},
|
|
90
90
|
...DEFAULT_ERROR_RULES,
|
|
91
91
|
];
|
|
92
|
-
return classifyProviderError(error, rules,
|
|
92
|
+
return classifyProviderError(error, rules, this.providerName, this.modelName);
|
|
93
93
|
}
|
|
94
94
|
// ===========================================================================
|
|
95
95
|
// Optional hooks — provider-specific quirks
|
|
@@ -1289,12 +1289,13 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
1289
1289
|
...options,
|
|
1290
1290
|
tools: baseTools,
|
|
1291
1291
|
};
|
|
1292
|
-
// Check for tools + JSON schema conflict (
|
|
1292
|
+
// Check for tools + JSON schema conflict, via the shared predicate (same
|
|
1293
|
+
// gate executeStream() and executeNativeGemini3Generate() use) so this
|
|
1294
|
+
// path also honors isGeminiProvider/isNativeAnthropicProvider instead of
|
|
1295
|
+
// hand-rolling its own tools+schema condition.
|
|
1293
1296
|
const wantsJsonOutput = options.output?.format === "json" || options.schema;
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
Object.keys(mergedOptions.tools).length > 0 &&
|
|
1297
|
-
!mergedOptions.disableTools) {
|
|
1297
|
+
const exclusionInForce = isToolsSchemaExclusionInForce(this.providerName, modelName, !mergedOptions.disableTools, Object.keys(mergedOptions.tools ?? {}).length);
|
|
1298
|
+
if (wantsJsonOutput && exclusionInForce) {
|
|
1298
1299
|
logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
|
|
1299
1300
|
mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
|
|
1300
1301
|
}
|
|
@@ -138,32 +138,6 @@ export declare class GoogleVertexProvider extends BaseProvider {
|
|
|
138
138
|
* this purpose.
|
|
139
139
|
*/
|
|
140
140
|
private resolveExpressApiKey;
|
|
141
|
-
/**
|
|
142
|
-
* Convert one AI-SDK tool into a Vertex Gemini function declaration.
|
|
143
|
-
* Single source for the pre-loop snapshot AND the mid-turn discovery
|
|
144
|
-
* refresh, so tools hydrated by search_tools get the exact same schema
|
|
145
|
-
* treatment (inline, typed, additionalProperties stripped).
|
|
146
|
-
*/
|
|
147
|
-
private buildGeminiFunctionDeclaration;
|
|
148
|
-
/**
|
|
149
|
-
* Mid-turn tool sync for the native Gemini loops. `search_tools` hydrates
|
|
150
|
-
* discovered tools into the live `options.tools` record between steps, but
|
|
151
|
-
* the loop's declarations + executeMap are a pre-loop snapshot — without
|
|
152
|
-
* this refresh, a tool discovered this turn stays invisible to the rest of
|
|
153
|
-
* the turn and every call to it dies as TOOL_NOT_FOUND. Mutates the
|
|
154
|
-
* declaration array in place (the request config holds it by reference)
|
|
155
|
-
* and clears breaker strikes accrued while the tool was still deferred.
|
|
156
|
-
*/
|
|
157
|
-
private refreshGeminiToolDeclarations;
|
|
158
|
-
/**
|
|
159
|
-
* Dispatch-miss recovery for the native Gemini loops: the model called a
|
|
160
|
-
* name missing from the executeMap snapshot. Re-read the live record (a
|
|
161
|
-
* tool hydrated by search_tools in this very step batch) or auto-hydrate a
|
|
162
|
-
* deferred catalog tool the model called directly by its advertised name.
|
|
163
|
-
* Returns the dedup-wrapped executor, or undefined when the name is
|
|
164
|
-
* genuinely unknown — callers keep TOOL_NOT_FOUND for that case.
|
|
165
|
-
*/
|
|
166
|
-
private resolveGeminiToolOnMiss;
|
|
167
141
|
/**
|
|
168
142
|
* Convert one AI-SDK tool into an Anthropic (Claude-on-Vertex) tool
|
|
169
143
|
* declaration. Single source for the pre-loop snapshot AND the mid-turn
|
|
@@ -174,18 +148,25 @@ export declare class GoogleVertexProvider extends BaseProvider {
|
|
|
174
148
|
*/
|
|
175
149
|
private buildAnthropicToolDeclaration;
|
|
176
150
|
/**
|
|
177
|
-
* Mid-turn tool sync for the Claude-on-Vertex loops
|
|
178
|
-
* counterpart of refreshGeminiToolDeclarations. Claude only calls tools
|
|
151
|
+
* Mid-turn tool sync for the Claude-on-Vertex loops. Claude only calls tools
|
|
179
152
|
* present in the request's `tools` array, so without this per-step refresh
|
|
180
153
|
* a tool discovered via search_tools is unreachable for the rest of the
|
|
181
154
|
* turn. Mutates the array in place (requestParams holds it by reference).
|
|
182
155
|
*/
|
|
183
156
|
private refreshAnthropicToolDeclarations;
|
|
184
157
|
/**
|
|
185
|
-
* Dispatch-miss recovery for the Claude-on-Vertex loops
|
|
186
|
-
* counterpart of resolveGeminiToolOnMiss.
|
|
158
|
+
* Dispatch-miss recovery for the Claude-on-Vertex loops.
|
|
187
159
|
*/
|
|
188
160
|
private resolveAnthropicToolOnMiss;
|
|
161
|
+
/**
|
|
162
|
+
* Build the synthetic `final_result` tool used to force structured output
|
|
163
|
+
* on Claude-on-Vertex. Anthropic has no native responseSchema, so a schema
|
|
164
|
+
* is instead enforced by requiring the model to call this tool with
|
|
165
|
+
* matching arguments. Single source for the non-streaming and streaming
|
|
166
|
+
* paths — both previously built this object independently from their own
|
|
167
|
+
* schema variable, byte-identical apart from that variable's name.
|
|
168
|
+
*/
|
|
169
|
+
private buildFinalResultTool;
|
|
189
170
|
/**
|
|
190
171
|
* Execute stream using native @google/genai SDK for Gemini 3 models on Vertex AI
|
|
191
172
|
* This bypasses @ai-sdk/google-vertex to properly handle thought_signature
|