@juspay/neurolink 9.59.4 → 9.59.6
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/dist/browser/neurolink.min.js +3 -3
- package/dist/core/baseProvider.js +13 -0
- package/dist/lib/core/baseProvider.js +13 -0
- package/dist/lib/neurolink.js +1 -1
- package/dist/lib/providers/googleVertex.d.ts +8 -1
- package/dist/lib/providers/googleVertex.js +42 -6
- package/dist/neurolink.js +1 -1
- package/dist/providers/googleVertex.d.ts +8 -1
- package/dist/providers/googleVertex.js +42 -6
- package/package.json +1 -1
|
@@ -570,6 +570,19 @@ export class BaseProvider {
|
|
|
570
570
|
if (!hasVideoFrames(messages)) {
|
|
571
571
|
return null;
|
|
572
572
|
}
|
|
573
|
+
// Bug 2 fix: callers requesting structured output (schema or explicit
|
|
574
|
+
// output.format) must NOT be hijacked into the prose-returning video
|
|
575
|
+
// analysis path. Without this gate, schema/format are silently dropped
|
|
576
|
+
// whenever messages contain >=3 image parts.
|
|
577
|
+
if (options.schema !== undefined || options.output?.format !== undefined) {
|
|
578
|
+
logger.info("[VideoFrameGen] Skipping video-frame analysis route; caller requested structured output", {
|
|
579
|
+
provider: this.providerName,
|
|
580
|
+
model: this.modelName,
|
|
581
|
+
hasSchema: options.schema !== undefined,
|
|
582
|
+
outputFormat: options.output?.format,
|
|
583
|
+
});
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
573
586
|
const videoAnalysisResult = await executeVideoAnalysis(messages, {
|
|
574
587
|
provider: options.provider,
|
|
575
588
|
providerName: this.providerName,
|
|
@@ -570,6 +570,19 @@ export class BaseProvider {
|
|
|
570
570
|
if (!hasVideoFrames(messages)) {
|
|
571
571
|
return null;
|
|
572
572
|
}
|
|
573
|
+
// Bug 2 fix: callers requesting structured output (schema or explicit
|
|
574
|
+
// output.format) must NOT be hijacked into the prose-returning video
|
|
575
|
+
// analysis path. Without this gate, schema/format are silently dropped
|
|
576
|
+
// whenever messages contain >=3 image parts.
|
|
577
|
+
if (options.schema !== undefined || options.output?.format !== undefined) {
|
|
578
|
+
logger.info("[VideoFrameGen] Skipping video-frame analysis route; caller requested structured output", {
|
|
579
|
+
provider: this.providerName,
|
|
580
|
+
model: this.modelName,
|
|
581
|
+
hasSchema: options.schema !== undefined,
|
|
582
|
+
outputFormat: options.output?.format,
|
|
583
|
+
});
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
573
586
|
const videoAnalysisResult = await executeVideoAnalysis(messages, {
|
|
574
587
|
provider: options.provider,
|
|
575
588
|
providerName: this.providerName,
|
package/dist/lib/neurolink.js
CHANGED
|
@@ -7255,7 +7255,7 @@ Current user's request: ${currentInput}`;
|
|
|
7255
7255
|
const finalOptions = {
|
|
7256
7256
|
timeout: options?.timeout ??
|
|
7257
7257
|
toolInfo?.tool?.timeoutMs ??
|
|
7258
|
-
TOOL_TIMEOUTS.
|
|
7258
|
+
TOOL_TIMEOUTS.EXECUTION_BATCH_MS,
|
|
7259
7259
|
maxRetries: options?.maxRetries ??
|
|
7260
7260
|
toolInfo?.tool?.maxRetries ??
|
|
7261
7261
|
RETRY_ATTEMPTS.DEFAULT,
|
|
@@ -311,7 +311,14 @@ export declare class GoogleVertexProvider extends BaseProvider {
|
|
|
311
311
|
*/
|
|
312
312
|
private buildImageGenerationParts;
|
|
313
313
|
/**
|
|
314
|
-
* Parse the Vertex AI image generation REST API response
|
|
314
|
+
* Parse the Vertex AI image generation REST API response.
|
|
315
|
+
*
|
|
316
|
+
* Dual-mode image models (gemini-3.1-flash-image-preview, gemini-2.5-flash-image,
|
|
317
|
+
* gemini-3-pro-image-preview) decide per-request whether to emit an image or text.
|
|
318
|
+
* When the response contains text parts but no image part, surface the text via
|
|
319
|
+
* `textFallback` so the caller can return a normal text result instead of throwing
|
|
320
|
+
* "model returned text instead of image data" and burning retries on a query that
|
|
321
|
+
* the model has already answered.
|
|
315
322
|
*/
|
|
316
323
|
private parseImageGenerationResponse;
|
|
317
324
|
/**
|
|
@@ -3015,7 +3015,14 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3015
3015
|
return parts;
|
|
3016
3016
|
}
|
|
3017
3017
|
/**
|
|
3018
|
-
* Parse the Vertex AI image generation REST API response
|
|
3018
|
+
* Parse the Vertex AI image generation REST API response.
|
|
3019
|
+
*
|
|
3020
|
+
* Dual-mode image models (gemini-3.1-flash-image-preview, gemini-2.5-flash-image,
|
|
3021
|
+
* gemini-3-pro-image-preview) decide per-request whether to emit an image or text.
|
|
3022
|
+
* When the response contains text parts but no image part, surface the text via
|
|
3023
|
+
* `textFallback` so the caller can return a normal text result instead of throwing
|
|
3024
|
+
* "model returned text instead of image data" and burning retries on a query that
|
|
3025
|
+
* the model has already answered.
|
|
3019
3026
|
*/
|
|
3020
3027
|
parseImageGenerationResponse(data, imageModelName) {
|
|
3021
3028
|
const candidate = data.candidates?.[0];
|
|
@@ -3030,10 +3037,15 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3030
3037
|
(part.inline_data &&
|
|
3031
3038
|
part.inline_data.mime_type?.startsWith("image/"))));
|
|
3032
3039
|
if (!imagePart) {
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3040
|
+
// Filter out empty/whitespace-only text parts so an effectively empty
|
|
3041
|
+
// response throws "no image data" instead of returning content: "".
|
|
3042
|
+
const textParts = candidate.content.parts
|
|
3043
|
+
.map((part) => (typeof part.text === "string" ? part.text : ""))
|
|
3044
|
+
.filter((text) => text.trim().length > 0);
|
|
3045
|
+
if (textParts.length > 0) {
|
|
3046
|
+
return { textFallback: textParts.join("") };
|
|
3047
|
+
}
|
|
3048
|
+
throw new ProviderError(`Image generation completed but no image data was returned. Model: ${imageModelName}`, this.providerName);
|
|
3037
3049
|
}
|
|
3038
3050
|
const imageData = imagePart.inlineData?.data || imagePart.inline_data?.data;
|
|
3039
3051
|
const mimeType = imagePart.inlineData?.mimeType ||
|
|
@@ -3127,7 +3139,31 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3127
3139
|
throw new ProviderError(`Vertex AI API error (${response.status}): ${errorText}`, this.providerName);
|
|
3128
3140
|
}
|
|
3129
3141
|
const data = (await response.json());
|
|
3130
|
-
const
|
|
3142
|
+
const parsed = this.parseImageGenerationResponse(data, imageModelName);
|
|
3143
|
+
// Dual-mode model decided to emit text instead of an image. Surface the
|
|
3144
|
+
// text as a normal text result instead of throwing — the model already
|
|
3145
|
+
// answered the user; failing here just burns retries.
|
|
3146
|
+
if ("textFallback" in parsed) {
|
|
3147
|
+
logger.info("Dual-mode image model returned text; returning as text result", {
|
|
3148
|
+
model: imageModelName,
|
|
3149
|
+
textLength: parsed.textFallback.length,
|
|
3150
|
+
responseTime: Date.now() - startTime,
|
|
3151
|
+
});
|
|
3152
|
+
const inputTokens = this.estimateTokenCount(prompt);
|
|
3153
|
+
const outputTokens = this.estimateTokenCount(parsed.textFallback);
|
|
3154
|
+
const textResult = {
|
|
3155
|
+
content: parsed.textFallback,
|
|
3156
|
+
provider: this.providerName,
|
|
3157
|
+
model: imageModelName,
|
|
3158
|
+
usage: {
|
|
3159
|
+
input: inputTokens,
|
|
3160
|
+
output: outputTokens,
|
|
3161
|
+
total: inputTokens + outputTokens,
|
|
3162
|
+
},
|
|
3163
|
+
};
|
|
3164
|
+
return await this.enhanceResult(textResult, options, startTime);
|
|
3165
|
+
}
|
|
3166
|
+
const { imageData, mimeType } = parsed;
|
|
3131
3167
|
logger.info("Image generation successful", {
|
|
3132
3168
|
model: imageModelName,
|
|
3133
3169
|
mimeType,
|
package/dist/neurolink.js
CHANGED
|
@@ -7255,7 +7255,7 @@ Current user's request: ${currentInput}`;
|
|
|
7255
7255
|
const finalOptions = {
|
|
7256
7256
|
timeout: options?.timeout ??
|
|
7257
7257
|
toolInfo?.tool?.timeoutMs ??
|
|
7258
|
-
TOOL_TIMEOUTS.
|
|
7258
|
+
TOOL_TIMEOUTS.EXECUTION_BATCH_MS,
|
|
7259
7259
|
maxRetries: options?.maxRetries ??
|
|
7260
7260
|
toolInfo?.tool?.maxRetries ??
|
|
7261
7261
|
RETRY_ATTEMPTS.DEFAULT,
|
|
@@ -311,7 +311,14 @@ export declare class GoogleVertexProvider extends BaseProvider {
|
|
|
311
311
|
*/
|
|
312
312
|
private buildImageGenerationParts;
|
|
313
313
|
/**
|
|
314
|
-
* Parse the Vertex AI image generation REST API response
|
|
314
|
+
* Parse the Vertex AI image generation REST API response.
|
|
315
|
+
*
|
|
316
|
+
* Dual-mode image models (gemini-3.1-flash-image-preview, gemini-2.5-flash-image,
|
|
317
|
+
* gemini-3-pro-image-preview) decide per-request whether to emit an image or text.
|
|
318
|
+
* When the response contains text parts but no image part, surface the text via
|
|
319
|
+
* `textFallback` so the caller can return a normal text result instead of throwing
|
|
320
|
+
* "model returned text instead of image data" and burning retries on a query that
|
|
321
|
+
* the model has already answered.
|
|
315
322
|
*/
|
|
316
323
|
private parseImageGenerationResponse;
|
|
317
324
|
/**
|
|
@@ -3015,7 +3015,14 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3015
3015
|
return parts;
|
|
3016
3016
|
}
|
|
3017
3017
|
/**
|
|
3018
|
-
* Parse the Vertex AI image generation REST API response
|
|
3018
|
+
* Parse the Vertex AI image generation REST API response.
|
|
3019
|
+
*
|
|
3020
|
+
* Dual-mode image models (gemini-3.1-flash-image-preview, gemini-2.5-flash-image,
|
|
3021
|
+
* gemini-3-pro-image-preview) decide per-request whether to emit an image or text.
|
|
3022
|
+
* When the response contains text parts but no image part, surface the text via
|
|
3023
|
+
* `textFallback` so the caller can return a normal text result instead of throwing
|
|
3024
|
+
* "model returned text instead of image data" and burning retries on a query that
|
|
3025
|
+
* the model has already answered.
|
|
3019
3026
|
*/
|
|
3020
3027
|
parseImageGenerationResponse(data, imageModelName) {
|
|
3021
3028
|
const candidate = data.candidates?.[0];
|
|
@@ -3030,10 +3037,15 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3030
3037
|
(part.inline_data &&
|
|
3031
3038
|
part.inline_data.mime_type?.startsWith("image/"))));
|
|
3032
3039
|
if (!imagePart) {
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3040
|
+
// Filter out empty/whitespace-only text parts so an effectively empty
|
|
3041
|
+
// response throws "no image data" instead of returning content: "".
|
|
3042
|
+
const textParts = candidate.content.parts
|
|
3043
|
+
.map((part) => (typeof part.text === "string" ? part.text : ""))
|
|
3044
|
+
.filter((text) => text.trim().length > 0);
|
|
3045
|
+
if (textParts.length > 0) {
|
|
3046
|
+
return { textFallback: textParts.join("") };
|
|
3047
|
+
}
|
|
3048
|
+
throw new ProviderError(`Image generation completed but no image data was returned. Model: ${imageModelName}`, this.providerName);
|
|
3037
3049
|
}
|
|
3038
3050
|
const imageData = imagePart.inlineData?.data || imagePart.inline_data?.data;
|
|
3039
3051
|
const mimeType = imagePart.inlineData?.mimeType ||
|
|
@@ -3127,7 +3139,31 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3127
3139
|
throw new ProviderError(`Vertex AI API error (${response.status}): ${errorText}`, this.providerName);
|
|
3128
3140
|
}
|
|
3129
3141
|
const data = (await response.json());
|
|
3130
|
-
const
|
|
3142
|
+
const parsed = this.parseImageGenerationResponse(data, imageModelName);
|
|
3143
|
+
// Dual-mode model decided to emit text instead of an image. Surface the
|
|
3144
|
+
// text as a normal text result instead of throwing — the model already
|
|
3145
|
+
// answered the user; failing here just burns retries.
|
|
3146
|
+
if ("textFallback" in parsed) {
|
|
3147
|
+
logger.info("Dual-mode image model returned text; returning as text result", {
|
|
3148
|
+
model: imageModelName,
|
|
3149
|
+
textLength: parsed.textFallback.length,
|
|
3150
|
+
responseTime: Date.now() - startTime,
|
|
3151
|
+
});
|
|
3152
|
+
const inputTokens = this.estimateTokenCount(prompt);
|
|
3153
|
+
const outputTokens = this.estimateTokenCount(parsed.textFallback);
|
|
3154
|
+
const textResult = {
|
|
3155
|
+
content: parsed.textFallback,
|
|
3156
|
+
provider: this.providerName,
|
|
3157
|
+
model: imageModelName,
|
|
3158
|
+
usage: {
|
|
3159
|
+
input: inputTokens,
|
|
3160
|
+
output: outputTokens,
|
|
3161
|
+
total: inputTokens + outputTokens,
|
|
3162
|
+
},
|
|
3163
|
+
};
|
|
3164
|
+
return await this.enhanceResult(textResult, options, startTime);
|
|
3165
|
+
}
|
|
3166
|
+
const { imageData, mimeType } = parsed;
|
|
3131
3167
|
logger.info("Image generation successful", {
|
|
3132
3168
|
model: imageModelName,
|
|
3133
3169
|
mimeType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.59.
|
|
3
|
+
"version": "9.59.6",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 13 providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
|
|
6
6
|
"author": {
|