@juspay/neurolink 9.63.1 → 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 +12 -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 +42 -11
- 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 +573 -554
- package/dist/cli/commands/mcp.js +29 -0
- package/dist/cli/commands/proxy.js +24 -5
- package/dist/cli/factories/commandFactory.d.ts +25 -1
- package/dist/cli/factories/commandFactory.js +341 -63
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/loop/optionsSchema.js +12 -0
- 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 +23 -3
- 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/modules/MessageBuilder.js +20 -0
- 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 +203 -2
- 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 +42 -11
- 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 +23 -3
- 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/modules/MessageBuilder.js +20 -0
- 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 +203 -2
- 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/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/lib/memory/hippocampusInitializer.js +32 -2
- package/dist/lib/middleware/builtin/lifecycle.js +52 -51
- 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 +342 -49
- 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.d.ts +11 -3
- package/dist/lib/providers/googleAiStudio.js +336 -344
- package/dist/lib/providers/googleNativeGemini3.d.ts +107 -2
- package/dist/lib/providers/googleNativeGemini3.js +381 -25
- package/dist/lib/providers/googleVertex.d.ts +116 -129
- package/dist/lib/providers/googleVertex.js +3002 -1988
- 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 +38 -22
- 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/aliases.d.ts +14 -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/common.d.ts +0 -3
- package/dist/lib/types/conversation.d.ts +10 -3
- package/dist/lib/types/generate.d.ts +76 -5
- package/dist/lib/types/index.d.ts +6 -0
- package/dist/lib/types/index.js +8 -0
- package/dist/lib/types/memory.d.ts +96 -0
- package/dist/lib/types/memory.js +23 -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 +284 -3
- 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 +8 -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 +56 -0
- package/dist/lib/utils/lifecycleCallbacks.js +100 -0
- 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.d.ts +10 -0
- package/dist/lib/utils/messageBuilder.js +83 -30
- package/dist/lib/utils/modelChoices.js +236 -3
- package/dist/lib/utils/modelDetection.d.ts +11 -0
- package/dist/lib/utils/modelDetection.js +27 -0
- 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/providerHealth.js +7 -7
- package/dist/lib/utils/safeFetch.d.ts +26 -0
- package/dist/lib/utils/safeFetch.js +83 -0
- package/dist/lib/utils/schemaConversion.d.ts +1 -1
- package/dist/lib/utils/schemaConversion.js +59 -4
- 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/tokenLimits.js +23 -32
- 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/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/memory/hippocampusInitializer.js +32 -2
- package/dist/middleware/builtin/lifecycle.js +52 -51
- 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 +342 -49
- 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.d.ts +11 -3
- package/dist/providers/googleAiStudio.js +335 -344
- package/dist/providers/googleNativeGemini3.d.ts +107 -2
- package/dist/providers/googleNativeGemini3.js +381 -25
- package/dist/providers/googleVertex.d.ts +116 -129
- package/dist/providers/googleVertex.js +3000 -1987
- 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 +38 -22
- 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/aliases.d.ts +14 -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/common.d.ts +0 -3
- package/dist/types/conversation.d.ts +10 -3
- package/dist/types/generate.d.ts +76 -5
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.js +8 -0
- package/dist/types/memory.d.ts +96 -0
- package/dist/types/memory.js +22 -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 +284 -3
- 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 +8 -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 +56 -0
- package/dist/utils/lifecycleCallbacks.js +99 -0
- 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.d.ts +10 -0
- package/dist/utils/messageBuilder.js +83 -30
- package/dist/utils/modelChoices.js +236 -3
- package/dist/utils/modelDetection.d.ts +11 -0
- package/dist/utils/modelDetection.js +27 -0
- 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/providerHealth.js +7 -7
- package/dist/utils/safeFetch.d.ts +26 -0
- package/dist/utils/safeFetch.js +82 -0
- package/dist/utils/schemaConversion.d.ts +1 -1
- package/dist/utils/schemaConversion.js +59 -4
- 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/tokenLimits.js +23 -32
- 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 +42 -8
|
@@ -9,12 +9,69 @@
|
|
|
9
9
|
* providers so they can share a single implementation.
|
|
10
10
|
*/
|
|
11
11
|
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { extname } from "node:path";
|
|
12
14
|
import { jsonSchema as aiJsonSchema, tool as createAISDKTool, } from "ai";
|
|
13
15
|
import { DEFAULT_MAX_STEPS, DEFAULT_TOOL_MAX_RETRIES, } from "../core/constants.js";
|
|
14
16
|
import { logger } from "../utils/logger.js";
|
|
15
|
-
import { convertZodToJsonSchema, inlineJsonSchema, isZodSchema, normalizeJsonSchemaObject, } from "../utils/schemaConversion.js";
|
|
17
|
+
import { convertZodToJsonSchema, ensureNestedSchemaTypes, inlineJsonSchema, isZodSchema, normalizeJsonSchemaObject, } from "../utils/schemaConversion.js";
|
|
16
18
|
import { createNativeThinkingConfig } from "../utils/thinkingConfig.js";
|
|
17
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
|
+
}
|
|
18
75
|
/**
|
|
19
76
|
* Sanitize a JSON Schema for Gemini's proto-based API.
|
|
20
77
|
*
|
|
@@ -100,6 +157,40 @@ export function sanitizeSchemaForGemini(schema) {
|
|
|
100
157
|
result[branch] = sanitizeSchemaForGemini(result[branch]);
|
|
101
158
|
}
|
|
102
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
|
+
}
|
|
103
194
|
return result;
|
|
104
195
|
}
|
|
105
196
|
/**
|
|
@@ -115,8 +206,27 @@ export function sanitizeSchemaForGemini(schema) {
|
|
|
115
206
|
export function sanitizeToolsForGemini(tools) {
|
|
116
207
|
const sanitized = {};
|
|
117
208
|
const dropped = [];
|
|
209
|
+
const renamed = [];
|
|
210
|
+
const originalNameMap = new Map();
|
|
118
211
|
for (const [name, tool] of Object.entries(tools)) {
|
|
119
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
|
+
}
|
|
120
230
|
// Access the legacy `parameters` field that may exist on older AI SDK tools.
|
|
121
231
|
// AI SDK v6 uses `inputSchema`, but v3/v4 tools and third-party wrappers use `parameters`.
|
|
122
232
|
const legacyTool = tool;
|
|
@@ -125,15 +235,15 @@ export function sanitizeToolsForGemini(tools) {
|
|
|
125
235
|
typeof params === "object" &&
|
|
126
236
|
"_def" in params &&
|
|
127
237
|
typeof params.parse === "function") {
|
|
128
|
-
const rawJsonSchema = convertZodToJsonSchema(params);
|
|
238
|
+
const rawJsonSchema = convertZodToJsonSchema(params, "openApi3");
|
|
129
239
|
const inlined = inlineJsonSchema(rawJsonSchema);
|
|
130
240
|
// Gemini sanitization strips Zod-only features not supported by the Gemini API:
|
|
131
241
|
// union types (anyOf/oneOf) are collapsed to string, default values and
|
|
132
242
|
// additionalProperties are removed. The resulting schema is Gemini-compatible
|
|
133
243
|
// but loses some type constraints from the original Zod schema.
|
|
134
244
|
const sanitizedSchema = sanitizeSchemaForGemini(inlined);
|
|
135
|
-
sanitized[
|
|
136
|
-
description: tool.description || `Tool: ${
|
|
245
|
+
sanitized[safeName] = createAISDKTool({
|
|
246
|
+
description: tool.description || `Tool: ${safeName}`,
|
|
137
247
|
inputSchema: aiJsonSchema(sanitizedSchema),
|
|
138
248
|
execute: tool.execute,
|
|
139
249
|
});
|
|
@@ -145,14 +255,14 @@ export function sanitizeToolsForGemini(tools) {
|
|
|
145
255
|
const rawSchema = params
|
|
146
256
|
.jsonSchema;
|
|
147
257
|
const sanitizedSchema = sanitizeSchemaForGemini(inlineJsonSchema(rawSchema));
|
|
148
|
-
sanitized[
|
|
149
|
-
description: tool.description || `Tool: ${
|
|
258
|
+
sanitized[safeName] = createAISDKTool({
|
|
259
|
+
description: tool.description || `Tool: ${safeName}`,
|
|
150
260
|
inputSchema: aiJsonSchema(sanitizedSchema),
|
|
151
261
|
execute: tool.execute,
|
|
152
262
|
});
|
|
153
263
|
}
|
|
154
264
|
else {
|
|
155
|
-
sanitized[
|
|
265
|
+
sanitized[safeName] = tool;
|
|
156
266
|
}
|
|
157
267
|
}
|
|
158
268
|
catch (error) {
|
|
@@ -161,7 +271,12 @@ export function sanitizeToolsForGemini(tools) {
|
|
|
161
271
|
dropped.push(name);
|
|
162
272
|
}
|
|
163
273
|
}
|
|
164
|
-
|
|
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 };
|
|
165
280
|
}
|
|
166
281
|
export function normalizeToolsForJsonSchemaProvider(tools) {
|
|
167
282
|
const normalizedTools = {};
|
|
@@ -171,7 +286,7 @@ export function normalizeToolsForJsonSchemaProvider(tools) {
|
|
|
171
286
|
const toolParams = legacyTool.parameters || tool.inputSchema;
|
|
172
287
|
let rawSchema;
|
|
173
288
|
if (isZodSchema(toolParams)) {
|
|
174
|
-
rawSchema = convertZodToJsonSchema(toolParams);
|
|
289
|
+
rawSchema = convertZodToJsonSchema(toolParams, "openApi3");
|
|
175
290
|
}
|
|
176
291
|
else if (toolParams && typeof toolParams === "object") {
|
|
177
292
|
rawSchema = toolParams;
|
|
@@ -210,11 +325,29 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
210
325
|
const functionDeclarations = [];
|
|
211
326
|
const executeMap = new Map();
|
|
212
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();
|
|
213
340
|
for (const [name, tool] of Object.entries(tools)) {
|
|
214
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
|
+
}
|
|
215
348
|
const decl = {
|
|
216
|
-
name,
|
|
217
|
-
description: tool.description || `Tool: ${
|
|
349
|
+
name: safeName,
|
|
350
|
+
description: tool.description || `Tool: ${safeName}`,
|
|
218
351
|
};
|
|
219
352
|
// Access legacy `parameters` (AI SDK v3/v4) or current `inputSchema` (v6)
|
|
220
353
|
const legacyTool = tool;
|
|
@@ -222,7 +355,7 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
222
355
|
let rawSchema;
|
|
223
356
|
const toolParams = legacyTool.parameters || tool.inputSchema;
|
|
224
357
|
if (isZodSchema(toolParams)) {
|
|
225
|
-
rawSchema = convertZodToJsonSchema(toolParams);
|
|
358
|
+
rawSchema = convertZodToJsonSchema(toolParams, "openApi3");
|
|
226
359
|
}
|
|
227
360
|
else if (typeof toolParams === "object") {
|
|
228
361
|
rawSchema = toolParams;
|
|
@@ -239,8 +372,9 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
239
372
|
decl.parametersJsonSchema = sanitizeSchemaForGemini(inlineJsonSchema(rawSchema));
|
|
240
373
|
}
|
|
241
374
|
functionDeclarations.push(decl);
|
|
375
|
+
usedNames.add(safeName);
|
|
242
376
|
if (tool.execute) {
|
|
243
|
-
executeMap.set(name, tool.execute);
|
|
377
|
+
executeMap.set(decl.name, tool.execute);
|
|
244
378
|
}
|
|
245
379
|
}
|
|
246
380
|
catch (err) {
|
|
@@ -251,10 +385,27 @@ export function buildNativeToolDeclarations(tools) {
|
|
|
251
385
|
if (skippedTools.length > 0) {
|
|
252
386
|
logger.warn(`[buildNativeToolDeclarations] ${skippedTools.length} tool(s) skipped due to schema errors: ${skippedTools.join(", ")}`);
|
|
253
387
|
}
|
|
254
|
-
|
|
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
|
+
};
|
|
255
398
|
}
|
|
256
399
|
/**
|
|
257
400
|
* Build the native @google/genai config object shared by stream and generate.
|
|
401
|
+
*
|
|
402
|
+
* Caller is responsible for the tools-vs-JSON conflict resolution: Gemini's
|
|
403
|
+
* function calling cannot be combined with `responseMimeType:
|
|
404
|
+
* "application/json"`, and `responseSchema` requires that mime type. So
|
|
405
|
+
* when tools are active, callers must NOT pass `wantsJsonOutput`/
|
|
406
|
+
* `responseSchema` here; when JSON/schema output is requested, callers
|
|
407
|
+
* must omit `toolsConfig`. The AI Studio path enforces this by forcing
|
|
408
|
+
* `disableTools: true` whenever JSON/schema output is requested.
|
|
258
409
|
*/
|
|
259
410
|
export function buildNativeConfig(options, toolsConfig) {
|
|
260
411
|
const config = {
|
|
@@ -272,6 +423,16 @@ export function buildNativeConfig(options, toolsConfig) {
|
|
|
272
423
|
if (nativeThinkingConfig) {
|
|
273
424
|
config.thinkingConfig = nativeThinkingConfig;
|
|
274
425
|
}
|
|
426
|
+
// Native JSON / schema enforcement. Only set when tools are NOT being sent
|
|
427
|
+
// (Gemini rejects the combination). responseSchema implies JSON mime type.
|
|
428
|
+
if (!toolsConfig) {
|
|
429
|
+
if (options.responseSchema || options.wantsJsonOutput) {
|
|
430
|
+
config.responseMimeType = "application/json";
|
|
431
|
+
}
|
|
432
|
+
if (options.responseSchema) {
|
|
433
|
+
config.responseSchema = options.responseSchema;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
275
436
|
return config;
|
|
276
437
|
}
|
|
277
438
|
/**
|
|
@@ -473,27 +634,38 @@ export function extractTextFromParts(rawResponseParts) {
|
|
|
473
634
|
* @param executeMap - Map of tool name to execute function
|
|
474
635
|
* @param failedTools - Mutable map tracking per-tool failure counts
|
|
475
636
|
* @param allToolCalls - Mutable array accumulating all tool call records
|
|
476
|
-
* @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.
|
|
477
641
|
* @returns Array of function responses for conversation history
|
|
478
642
|
*/
|
|
479
643
|
export async function executeNativeToolCalls(logLabel, stepFunctionCalls, executeMap, failedTools, allToolCalls, options) {
|
|
480
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;
|
|
481
649
|
for (const call of stepFunctionCalls) {
|
|
482
|
-
|
|
650
|
+
const exposedName = externalName(call.name);
|
|
651
|
+
allToolCalls.push({ toolName: exposedName, args: call.args });
|
|
483
652
|
// Check if this tool has already exceeded retry limit
|
|
484
653
|
const failedInfo = failedTools.get(call.name);
|
|
485
654
|
if (failedInfo && failedInfo.count >= DEFAULT_TOOL_MAX_RETRIES) {
|
|
486
|
-
logger.warn(`${logLabel} Tool "${
|
|
655
|
+
logger.warn(`${logLabel} Tool "${exposedName}" has exceeded retry limit (${DEFAULT_TOOL_MAX_RETRIES}), skipping execution`);
|
|
487
656
|
const errorOutput = {
|
|
488
|
-
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.`,
|
|
489
658
|
status: "permanently_failed",
|
|
490
659
|
do_not_retry: true,
|
|
491
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.
|
|
492
664
|
functionResponses.push({
|
|
493
665
|
functionResponse: { name: call.name, response: errorOutput },
|
|
494
666
|
});
|
|
495
667
|
options?.toolExecutions?.push({
|
|
496
|
-
name:
|
|
668
|
+
name: exposedName,
|
|
497
669
|
input: call.args,
|
|
498
670
|
output: errorOutput,
|
|
499
671
|
});
|
|
@@ -514,7 +686,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
514
686
|
functionResponse: { name: call.name, response: { result } },
|
|
515
687
|
});
|
|
516
688
|
options?.toolExecutions?.push({
|
|
517
|
-
name:
|
|
689
|
+
name: exposedName,
|
|
518
690
|
input: call.args,
|
|
519
691
|
output: result,
|
|
520
692
|
});
|
|
@@ -529,12 +701,12 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
529
701
|
currentFailInfo.count++;
|
|
530
702
|
currentFailInfo.lastError = errorMessage;
|
|
531
703
|
failedTools.set(call.name, currentFailInfo);
|
|
532
|
-
logger.warn(`${logLabel} Tool "${
|
|
704
|
+
logger.warn(`${logLabel} Tool "${exposedName}" failed (attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}): ${errorMessage}`);
|
|
533
705
|
// Determine if this is a permanent failure
|
|
534
706
|
const isPermanentFailure = currentFailInfo.count >= DEFAULT_TOOL_MAX_RETRIES;
|
|
535
707
|
const errorOutput = {
|
|
536
708
|
error: isPermanentFailure
|
|
537
|
-
? `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.`
|
|
538
710
|
: `TOOL_EXECUTION_ERROR: ${errorMessage}. Retry attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}.`,
|
|
539
711
|
status: isPermanentFailure ? "permanently_failed" : "failed",
|
|
540
712
|
do_not_retry: isPermanentFailure,
|
|
@@ -545,7 +717,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
545
717
|
functionResponse: { name: call.name, response: errorOutput },
|
|
546
718
|
});
|
|
547
719
|
options?.toolExecutions?.push({
|
|
548
|
-
name:
|
|
720
|
+
name: exposedName,
|
|
549
721
|
input: call.args,
|
|
550
722
|
output: errorOutput,
|
|
551
723
|
});
|
|
@@ -554,7 +726,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
554
726
|
else {
|
|
555
727
|
// Tool not found is a permanent error
|
|
556
728
|
const errorOutput = {
|
|
557
|
-
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.`,
|
|
558
730
|
status: "permanently_failed",
|
|
559
731
|
do_not_retry: true,
|
|
560
732
|
};
|
|
@@ -562,7 +734,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
|
|
|
562
734
|
functionResponse: { name: call.name, response: errorOutput },
|
|
563
735
|
});
|
|
564
736
|
options?.toolExecutions?.push({
|
|
565
|
-
name:
|
|
737
|
+
name: exposedName,
|
|
566
738
|
input: call.args,
|
|
567
739
|
output: errorOutput,
|
|
568
740
|
});
|
|
@@ -597,4 +769,188 @@ export function pushModelResponseToHistory(currentContents, rawResponseParts, st
|
|
|
597
769
|
: stepFunctionCalls.map((fc) => ({ functionCall: fc })),
|
|
598
770
|
});
|
|
599
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Convert a Zod schema (or AI SDK `jsonSchema()` wrapper) into the shape
|
|
774
|
+
* `@google/genai` accepts as `responseSchema`. Mirrors the inline pipeline
|
|
775
|
+
* the Vertex Gemini paths already use:
|
|
776
|
+
*
|
|
777
|
+
* convertZodToJsonSchema → inlineJsonSchema → strip `$schema` → ensure
|
|
778
|
+
* every nested schema has a `type` (Vertex/Gemini reject schemas missing
|
|
779
|
+
* that field, even on nested objects).
|
|
780
|
+
*
|
|
781
|
+
* Lives here so the AI Studio and Vertex paths can share the same
|
|
782
|
+
* sanitization without duplicating the schema-conversion churn.
|
|
783
|
+
*/
|
|
784
|
+
export function buildGeminiResponseSchema(schema) {
|
|
785
|
+
const raw = convertZodToJsonSchema(schema, "openApi3");
|
|
786
|
+
const inlined = inlineJsonSchema(raw);
|
|
787
|
+
if (inlined.$schema) {
|
|
788
|
+
delete inlined.$schema;
|
|
789
|
+
}
|
|
790
|
+
return ensureNestedSchemaTypes(inlined);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Map NeuroLink ChatMessage[] history into the @google/genai content format
|
|
794
|
+
* and push the entries onto a contents array.
|
|
795
|
+
*
|
|
796
|
+
* Used by the native Vertex Gemini and Google AI Studio paths to honor
|
|
797
|
+
* `options.conversationMessages` so multi-turn conversations (memory, loop
|
|
798
|
+
* REPL, agent flows) actually carry prior turns into the request.
|
|
799
|
+
*
|
|
800
|
+
* Behavior notes:
|
|
801
|
+
* - Only `user` and `assistant` roles are forwarded; system messages are
|
|
802
|
+
* expected to be wired via `systemInstruction`, and tool-call /
|
|
803
|
+
* tool-result roles only appear inside intra-call tool loops which build
|
|
804
|
+
* their own model/function entries.
|
|
805
|
+
* - String content is wrapped as a single `{ text }` part. Empty strings
|
|
806
|
+
* are skipped to avoid sending empty parts that some Gemini regions
|
|
807
|
+
* reject.
|
|
808
|
+
* - The current user input should be appended AFTER calling this helper
|
|
809
|
+
* so the prior turns appear first in chronological order.
|
|
810
|
+
*/
|
|
811
|
+
export function prependConversationMessages(contents, conversationMessages) {
|
|
812
|
+
if (!conversationMessages || conversationMessages.length === 0) {
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
for (const msg of conversationMessages) {
|
|
816
|
+
if (msg.role !== "user" && msg.role !== "assistant") {
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
820
|
+
if (text.length === 0) {
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
contents.push({
|
|
824
|
+
role: msg.role === "assistant" ? "model" : "user",
|
|
825
|
+
parts: [{ text }],
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Build the `parts` array for the current user turn of a Gemini native
|
|
831
|
+
* `generateContent` request, including inline image + PDF blobs.
|
|
832
|
+
*
|
|
833
|
+
* Both providers that hit the native `@google/genai` SDK — `GoogleVertex`
|
|
834
|
+
* and `GoogleAIStudio` — need this. The previous AI Studio code only
|
|
835
|
+
* pushed a single `{ text }` part, which silently dropped `input.images`
|
|
836
|
+
* and `input.pdfFiles` on the floor: the model received text only and
|
|
837
|
+
* legitimately reported "no image attached". Extracting this from the
|
|
838
|
+
* Vertex copy keeps both providers on one definition.
|
|
839
|
+
*
|
|
840
|
+
* Accepted shapes per element (mirroring the runtime behaviour the Vertex
|
|
841
|
+
* code already supported):
|
|
842
|
+
* - `Buffer` → used as-is
|
|
843
|
+
* - local file path → read via `readFileSync`, MIME guessed from extension
|
|
844
|
+
* - `data:<mime>;base64,...` URL → mime parsed, data base64-decoded
|
|
845
|
+
* - `http(s)://...` URL → fetched, mime from `content-type`
|
|
846
|
+
* - any other string → assumed to be a base64-encoded payload
|
|
847
|
+
*
|
|
848
|
+
* Image MIME guessing is conservative — only known extensions override the
|
|
849
|
+
* default `image/jpeg`. Fetch failures are logged and the offending entry
|
|
850
|
+
* is skipped rather than aborting the entire request, matching prior
|
|
851
|
+
* Vertex behaviour.
|
|
852
|
+
*/
|
|
853
|
+
export async function buildUserPartsWithMultimodal(input, textOverride, logPrefix = "[GeminiNative]") {
|
|
854
|
+
const text = typeof textOverride === "string" ? textOverride : (input?.text ?? "");
|
|
855
|
+
const parts = [{ text }];
|
|
856
|
+
if (input?.pdfFiles && input.pdfFiles.length > 0) {
|
|
857
|
+
logger.debug(`${logPrefix} Processing ${input.pdfFiles.length} PDF(s)`);
|
|
858
|
+
for (const pdfFile of input.pdfFiles) {
|
|
859
|
+
let pdfBuffer;
|
|
860
|
+
if (typeof pdfFile === "string") {
|
|
861
|
+
if (existsSync(pdfFile)) {
|
|
862
|
+
pdfBuffer = readFileSync(pdfFile);
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
// Treat as already-base64-encoded payload
|
|
866
|
+
pdfBuffer = Buffer.from(pdfFile, "base64");
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
pdfBuffer = pdfFile;
|
|
871
|
+
}
|
|
872
|
+
parts.push({
|
|
873
|
+
inlineData: {
|
|
874
|
+
mimeType: "application/pdf",
|
|
875
|
+
data: pdfBuffer.toString("base64"),
|
|
876
|
+
},
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (input?.images && input.images.length > 0) {
|
|
881
|
+
logger.debug(`${logPrefix} Processing ${input.images.length} image(s)`);
|
|
882
|
+
for (const rawImage of input.images) {
|
|
883
|
+
// `images` may carry plain Buffer/string values or `{ data, altText? }`
|
|
884
|
+
// objects. Normalise to the inner payload before format detection.
|
|
885
|
+
const image = rawImage && typeof rawImage === "object" && !Buffer.isBuffer(rawImage)
|
|
886
|
+
? rawImage.data
|
|
887
|
+
: rawImage;
|
|
888
|
+
let imageBuffer;
|
|
889
|
+
let mimeType = "image/jpeg";
|
|
890
|
+
if (typeof image === "string") {
|
|
891
|
+
if (existsSync(image)) {
|
|
892
|
+
imageBuffer = readFileSync(image);
|
|
893
|
+
const ext = extname(image).toLowerCase();
|
|
894
|
+
if (ext === ".png") {
|
|
895
|
+
mimeType = "image/png";
|
|
896
|
+
}
|
|
897
|
+
else if (ext === ".gif") {
|
|
898
|
+
mimeType = "image/gif";
|
|
899
|
+
}
|
|
900
|
+
else if (ext === ".webp") {
|
|
901
|
+
mimeType = "image/webp";
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
else if (image.startsWith("data:")) {
|
|
905
|
+
const matches = image.match(/^data:([^;]+);base64,(.+)$/);
|
|
906
|
+
if (matches) {
|
|
907
|
+
mimeType = matches[1];
|
|
908
|
+
imageBuffer = Buffer.from(matches[2], "base64");
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
else if (image.startsWith("http://") ||
|
|
915
|
+
image.startsWith("https://")) {
|
|
916
|
+
try {
|
|
917
|
+
const response = await fetch(image);
|
|
918
|
+
if (!response.ok) {
|
|
919
|
+
logger.warn(`${logPrefix} Image fetch failed: ${response.status} ${response.statusText}, skipping`, { url: image });
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
923
|
+
imageBuffer = Buffer.from(arrayBuffer);
|
|
924
|
+
const headerMime = response.headers.get("content-type");
|
|
925
|
+
if (headerMime && headerMime.startsWith("image/")) {
|
|
926
|
+
mimeType = headerMime.split(";")[0];
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
catch (fetchError) {
|
|
930
|
+
logger.warn(`${logPrefix} Image URL fetch threw, skipping: ${fetchError instanceof Error
|
|
931
|
+
? fetchError.message
|
|
932
|
+
: String(fetchError)}`, { url: image });
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
imageBuffer = Buffer.from(image, "base64");
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
else {
|
|
941
|
+
imageBuffer = image;
|
|
942
|
+
}
|
|
943
|
+
if (!imageBuffer) {
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
parts.push({
|
|
947
|
+
inlineData: {
|
|
948
|
+
mimeType,
|
|
949
|
+
data: imageBuffer.toString("base64"),
|
|
950
|
+
},
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return parts;
|
|
955
|
+
}
|
|
600
956
|
//# sourceMappingURL=googleNativeGemini3.js.map
|