@juspay/neurolink 9.64.0 → 9.65.0
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 +6 -0
- package/README.md +18 -17
- package/dist/adapters/providerImageAdapter.js +29 -1
- package/dist/adapters/replicate/auth.d.ts +19 -0
- package/dist/adapters/replicate/auth.js +32 -0
- package/dist/adapters/replicate/predictionLifecycle.d.ts +46 -0
- package/dist/adapters/replicate/predictionLifecycle.js +283 -0
- package/dist/adapters/video/klingVideoHandler.d.ts +37 -0
- package/dist/adapters/video/klingVideoHandler.js +305 -0
- package/dist/adapters/video/replicateVideoHandler.d.ts +29 -0
- package/dist/adapters/video/replicateVideoHandler.js +157 -0
- package/dist/adapters/video/runwayVideoHandler.d.ts +32 -0
- package/dist/adapters/video/runwayVideoHandler.js +316 -0
- package/dist/adapters/video/vertexVideoHandler.d.ts +19 -1
- package/dist/adapters/video/vertexVideoHandler.js +33 -9
- package/dist/autoresearch/runner.js +8 -2
- package/dist/avatar/index.d.ts +13 -0
- package/dist/avatar/index.js +13 -0
- package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
- package/dist/avatar/providers/DIDAvatar.js +501 -0
- package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
- package/dist/avatar/providers/HeyGenAvatar.js +337 -0
- package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
- package/dist/avatar/providers/ReplicateAvatar.js +267 -0
- package/dist/browser/neurolink.min.js +633 -610
- package/dist/cli/commands/mcp.js +29 -0
- package/dist/cli/commands/proxy.js +24 -5
- package/dist/cli/factories/commandFactory.d.ts +11 -1
- package/dist/cli/factories/commandFactory.js +291 -38
- package/dist/constants/contextWindows.js +101 -0
- package/dist/constants/enums.d.ts +273 -2
- package/dist/constants/enums.js +290 -1
- package/dist/constants/videoErrors.d.ts +4 -0
- package/dist/constants/videoErrors.js +4 -0
- package/dist/core/baseProvider.d.ts +22 -2
- package/dist/core/baseProvider.js +217 -11
- package/dist/core/constants.d.ts +11 -0
- package/dist/core/constants.js +69 -1
- package/dist/core/redisConversationMemoryManager.js +6 -0
- package/dist/evaluation/index.d.ts +2 -0
- package/dist/evaluation/index.js +4 -0
- package/dist/factories/providerFactory.js +7 -1
- package/dist/factories/providerRegistry.js +202 -5
- package/dist/features/ppt/contentPlanner.js +42 -14
- package/dist/index.d.ts +9 -1
- package/dist/index.js +16 -1
- package/dist/lib/adapters/providerImageAdapter.js +29 -1
- package/dist/lib/adapters/replicate/auth.d.ts +19 -0
- package/dist/lib/adapters/replicate/auth.js +33 -0
- package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
- package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
- package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
- package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
- package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
- package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
- package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
- package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
- package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
- package/dist/lib/adapters/video/vertexVideoHandler.js +33 -9
- package/dist/lib/autoresearch/runner.js +8 -2
- package/dist/lib/avatar/index.d.ts +13 -0
- package/dist/lib/avatar/index.js +14 -0
- package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
- package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
- package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
- package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
- package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
- package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
- package/dist/lib/constants/contextWindows.js +101 -0
- package/dist/lib/constants/enums.d.ts +273 -2
- package/dist/lib/constants/enums.js +290 -1
- package/dist/lib/constants/videoErrors.d.ts +4 -0
- package/dist/lib/constants/videoErrors.js +4 -0
- package/dist/lib/core/baseProvider.d.ts +22 -2
- package/dist/lib/core/baseProvider.js +217 -11
- package/dist/lib/core/constants.d.ts +11 -0
- package/dist/lib/core/constants.js +69 -1
- package/dist/lib/core/redisConversationMemoryManager.js +6 -0
- package/dist/lib/evaluation/index.d.ts +2 -0
- package/dist/lib/evaluation/index.js +4 -0
- package/dist/lib/factories/providerFactory.js +7 -1
- package/dist/lib/factories/providerRegistry.js +202 -5
- package/dist/lib/features/ppt/contentPlanner.js +42 -14
- package/dist/lib/index.d.ts +9 -1
- package/dist/lib/index.js +16 -1
- package/dist/lib/middleware/builtin/lifecycle.js +39 -9
- package/dist/lib/music/index.d.ts +13 -0
- package/dist/lib/music/index.js +14 -0
- package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
- package/dist/lib/music/providers/BeatovenMusic.js +334 -0
- package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
- package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
- package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
- package/dist/lib/music/providers/LyriaMusic.js +173 -0
- package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
- package/dist/lib/music/providers/ReplicateMusic.js +262 -0
- package/dist/lib/neurolink.d.ts +30 -0
- package/dist/lib/neurolink.js +323 -77
- package/dist/lib/providers/amazonBedrock.d.ts +10 -0
- package/dist/lib/providers/amazonBedrock.js +94 -39
- package/dist/lib/providers/anthropic.js +55 -7
- package/dist/lib/providers/anthropicBaseProvider.js +1 -1
- package/dist/lib/providers/azureOpenai.js +66 -17
- package/dist/lib/providers/cloudflare.d.ts +35 -0
- package/dist/lib/providers/cloudflare.js +174 -0
- package/dist/lib/providers/cohere.d.ts +52 -0
- package/dist/lib/providers/cohere.js +253 -0
- package/dist/lib/providers/deepseek.js +72 -17
- package/dist/lib/providers/fireworks.d.ts +33 -0
- package/dist/lib/providers/fireworks.js +164 -0
- package/dist/lib/providers/googleAiStudio.js +45 -6
- package/dist/lib/providers/googleNativeGemini3.d.ts +24 -1
- package/dist/lib/providers/googleNativeGemini3.js +173 -21
- package/dist/lib/providers/googleVertex.js +173 -17
- package/dist/lib/providers/groq.d.ts +33 -0
- package/dist/lib/providers/groq.js +181 -0
- package/dist/lib/providers/huggingFace.js +9 -8
- package/dist/lib/providers/ideogram.d.ts +34 -0
- package/dist/lib/providers/ideogram.js +184 -0
- package/dist/lib/providers/index.d.ts +13 -0
- package/dist/lib/providers/index.js +13 -0
- package/dist/lib/providers/jina.d.ts +59 -0
- package/dist/lib/providers/jina.js +218 -0
- package/dist/lib/providers/llamaCpp.js +14 -46
- package/dist/lib/providers/lmStudio.js +14 -47
- package/dist/lib/providers/mistral.js +7 -7
- package/dist/lib/providers/nvidiaNim.js +160 -19
- package/dist/lib/providers/ollama.js +7 -7
- package/dist/lib/providers/openAI.d.ts +22 -1
- package/dist/lib/providers/openAI.js +181 -0
- package/dist/lib/providers/openRouter.js +35 -23
- package/dist/lib/providers/openaiCompatible.js +9 -8
- package/dist/lib/providers/perplexity.d.ts +33 -0
- package/dist/lib/providers/perplexity.js +179 -0
- package/dist/lib/providers/recraft.d.ts +34 -0
- package/dist/lib/providers/recraft.js +197 -0
- package/dist/lib/providers/replicate.d.ts +75 -0
- package/dist/lib/providers/replicate.js +403 -0
- package/dist/lib/providers/stability.d.ts +37 -0
- package/dist/lib/providers/stability.js +191 -0
- package/dist/lib/providers/togetherAi.d.ts +33 -0
- package/dist/lib/providers/togetherAi.js +176 -0
- package/dist/lib/providers/voyage.d.ts +47 -0
- package/dist/lib/providers/voyage.js +177 -0
- package/dist/lib/providers/xai.d.ts +33 -0
- package/dist/lib/providers/xai.js +172 -0
- package/dist/lib/telemetry/index.d.ts +1 -1
- package/dist/lib/telemetry/index.js +1 -1
- package/dist/lib/telemetry/tracers.d.ts +19 -0
- package/dist/lib/telemetry/tracers.js +19 -0
- package/dist/lib/telemetry/withSpan.d.ts +35 -0
- package/dist/lib/telemetry/withSpan.js +103 -0
- package/dist/lib/types/avatar.d.ts +143 -0
- package/dist/lib/types/avatar.js +20 -0
- package/dist/lib/types/cli.d.ts +6 -0
- package/dist/lib/types/generate.d.ts +62 -5
- package/dist/lib/types/index.d.ts +5 -0
- package/dist/lib/types/index.js +7 -0
- package/dist/lib/types/middleware.d.ts +27 -0
- package/dist/lib/types/multimodal.d.ts +35 -2
- package/dist/lib/types/music.d.ts +165 -0
- package/dist/lib/types/music.js +21 -0
- package/dist/lib/types/providers.d.ts +144 -1
- package/dist/lib/types/replicate.d.ts +67 -0
- package/dist/lib/types/replicate.js +10 -0
- package/dist/lib/types/safeFetch.d.ts +15 -0
- package/dist/lib/types/safeFetch.js +7 -0
- package/dist/lib/types/stream.d.ts +2 -1
- package/dist/lib/types/tools.d.ts +13 -0
- package/dist/lib/types/video.d.ts +89 -0
- package/dist/lib/types/video.js +15 -0
- package/dist/lib/utils/avatarProcessor.d.ts +68 -0
- package/dist/lib/utils/avatarProcessor.js +172 -0
- package/dist/lib/utils/cloneOptions.d.ts +36 -0
- package/dist/lib/utils/cloneOptions.js +62 -0
- package/dist/lib/utils/lifecycleCallbacks.d.ts +51 -8
- package/dist/lib/utils/lifecycleCallbacks.js +82 -26
- package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
- package/dist/lib/utils/lifecycleTimeout.js +39 -0
- package/dist/lib/utils/logSanitize.d.ts +49 -0
- package/dist/lib/utils/logSanitize.js +170 -0
- package/dist/lib/utils/loggingFetch.d.ts +29 -0
- package/dist/lib/utils/loggingFetch.js +60 -0
- package/dist/lib/utils/messageBuilder.js +43 -25
- package/dist/lib/utils/modelChoices.js +236 -3
- package/dist/lib/utils/musicProcessor.d.ts +67 -0
- package/dist/lib/utils/musicProcessor.js +189 -0
- package/dist/lib/utils/optionsConversion.js +3 -2
- package/dist/lib/utils/parameterValidation.js +14 -4
- package/dist/lib/utils/pricing.js +193 -0
- package/dist/lib/utils/providerConfig.d.ts +55 -0
- package/dist/lib/utils/providerConfig.js +224 -0
- package/dist/lib/utils/safeFetch.d.ts +26 -0
- package/dist/lib/utils/safeFetch.js +83 -0
- package/dist/lib/utils/sizeGuard.d.ts +34 -0
- package/dist/lib/utils/sizeGuard.js +45 -0
- package/dist/lib/utils/ssrfGuard.d.ts +52 -0
- package/dist/lib/utils/ssrfGuard.js +411 -0
- package/dist/lib/utils/videoProcessor.d.ts +60 -0
- package/dist/lib/utils/videoProcessor.js +201 -0
- package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
- package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
- package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
- package/dist/middleware/builtin/lifecycle.js +39 -9
- package/dist/music/index.d.ts +13 -0
- package/dist/music/index.js +13 -0
- package/dist/music/providers/BeatovenMusic.d.ts +31 -0
- package/dist/music/providers/BeatovenMusic.js +333 -0
- package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
- package/dist/music/providers/ElevenLabsMusic.js +168 -0
- package/dist/music/providers/LyriaMusic.d.ts +29 -0
- package/dist/music/providers/LyriaMusic.js +172 -0
- package/dist/music/providers/ReplicateMusic.d.ts +31 -0
- package/dist/music/providers/ReplicateMusic.js +261 -0
- package/dist/neurolink.d.ts +30 -0
- package/dist/neurolink.js +323 -77
- package/dist/providers/amazonBedrock.d.ts +10 -0
- package/dist/providers/amazonBedrock.js +94 -39
- package/dist/providers/anthropic.js +55 -7
- package/dist/providers/anthropicBaseProvider.js +1 -1
- package/dist/providers/azureOpenai.js +66 -17
- package/dist/providers/cloudflare.d.ts +35 -0
- package/dist/providers/cloudflare.js +173 -0
- package/dist/providers/cohere.d.ts +52 -0
- package/dist/providers/cohere.js +252 -0
- package/dist/providers/deepseek.js +72 -17
- package/dist/providers/fireworks.d.ts +33 -0
- package/dist/providers/fireworks.js +163 -0
- package/dist/providers/googleAiStudio.js +45 -6
- package/dist/providers/googleNativeGemini3.d.ts +24 -1
- package/dist/providers/googleNativeGemini3.js +173 -21
- package/dist/providers/googleVertex.js +173 -17
- package/dist/providers/groq.d.ts +33 -0
- package/dist/providers/groq.js +180 -0
- package/dist/providers/huggingFace.js +9 -8
- package/dist/providers/ideogram.d.ts +34 -0
- package/dist/providers/ideogram.js +183 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.js +13 -0
- package/dist/providers/jina.d.ts +59 -0
- package/dist/providers/jina.js +217 -0
- package/dist/providers/llamaCpp.js +14 -46
- package/dist/providers/lmStudio.js +14 -47
- package/dist/providers/mistral.js +7 -7
- package/dist/providers/nvidiaNim.js +160 -19
- package/dist/providers/ollama.js +7 -7
- package/dist/providers/openAI.d.ts +22 -1
- package/dist/providers/openAI.js +181 -0
- package/dist/providers/openRouter.js +35 -23
- package/dist/providers/openaiCompatible.js +9 -8
- package/dist/providers/perplexity.d.ts +33 -0
- package/dist/providers/perplexity.js +178 -0
- package/dist/providers/recraft.d.ts +34 -0
- package/dist/providers/recraft.js +196 -0
- package/dist/providers/replicate.d.ts +75 -0
- package/dist/providers/replicate.js +402 -0
- package/dist/providers/stability.d.ts +37 -0
- package/dist/providers/stability.js +190 -0
- package/dist/providers/togetherAi.d.ts +33 -0
- package/dist/providers/togetherAi.js +175 -0
- package/dist/providers/voyage.d.ts +47 -0
- package/dist/providers/voyage.js +176 -0
- package/dist/providers/xai.d.ts +33 -0
- package/dist/providers/xai.js +171 -0
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/tracers.d.ts +19 -0
- package/dist/telemetry/tracers.js +19 -0
- package/dist/telemetry/withSpan.d.ts +35 -0
- package/dist/telemetry/withSpan.js +103 -0
- package/dist/types/avatar.d.ts +143 -0
- package/dist/types/avatar.js +19 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/generate.d.ts +62 -5
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +7 -0
- package/dist/types/middleware.d.ts +27 -0
- package/dist/types/multimodal.d.ts +35 -2
- package/dist/types/music.d.ts +165 -0
- package/dist/types/music.js +20 -0
- package/dist/types/providers.d.ts +144 -1
- package/dist/types/replicate.d.ts +67 -0
- package/dist/types/replicate.js +9 -0
- package/dist/types/safeFetch.d.ts +15 -0
- package/dist/types/safeFetch.js +6 -0
- package/dist/types/stream.d.ts +2 -1
- package/dist/types/tools.d.ts +13 -0
- package/dist/types/video.d.ts +89 -0
- package/dist/types/video.js +14 -0
- package/dist/utils/avatarProcessor.d.ts +68 -0
- package/dist/utils/avatarProcessor.js +171 -0
- package/dist/utils/cloneOptions.d.ts +36 -0
- package/dist/utils/cloneOptions.js +61 -0
- package/dist/utils/lifecycleCallbacks.d.ts +51 -8
- package/dist/utils/lifecycleCallbacks.js +82 -26
- package/dist/utils/lifecycleTimeout.d.ts +25 -0
- package/dist/utils/lifecycleTimeout.js +38 -0
- package/dist/utils/logSanitize.d.ts +49 -0
- package/dist/utils/logSanitize.js +169 -0
- package/dist/utils/loggingFetch.d.ts +29 -0
- package/dist/utils/loggingFetch.js +59 -0
- package/dist/utils/messageBuilder.js +43 -25
- package/dist/utils/modelChoices.js +236 -3
- package/dist/utils/musicProcessor.d.ts +67 -0
- package/dist/utils/musicProcessor.js +188 -0
- package/dist/utils/optionsConversion.js +3 -2
- package/dist/utils/parameterValidation.js +14 -4
- package/dist/utils/pricing.js +193 -0
- package/dist/utils/providerConfig.d.ts +55 -0
- package/dist/utils/providerConfig.js +224 -0
- package/dist/utils/safeFetch.d.ts +26 -0
- package/dist/utils/safeFetch.js +82 -0
- package/dist/utils/sizeGuard.d.ts +34 -0
- package/dist/utils/sizeGuard.js +44 -0
- package/dist/utils/ssrfGuard.d.ts +52 -0
- package/dist/utils/ssrfGuard.js +410 -0
- package/dist/utils/videoProcessor.d.ts +60 -0
- package/dist/utils/videoProcessor.js +200 -0
- package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
- package/dist/voice/providers/FishAudioTTS.js +182 -0
- package/dist/workflow/core/ensembleExecutor.js +26 -9
- package/package.json +32 -5
|
@@ -17,6 +17,61 @@ import { logger } from "../utils/logger.js";
|
|
|
17
17
|
import { convertZodToJsonSchema, ensureNestedSchemaTypes, inlineJsonSchema, isZodSchema, normalizeJsonSchemaObject, } from "../utils/schemaConversion.js";
|
|
18
18
|
import { createNativeThinkingConfig } from "../utils/thinkingConfig.js";
|
|
19
19
|
// ── Functions ──
|
|
20
|
+
/**
|
|
21
|
+
* Google's `function_declarations[].name` validator regex.
|
|
22
|
+
*
|
|
23
|
+
* Empirically (and per the Vertex/AI Studio API error message), the server
|
|
24
|
+
* enforces `[A-Za-z_][A-Za-z0-9_.:-]{0,127}`. Tool names that don't match
|
|
25
|
+
* fail with HTTP 400 "Invalid function name", which surfaces as a misleading
|
|
26
|
+
* tool-calling failure for the whole request.
|
|
27
|
+
*
|
|
28
|
+
* MCP-imported or user-registered tools may legally contain characters
|
|
29
|
+
* outside this set (e.g. `/`, spaces, unicode), so we sanitize defensively
|
|
30
|
+
* before sending to Google. The sanitized name is also used as the
|
|
31
|
+
* `executeMap` key so the round-trip from Google's function-call response
|
|
32
|
+
* back to our executor still works.
|
|
33
|
+
*/
|
|
34
|
+
const GOOGLE_FN_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_.:-]{0,127}$/;
|
|
35
|
+
const GOOGLE_FN_NAME_MAX_LENGTH = 128;
|
|
36
|
+
export function sanitizeForGoogleFunctionName(name) {
|
|
37
|
+
if (GOOGLE_FN_NAME_REGEX.test(name)) {
|
|
38
|
+
return name;
|
|
39
|
+
}
|
|
40
|
+
let sanitized = name.replace(/[^A-Za-z0-9_.:-]/g, "_");
|
|
41
|
+
if (!/^[A-Za-z_]/.test(sanitized)) {
|
|
42
|
+
sanitized = `_${sanitized}`;
|
|
43
|
+
}
|
|
44
|
+
if (sanitized.length > GOOGLE_FN_NAME_MAX_LENGTH) {
|
|
45
|
+
sanitized = sanitized.slice(0, GOOGLE_FN_NAME_MAX_LENGTH);
|
|
46
|
+
}
|
|
47
|
+
return sanitized;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a sanitized Gemini tool name to one that is both unique within
|
|
51
|
+
* the current request and at most 128 characters. When the candidate
|
|
52
|
+
* collides with an already-used name we append `_2`, `_3`, … — but
|
|
53
|
+
* reserve room for the suffix by truncating the base first so the
|
|
54
|
+
* resolved name never exceeds Google's `function_declarations[].name`
|
|
55
|
+
* limit.
|
|
56
|
+
*
|
|
57
|
+
* @param base The already-sanitized candidate name.
|
|
58
|
+
* @param isTaken Predicate that returns true if `name` is already used.
|
|
59
|
+
*/
|
|
60
|
+
export function resolveUniqueGoogleFunctionName(base, isTaken) {
|
|
61
|
+
if (!isTaken(base)) {
|
|
62
|
+
return base;
|
|
63
|
+
}
|
|
64
|
+
let suffix = 2;
|
|
65
|
+
while (true) {
|
|
66
|
+
const suffixStr = `_${suffix}`;
|
|
67
|
+
const trimmedBase = base.slice(0, GOOGLE_FN_NAME_MAX_LENGTH - suffixStr.length);
|
|
68
|
+
const candidate = `${trimmedBase}${suffixStr}`;
|
|
69
|
+
if (!isTaken(candidate)) {
|
|
70
|
+
return candidate;
|
|
71
|
+
}
|
|
72
|
+
suffix++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
20
75
|
/**
|
|
21
76
|
* Sanitize a JSON Schema for Gemini's proto-based API.
|
|
22
77
|
*
|
|
@@ -102,6 +157,40 @@ export function sanitizeSchemaForGemini(schema) {
|
|
|
102
157
|
result[branch] = sanitizeSchemaForGemini(result[branch]);
|
|
103
158
|
}
|
|
104
159
|
}
|
|
160
|
+
// JSON Schema Draft-4 `exclusiveMinimum: true` / `exclusiveMaximum: true`
|
|
161
|
+
// (boolean form) is rejected by Gemini's OpenAPI 3.0 validator, which
|
|
162
|
+
// expects a numeric bound. zod-to-json-schema's openApi3 target still
|
|
163
|
+
// emits the Draft-4 form for `z.number().positive()` etc. Translate to
|
|
164
|
+
// the numeric form when paired with `minimum`/`maximum`, or drop.
|
|
165
|
+
if (typeof result.exclusiveMinimum === "boolean") {
|
|
166
|
+
if (result.exclusiveMinimum === true &&
|
|
167
|
+
typeof result.minimum === "number") {
|
|
168
|
+
result.exclusiveMinimum = result.minimum;
|
|
169
|
+
delete result.minimum;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
delete result.exclusiveMinimum;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (typeof result.exclusiveMaximum === "boolean") {
|
|
176
|
+
if (result.exclusiveMaximum === true &&
|
|
177
|
+
typeof result.maximum === "number") {
|
|
178
|
+
result.exclusiveMaximum = result.maximum;
|
|
179
|
+
delete result.maximum;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
delete result.exclusiveMaximum;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Clamp `maximum`/`minimum` past int32 — Gemini's protobuf serializer
|
|
186
|
+
// treats `type: "integer"` as int32 and rejects bounds beyond ~2.1e9.
|
|
187
|
+
const INT32_MAX = 2147483647;
|
|
188
|
+
if (typeof result.maximum === "number" && result.maximum > INT32_MAX) {
|
|
189
|
+
delete result.maximum;
|
|
190
|
+
}
|
|
191
|
+
if (typeof result.minimum === "number" && result.minimum < -INT32_MAX) {
|
|
192
|
+
delete result.minimum;
|
|
193
|
+
}
|
|
105
194
|
return result;
|
|
106
195
|
}
|
|
107
196
|
/**
|
|
@@ -117,8 +206,27 @@ export function sanitizeSchemaForGemini(schema) {
|
|
|
117
206
|
export function sanitizeToolsForGemini(tools) {
|
|
118
207
|
const sanitized = {};
|
|
119
208
|
const dropped = [];
|
|
209
|
+
const renamed = [];
|
|
210
|
+
const originalNameMap = new Map();
|
|
120
211
|
for (const [name, tool] of Object.entries(tools)) {
|
|
121
212
|
try {
|
|
213
|
+
// Sanitize the tool name to fit Google's function_declarations regex.
|
|
214
|
+
// Without this, MCP-imported or user-registered tools whose names contain
|
|
215
|
+
// characters outside [A-Za-z_][A-Za-z0-9_.:-]{0,127} cause the entire
|
|
216
|
+
// request to 400 with "Invalid function name", surfacing as a misleading
|
|
217
|
+
// tool-calling failure. Distinct originals that collapse onto the same
|
|
218
|
+
// sanitized name (e.g. "my/tool" and "my-tool" → "my_tool") are
|
|
219
|
+
// disambiguated with a numeric suffix that preserves Google's 128-char
|
|
220
|
+
// ceiling.
|
|
221
|
+
const candidate = sanitizeForGoogleFunctionName(name);
|
|
222
|
+
const safeName = resolveUniqueGoogleFunctionName(candidate, (n) => n in sanitized);
|
|
223
|
+
// Always record the mapping so downstream code can translate every
|
|
224
|
+
// safeName back to the original — including the no-rename identity
|
|
225
|
+
// mapping, which simplifies the lookup path.
|
|
226
|
+
originalNameMap.set(safeName, name);
|
|
227
|
+
if (safeName !== name) {
|
|
228
|
+
renamed.push({ from: name, to: safeName });
|
|
229
|
+
}
|
|
122
230
|
// Access the legacy `parameters` field that may exist on older AI SDK tools.
|
|
123
231
|
// AI SDK v6 uses `inputSchema`, but v3/v4 tools and third-party wrappers use `parameters`.
|
|
124
232
|
const legacyTool = tool;
|
|
@@ -134,8 +242,8 @@ export function sanitizeToolsForGemini(tools) {
|
|
|
134
242
|
// additionalProperties are removed. The resulting schema is Gemini-compatible
|
|
135
243
|
// but loses some type constraints from the original Zod schema.
|
|
136
244
|
const sanitizedSchema = sanitizeSchemaForGemini(inlined);
|
|
137
|
-
sanitized[
|
|
138
|
-
description: tool.description || `Tool: ${
|
|
245
|
+
sanitized[safeName] = createAISDKTool({
|
|
246
|
+
description: tool.description || `Tool: ${safeName}`,
|
|
139
247
|
inputSchema: aiJsonSchema(sanitizedSchema),
|
|
140
248
|
execute: tool.execute,
|
|
141
249
|
});
|
|
@@ -147,14 +255,14 @@ export function sanitizeToolsForGemini(tools) {
|
|
|
147
255
|
const rawSchema = params
|
|
148
256
|
.jsonSchema;
|
|
149
257
|
const sanitizedSchema = sanitizeSchemaForGemini(inlineJsonSchema(rawSchema));
|
|
150
|
-
sanitized[
|
|
151
|
-
description: tool.description || `Tool: ${
|
|
258
|
+
sanitized[safeName] = createAISDKTool({
|
|
259
|
+
description: tool.description || `Tool: ${safeName}`,
|
|
152
260
|
inputSchema: aiJsonSchema(sanitizedSchema),
|
|
153
261
|
execute: tool.execute,
|
|
154
262
|
});
|
|
155
263
|
}
|
|
156
264
|
else {
|
|
157
|
-
sanitized[
|
|
265
|
+
sanitized[safeName] = tool;
|
|
158
266
|
}
|
|
159
267
|
}
|
|
160
268
|
catch (error) {
|
|
@@ -163,7 +271,12 @@ export function sanitizeToolsForGemini(tools) {
|
|
|
163
271
|
dropped.push(name);
|
|
164
272
|
}
|
|
165
273
|
}
|
|
166
|
-
|
|
274
|
+
if (renamed.length > 0) {
|
|
275
|
+
logger.warn(`[Gemini] ${renamed.length} tool name(s) sanitized for Google's function-name regex: ${renamed
|
|
276
|
+
.map((r) => `"${r.from}" -> "${r.to}"`)
|
|
277
|
+
.join(", ")}`);
|
|
278
|
+
}
|
|
279
|
+
return { tools: sanitized, dropped, originalNameMap };
|
|
167
280
|
}
|
|
168
281
|
export function normalizeToolsForJsonSchemaProvider(tools) {
|
|
169
282
|
const normalizedTools = {};
|
|
@@ -212,11 +325,29 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
212
325
|
const functionDeclarations = [];
|
|
213
326
|
const executeMap = new Map();
|
|
214
327
|
const skippedTools = [];
|
|
328
|
+
const renamedTools = [];
|
|
329
|
+
// Disambiguate distinct originals that collapse onto the same sanitized
|
|
330
|
+
// name (e.g. "my/tool" and "my-tool" both → "my_tool") via
|
|
331
|
+
// resolveUniqueGoogleFunctionName, which appends `_N` while keeping the
|
|
332
|
+
// final string within Google's 128-char limit. Track all assigned names
|
|
333
|
+
// regardless of whether the tool has an `execute` function (tools without
|
|
334
|
+
// execute are still pushed to functionDeclarations). The originalNameMap
|
|
335
|
+
// lets the calling stream loop translate Google-returned function-call
|
|
336
|
+
// names back to the consumer-facing identifier so the sanitization is
|
|
337
|
+
// transport-only.
|
|
338
|
+
const usedNames = new Set();
|
|
339
|
+
const originalNameMap = new Map();
|
|
215
340
|
for (const [name, tool] of Object.entries(tools)) {
|
|
216
341
|
try {
|
|
342
|
+
const candidate = sanitizeForGoogleFunctionName(name);
|
|
343
|
+
const safeName = resolveUniqueGoogleFunctionName(candidate, (n) => usedNames.has(n));
|
|
344
|
+
originalNameMap.set(safeName, name);
|
|
345
|
+
if (safeName !== name) {
|
|
346
|
+
renamedTools.push({ from: name, to: safeName });
|
|
347
|
+
}
|
|
217
348
|
const decl = {
|
|
218
|
-
name,
|
|
219
|
-
description: tool.description || `Tool: ${
|
|
349
|
+
name: safeName,
|
|
350
|
+
description: tool.description || `Tool: ${safeName}`,
|
|
220
351
|
};
|
|
221
352
|
// Access legacy `parameters` (AI SDK v3/v4) or current `inputSchema` (v6)
|
|
222
353
|
const legacyTool = tool;
|
|
@@ -241,8 +372,9 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
241
372
|
decl.parametersJsonSchema = sanitizeSchemaForGemini(inlineJsonSchema(rawSchema));
|
|
242
373
|
}
|
|
243
374
|
functionDeclarations.push(decl);
|
|
375
|
+
usedNames.add(safeName);
|
|
244
376
|
if (tool.execute) {
|
|
245
|
-
executeMap.set(name, tool.execute);
|
|
377
|
+
executeMap.set(decl.name, tool.execute);
|
|
246
378
|
}
|
|
247
379
|
}
|
|
248
380
|
catch (err) {
|
|
@@ -253,7 +385,16 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
253
385
|
if (skippedTools.length > 0) {
|
|
254
386
|
logger.warn(`[buildNativeToolDeclarations] ${skippedTools.length} tool(s) skipped due to schema errors: ${skippedTools.join(", ")}`);
|
|
255
387
|
}
|
|
256
|
-
|
|
388
|
+
if (renamedTools.length > 0) {
|
|
389
|
+
logger.warn(`[buildNativeToolDeclarations] ${renamedTools.length} tool name(s) sanitized for Google's function-name regex: ${renamedTools
|
|
390
|
+
.map((r) => `"${r.from}" -> "${r.to}"`)
|
|
391
|
+
.join(", ")}`);
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
toolsConfig: [{ functionDeclarations }],
|
|
395
|
+
executeMap,
|
|
396
|
+
originalNameMap,
|
|
397
|
+
};
|
|
257
398
|
}
|
|
258
399
|
/**
|
|
259
400
|
* Build the native @google/genai config object shared by stream and generate.
|
|
@@ -493,27 +634,38 @@ export function extractTextFromParts(rawResponseParts) {
|
|
|
493
634
|
* @param executeMap - Map of tool name to execute function
|
|
494
635
|
* @param failedTools - Mutable map tracking per-tool failure counts
|
|
495
636
|
* @param allToolCalls - Mutable array accumulating all tool call records
|
|
496
|
-
* @param options - Optional settings for execution tracking and cancellation
|
|
637
|
+
* @param options - Optional settings for execution tracking and cancellation,
|
|
638
|
+
* plus an `originalNameMap` (Google-safe → consumer-supplied
|
|
639
|
+
* identifier) so the sanitization stays transport-only and
|
|
640
|
+
* consumers see the names they registered.
|
|
497
641
|
* @returns Array of function responses for conversation history
|
|
498
642
|
*/
|
|
499
643
|
export async function executeNativeToolCalls(logLabel, stepFunctionCalls, executeMap, failedTools, allToolCalls, options) {
|
|
500
644
|
const functionResponses = [];
|
|
645
|
+
// Translate a Google-safe sanitized name back to the consumer-facing
|
|
646
|
+
// original name. Falls back to the safe name if the map is missing or
|
|
647
|
+
// doesn't contain the call (e.g. tool added mid-conversation).
|
|
648
|
+
const externalName = (safeName) => options?.originalNameMap?.get(safeName) ?? safeName;
|
|
501
649
|
for (const call of stepFunctionCalls) {
|
|
502
|
-
|
|
650
|
+
const exposedName = externalName(call.name);
|
|
651
|
+
allToolCalls.push({ toolName: exposedName, args: call.args });
|
|
503
652
|
// Check if this tool has already exceeded retry limit
|
|
504
653
|
const failedInfo = failedTools.get(call.name);
|
|
505
654
|
if (failedInfo && failedInfo.count >= DEFAULT_TOOL_MAX_RETRIES) {
|
|
506
|
-
logger.warn(`${logLabel} Tool "${
|
|
655
|
+
logger.warn(`${logLabel} Tool "${exposedName}" has exceeded retry limit (${DEFAULT_TOOL_MAX_RETRIES}), skipping execution`);
|
|
507
656
|
const errorOutput = {
|
|
508
|
-
error: `TOOL_PERMANENTLY_FAILED: The tool "${
|
|
657
|
+
error: `TOOL_PERMANENTLY_FAILED: The tool "${exposedName}" has failed ${failedInfo.count} times and will not be retried. Last error: ${failedInfo.lastError}. Please proceed without using this tool or inform the user that this functionality is unavailable.`,
|
|
509
658
|
status: "permanently_failed",
|
|
510
659
|
do_not_retry: true,
|
|
511
660
|
};
|
|
661
|
+
// Wire transport-side `name: call.name` (Google needs the sanitized
|
|
662
|
+
// form to match the function declaration) while exposing the
|
|
663
|
+
// consumer-facing name in toolExecutions metadata.
|
|
512
664
|
functionResponses.push({
|
|
513
665
|
functionResponse: { name: call.name, response: errorOutput },
|
|
514
666
|
});
|
|
515
667
|
options?.toolExecutions?.push({
|
|
516
|
-
name:
|
|
668
|
+
name: exposedName,
|
|
517
669
|
input: call.args,
|
|
518
670
|
output: errorOutput,
|
|
519
671
|
});
|
|
@@ -534,7 +686,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
534
686
|
functionResponse: { name: call.name, response: { result } },
|
|
535
687
|
});
|
|
536
688
|
options?.toolExecutions?.push({
|
|
537
|
-
name:
|
|
689
|
+
name: exposedName,
|
|
538
690
|
input: call.args,
|
|
539
691
|
output: result,
|
|
540
692
|
});
|
|
@@ -549,12 +701,12 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
549
701
|
currentFailInfo.count++;
|
|
550
702
|
currentFailInfo.lastError = errorMessage;
|
|
551
703
|
failedTools.set(call.name, currentFailInfo);
|
|
552
|
-
logger.warn(`${logLabel} Tool "${
|
|
704
|
+
logger.warn(`${logLabel} Tool "${exposedName}" failed (attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}): ${errorMessage}`);
|
|
553
705
|
// Determine if this is a permanent failure
|
|
554
706
|
const isPermanentFailure = currentFailInfo.count >= DEFAULT_TOOL_MAX_RETRIES;
|
|
555
707
|
const errorOutput = {
|
|
556
708
|
error: isPermanentFailure
|
|
557
|
-
? `TOOL_PERMANENTLY_FAILED: The tool "${
|
|
709
|
+
? `TOOL_PERMANENTLY_FAILED: The tool "${exposedName}" has failed ${currentFailInfo.count} times with error: ${errorMessage}. This tool will not be retried. Please proceed without using this tool or inform the user that this functionality is unavailable.`
|
|
558
710
|
: `TOOL_EXECUTION_ERROR: ${errorMessage}. Retry attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}.`,
|
|
559
711
|
status: isPermanentFailure ? "permanently_failed" : "failed",
|
|
560
712
|
do_not_retry: isPermanentFailure,
|
|
@@ -565,7 +717,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
565
717
|
functionResponse: { name: call.name, response: errorOutput },
|
|
566
718
|
});
|
|
567
719
|
options?.toolExecutions?.push({
|
|
568
|
-
name:
|
|
720
|
+
name: exposedName,
|
|
569
721
|
input: call.args,
|
|
570
722
|
output: errorOutput,
|
|
571
723
|
});
|
|
@@ -574,7 +726,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
574
726
|
else {
|
|
575
727
|
// Tool not found is a permanent error
|
|
576
728
|
const errorOutput = {
|
|
577
|
-
error: `TOOL_NOT_FOUND: The tool "${
|
|
729
|
+
error: `TOOL_NOT_FOUND: The tool "${exposedName}" does not exist. Do not attempt to call this tool again.`,
|
|
578
730
|
status: "permanently_failed",
|
|
579
731
|
do_not_retry: true,
|
|
580
732
|
};
|
|
@@ -582,7 +734,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
582
734
|
functionResponse: { name: call.name, response: errorOutput },
|
|
583
735
|
});
|
|
584
736
|
options?.toolExecutions?.push({
|
|
585
|
-
name:
|
|
737
|
+
name: exposedName,
|
|
586
738
|
input: call.args,
|
|
587
739
|
output: errorOutput,
|
|
588
740
|
});
|
|
@@ -18,7 +18,7 @@ import { convertZodToJsonSchema, inlineJsonSchema, ensureNestedSchemaTypes, } fr
|
|
|
18
18
|
import { createNativeThinkingConfig } from "../utils/thinkingConfig.js";
|
|
19
19
|
import { TimeoutError } from "../utils/async/index.js";
|
|
20
20
|
import { prependConversationMessages } from "./googleNativeGemini3.js";
|
|
21
|
-
import { ATTR, tracers, withClientSpan, withSpan } from "../telemetry/index.js";
|
|
21
|
+
import { ATTR, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "../telemetry/index.js";
|
|
22
22
|
import { calculateCost } from "../utils/pricing.js";
|
|
23
23
|
// Import proper types for multimodal message handling
|
|
24
24
|
// Dynamic import helper for native Anthropic Vertex SDK
|
|
@@ -35,6 +35,110 @@ const hasAnthropicSupport = () => {
|
|
|
35
35
|
// Actual availability is checked at runtime when creating the client
|
|
36
36
|
return true;
|
|
37
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Recursively strip JSON-schema fields that Vertex Gemini's function-call
|
|
40
|
+
* validator rejects with 400 INVALID_ARGUMENT. Vertex implements OpenAPI 3.0
|
|
41
|
+
* Schema strictly and rejects extension fields that the broader JSON Schema
|
|
42
|
+
* spec allows. The fields stripped here have no semantic meaning for the
|
|
43
|
+
* model, so removing them is safe for every caller.
|
|
44
|
+
*
|
|
45
|
+
* Fields removed:
|
|
46
|
+
* - `additionalProperties` — extension; Vertex rejects on any nested object.
|
|
47
|
+
* - `default` — Vertex rejects defaults on object/array-typed properties and
|
|
48
|
+
* on properties that are also marked `required`. Safest to strip globally
|
|
49
|
+
* because the model never inspects them.
|
|
50
|
+
* - `$schema`, `$id`, `$ref`, `definitions`, `$defs` — JSON-Schema-meta
|
|
51
|
+
* fields that Vertex doesn't recognise.
|
|
52
|
+
* - `examples` — accepted by some Gemini variants but not 2.5-flash; strip
|
|
53
|
+
* to avoid the model rejecting tool schemas under that path.
|
|
54
|
+
*/
|
|
55
|
+
function stripAdditionalPropertiesDeep(schema) {
|
|
56
|
+
if (!schema || typeof schema !== "object") {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const FIELDS_TO_STRIP = [
|
|
60
|
+
"additionalProperties",
|
|
61
|
+
"default",
|
|
62
|
+
"$schema",
|
|
63
|
+
"$id",
|
|
64
|
+
"$ref",
|
|
65
|
+
"definitions",
|
|
66
|
+
"$defs",
|
|
67
|
+
"examples",
|
|
68
|
+
];
|
|
69
|
+
for (const field of FIELDS_TO_STRIP) {
|
|
70
|
+
if (field in schema) {
|
|
71
|
+
delete schema[field];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// JSON Schema Draft-4 `exclusiveMinimum: true` / `exclusiveMaximum: true`
|
|
75
|
+
// (boolean form) is rejected by Vertex's OpenAPI 3.0 validator, which
|
|
76
|
+
// expects a numeric bound. zod-to-json-schema's openApi3 target still
|
|
77
|
+
// emits the Draft-4 form for `z.number().positive()` etc. Translate the
|
|
78
|
+
// boolean form into the numeric form when paired with `minimum` /
|
|
79
|
+
// `maximum`; otherwise drop it (the model doesn't validate, so the
|
|
80
|
+
// constraint is informational only).
|
|
81
|
+
if (typeof schema.exclusiveMinimum === "boolean") {
|
|
82
|
+
if (schema.exclusiveMinimum === true &&
|
|
83
|
+
typeof schema.minimum === "number") {
|
|
84
|
+
schema.exclusiveMinimum = schema.minimum;
|
|
85
|
+
delete schema.minimum;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
delete schema.exclusiveMinimum;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (typeof schema.exclusiveMaximum === "boolean") {
|
|
92
|
+
if (schema.exclusiveMaximum === true &&
|
|
93
|
+
typeof schema.maximum === "number") {
|
|
94
|
+
schema.exclusiveMaximum = schema.maximum;
|
|
95
|
+
delete schema.maximum;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
delete schema.exclusiveMaximum;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Strip `maximum` values that exceed int32 range — Vertex's protobuf
|
|
102
|
+
// serializer treats `type: "integer"` as int32 and rejects bounds beyond
|
|
103
|
+
// 2^31. zod's `.positive().int()` emits Number.MAX_SAFE_INTEGER as the
|
|
104
|
+
// upper bound (8.9e15), which trips this. The constraint is informational
|
|
105
|
+
// for the model anyway, so dropping it is safe.
|
|
106
|
+
const INT32_MAX = 2147483647;
|
|
107
|
+
if (typeof schema.maximum === "number" && schema.maximum > INT32_MAX) {
|
|
108
|
+
delete schema.maximum;
|
|
109
|
+
}
|
|
110
|
+
if (typeof schema.minimum === "number" && schema.minimum < -INT32_MAX) {
|
|
111
|
+
delete schema.minimum;
|
|
112
|
+
}
|
|
113
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
114
|
+
for (const child of Object.values(schema.properties)) {
|
|
115
|
+
if (child && typeof child === "object") {
|
|
116
|
+
stripAdditionalPropertiesDeep(child);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (schema.items && typeof schema.items === "object") {
|
|
121
|
+
if (Array.isArray(schema.items)) {
|
|
122
|
+
for (const item of schema.items) {
|
|
123
|
+
if (item && typeof item === "object") {
|
|
124
|
+
stripAdditionalPropertiesDeep(item);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
stripAdditionalPropertiesDeep(schema.items);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const key of ["allOf", "anyOf", "oneOf"]) {
|
|
133
|
+
if (Array.isArray(schema[key])) {
|
|
134
|
+
for (const branch of schema[key]) {
|
|
135
|
+
if (branch && typeof branch === "object") {
|
|
136
|
+
stripAdditionalPropertiesDeep(branch);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
38
142
|
// Configuration helpers - now using consolidated utility
|
|
39
143
|
const getVertexProjectId = () => {
|
|
40
144
|
return validateApiKey(createVertexProjectConfig());
|
|
@@ -604,7 +708,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
604
708
|
// the test:tracing observability harness sees the same span hierarchy
|
|
605
709
|
// it sees for AI Studio. BaseProvider.stream does NOT emit this span
|
|
606
710
|
// for any provider — each native provider has to add it itself.
|
|
607
|
-
return
|
|
711
|
+
return withClientStreamSpan({
|
|
608
712
|
name: "neurolink.provider.stream",
|
|
609
713
|
tracer: tracers.provider,
|
|
610
714
|
attributes: {
|
|
@@ -671,7 +775,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
671
775
|
this.emitStreamEnd(modelName, streamStartTime, false, error);
|
|
672
776
|
throw error;
|
|
673
777
|
}
|
|
674
|
-
});
|
|
778
|
+
}, (r) => r.stream, (r, wrapped) => ({ ...r, stream: wrapped }));
|
|
675
779
|
}
|
|
676
780
|
/**
|
|
677
781
|
* Emit `stream:end` so the Pipeline B observability listener creates a
|
|
@@ -746,8 +850,11 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
746
850
|
});
|
|
747
851
|
// Build contents from input with multimodal support
|
|
748
852
|
const contents = [];
|
|
749
|
-
// Build user message parts - start with text
|
|
750
|
-
|
|
853
|
+
// Build user message parts - start with text.
|
|
854
|
+
// `options.input.text` is `string | undefined` in strict mode; the
|
|
855
|
+
// VertexNativePart `text` field requires `string`, so coerce to "" if
|
|
856
|
+
// unset (the multimodal-only path still appends other parts below).
|
|
857
|
+
const userParts = [{ text: options.input.text ?? "" }];
|
|
751
858
|
// Add PDF files as inlineData parts if present
|
|
752
859
|
// Cast input to access multimodal properties that may exist at runtime
|
|
753
860
|
const multimodalInput = options.input;
|
|
@@ -886,6 +993,12 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
886
993
|
// ensureNestedSchemaTypes recursively adds missing type fields to tool schemas
|
|
887
994
|
// Note: convertZodToJsonSchema now uses openApi3 target which produces nullable: true
|
|
888
995
|
const typedSchema = ensureNestedSchemaTypes(inlinedSchema);
|
|
996
|
+
// Strip `additionalProperties` recursively — Vertex Gemini's
|
|
997
|
+
// function-call validator rejects it on object schemas (returns
|
|
998
|
+
// 400 INVALID_ARGUMENT) even though it's valid OpenAPI 3. The
|
|
999
|
+
// field has no semantic meaning to the model, so dropping it
|
|
1000
|
+
// before send is safe for every caller.
|
|
1001
|
+
stripAdditionalPropertiesDeep(typedSchema);
|
|
889
1002
|
decl.parametersJsonSchema = typedSchema;
|
|
890
1003
|
}
|
|
891
1004
|
functionDeclarations.push(decl);
|
|
@@ -1426,6 +1539,12 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1426
1539
|
// ensureNestedSchemaTypes recursively adds missing type fields to tool schemas
|
|
1427
1540
|
// Note: convertZodToJsonSchema now uses openApi3 target which produces nullable: true
|
|
1428
1541
|
const typedSchema = ensureNestedSchemaTypes(inlinedSchema);
|
|
1542
|
+
// Strip `additionalProperties` recursively — Vertex Gemini's
|
|
1543
|
+
// function-call validator rejects it on object schemas (returns
|
|
1544
|
+
// 400 INVALID_ARGUMENT) even though it's valid OpenAPI 3. The
|
|
1545
|
+
// field has no semantic meaning to the model, so dropping it
|
|
1546
|
+
// before send is safe for every caller.
|
|
1547
|
+
stripAdditionalPropertiesDeep(typedSchema);
|
|
1429
1548
|
decl.parametersJsonSchema = typedSchema;
|
|
1430
1549
|
}
|
|
1431
1550
|
functionDeclarations.push(decl);
|
|
@@ -2736,6 +2855,17 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2736
2855
|
this.emitGenerationEnd(modelName, videoResult, generateStartTime, true);
|
|
2737
2856
|
return videoResult;
|
|
2738
2857
|
}
|
|
2858
|
+
// TTS direct-synthesis mode: when caller passes `tts.enabled` without
|
|
2859
|
+
// `tts.useAiResponse`, route to the shared `handleDirectTTSSynthesis`
|
|
2860
|
+
// (synthesise the input text directly; no LLM call). BaseProvider's
|
|
2861
|
+
// standard generate() does the same dispatch — we replicate it here
|
|
2862
|
+
// because Vertex's override bypasses that path.
|
|
2863
|
+
if (options.tts?.enabled && !options.tts?.useAiResponse) {
|
|
2864
|
+
logger.info("[GoogleVertex] Routing TTS direct-synthesis to handleDirectTTSSynthesis", { model: modelName });
|
|
2865
|
+
const ttsResult = await this.handleDirectTTSSynthesis(options, generateStartTime);
|
|
2866
|
+
this.emitGenerationEnd(modelName, ttsResult, generateStartTime, true);
|
|
2867
|
+
return ttsResult;
|
|
2868
|
+
}
|
|
2739
2869
|
// Check if this is an image generation model - route to executeImageGeneration without tools
|
|
2740
2870
|
const isImageModel = IMAGE_GENERATION_MODELS.some((m) => modelName.toLowerCase().startsWith(m.toLowerCase()));
|
|
2741
2871
|
if (isImageModel) {
|
|
@@ -2796,23 +2926,41 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2796
2926
|
"";
|
|
2797
2927
|
try {
|
|
2798
2928
|
let result;
|
|
2799
|
-
//
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2929
|
+
// Wrap the actual native generate call in `neurolink.executeGeneration`
|
|
2930
|
+
// so the observability span chain (tested by
|
|
2931
|
+
// "Tracing: Generate Span Chain") sees a third inner span on the
|
|
2932
|
+
// native @google/genai / @anthropic-ai/vertex-sdk path — Pipeline A
|
|
2933
|
+
// gets this for free from GenerationHandler.executeGeneration.
|
|
2934
|
+
result = await withSpan({
|
|
2935
|
+
name: "neurolink.executeGeneration",
|
|
2936
|
+
tracer: tracers.provider,
|
|
2937
|
+
attributes: {
|
|
2938
|
+
[ATTR.GEN_AI_SYSTEM]: this.providerName,
|
|
2939
|
+
[ATTR.GEN_AI_MODEL]: modelName,
|
|
2940
|
+
"neurolink.path": isAnthropicModel(modelName)
|
|
2941
|
+
? "native.anthropic"
|
|
2942
|
+
: "native.google-genai",
|
|
2943
|
+
},
|
|
2944
|
+
}, async () => {
|
|
2945
|
+
if (isAnthropicModel(modelName)) {
|
|
2946
|
+
logger.info("[GoogleVertex] Routing Claude generate to native @anthropic-ai/vertex-sdk", {
|
|
2947
|
+
model: modelName,
|
|
2948
|
+
totalToolCount: Object.keys(mergedOptions.tools).length,
|
|
2949
|
+
});
|
|
2950
|
+
return this.executeNativeAnthropicGenerate(mergedOptions);
|
|
2951
|
+
}
|
|
2809
2952
|
logger.info("[GoogleVertex] Routing Gemini generate to native @google/genai", {
|
|
2810
2953
|
model: modelName,
|
|
2811
2954
|
totalToolCount: Object.keys(mergedOptions.tools).length,
|
|
2812
2955
|
});
|
|
2813
|
-
|
|
2814
|
-
}
|
|
2956
|
+
return this.executeNativeGemini3Generate(mergedOptions);
|
|
2957
|
+
});
|
|
2815
2958
|
this.attachUsageAndCostAttributes(generateSpan, modelName, result?.usage);
|
|
2959
|
+
// Pipe through TTS-of-AI-response when caller asks for it. The
|
|
2960
|
+
// shared `synthesizeAIResponseIfNeeded` no-ops when tts is not
|
|
2961
|
+
// enabled / useAiResponse is false, so the cost is zero on
|
|
2962
|
+
// non-TTS paths.
|
|
2963
|
+
result = await this.synthesizeAIResponseIfNeeded(result, options);
|
|
2816
2964
|
// Fire onFinish lifecycle callback for the native generate path.
|
|
2817
2965
|
// Pipeline A providers get this for free via the AI SDK middleware
|
|
2818
2966
|
// wrapper (LifecycleMiddleware); native @google/genai bypasses
|
|
@@ -3052,6 +3200,14 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3052
3200
|
? { error: error instanceof Error ? error.message : String(error) }
|
|
3053
3201
|
: {}),
|
|
3054
3202
|
});
|
|
3203
|
+
// Mark on the result so the SDK-level runStandardGenerateRequest knows
|
|
3204
|
+
// this provider already emitted `generation:end` itself and skips its
|
|
3205
|
+
// own duplicate emission. Without this flag the public event listener
|
|
3206
|
+
// (and the observability test) would see two events per generate call.
|
|
3207
|
+
if (result && typeof result === "object") {
|
|
3208
|
+
result._generationEndEmitted =
|
|
3209
|
+
true;
|
|
3210
|
+
}
|
|
3055
3211
|
}
|
|
3056
3212
|
formatProviderError(error) {
|
|
3057
3213
|
const errorRecord = error;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type LanguageModel } from "ai";
|
|
2
|
+
import type { AIProviderName } from "../constants/enums.js";
|
|
3
|
+
import { BaseProvider } from "../core/baseProvider.js";
|
|
4
|
+
import type { NeurolinkCredentials, StreamOptions, StreamResult, ValidationSchema } from "../types/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* Groq Provider
|
|
7
|
+
*
|
|
8
|
+
* Sub-100ms inference of Llama / Mistral / Gemma at api.groq.com/openai/v1
|
|
9
|
+
* (OpenAI-compatible). Best for low-latency tier; trade-off vs other open
|
|
10
|
+
* model hosts is throughput latency, not quality.
|
|
11
|
+
*
|
|
12
|
+
* @see https://console.groq.com/docs/quickstart
|
|
13
|
+
*/
|
|
14
|
+
export declare class GroqProvider extends BaseProvider {
|
|
15
|
+
private model;
|
|
16
|
+
private apiKey;
|
|
17
|
+
private baseURL;
|
|
18
|
+
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["groq"]);
|
|
19
|
+
protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
|
20
|
+
private executeStreamInner;
|
|
21
|
+
protected getProviderName(): AIProviderName;
|
|
22
|
+
protected getDefaultModel(): string;
|
|
23
|
+
protected getAISDKModel(): LanguageModel;
|
|
24
|
+
protected formatProviderError(error: unknown): Error;
|
|
25
|
+
validateConfiguration(): Promise<boolean>;
|
|
26
|
+
getConfiguration(): {
|
|
27
|
+
provider: AIProviderName;
|
|
28
|
+
model: string;
|
|
29
|
+
defaultModel: string;
|
|
30
|
+
baseURL: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export default GroqProvider;
|