@juspay/neurolink 11.28.0 → 11.29.1
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 +1 -5
- package/dist/browser/neurolink.min.js +359 -359
- package/dist/core/baseProvider.js +19 -17
- package/dist/core/resolveRequestKind.d.ts +26 -0
- package/dist/core/resolveRequestKind.js +49 -0
- package/dist/neurolink.js +7 -3
- package/dist/providers/googleVertex/client.js +21 -9
- package/dist/providers/replicate.js +13 -13
- package/dist/types/dispatch.d.ts +22 -0
- package/dist/types/dispatch.js +6 -0
- package/dist/types/generate.d.ts +29 -37
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/video.d.ts +13 -0
- package/dist/utils/videoProcessor.d.ts +11 -5
- package/dist/utils/videoProcessor.js +23 -16
- package/package.json +2 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
2
2
|
import { directAgentTools } from "../agent/directTools.js";
|
|
3
3
|
import { defaultProviderFor } from "../factories/mediaHandlerCatalog.js";
|
|
4
|
-
import { isImageGenerationModel } from "./constants.js";
|
|
5
4
|
import { MiddlewareFactory } from "../middleware/factory.js";
|
|
6
5
|
import { modelSupports } from "../models/modelRegistry.js";
|
|
6
|
+
import { resolveRequestKind } from "./resolveRequestKind.js";
|
|
7
7
|
import { ATTR, tracers } from "../telemetry/index.js";
|
|
8
8
|
import { ERROR_CODES, isAbortError, NeuroLinkError, } from "../utils/errorHandling.js";
|
|
9
9
|
import { ProviderError } from "../types/index.js";
|
|
@@ -239,14 +239,12 @@ export class BaseProvider {
|
|
|
239
239
|
}
|
|
240
240
|
// CRITICAL: Image generation models don't support real streaming
|
|
241
241
|
// Force fake streaming for image models to ensure image output is yielded.
|
|
242
|
-
//
|
|
243
|
-
// JSON analysis) so dual-mode models like
|
|
244
|
-
// can still perform text/structured
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
options.output?.format === "text";
|
|
249
|
-
if (isImageModel && !requestsNonImageOutput) {
|
|
242
|
+
// resolveRequestKind() skips this path when the caller explicitly requests
|
|
243
|
+
// non-image output (e.g. JSON analysis) so dual-mode models like
|
|
244
|
+
// gemini-3.1-flash-image-preview can still perform text/structured
|
|
245
|
+
// generation — see its doc comment for the full precedence table.
|
|
246
|
+
const requestKind = resolveRequestKind(options, this.modelName);
|
|
247
|
+
if (requestKind === "image") {
|
|
250
248
|
logger.info(`Image model detected, forcing fake streaming`, {
|
|
251
249
|
provider: this.providerName,
|
|
252
250
|
model: this.modelName,
|
|
@@ -1067,14 +1065,13 @@ export class BaseProvider {
|
|
|
1067
1065
|
}
|
|
1068
1066
|
async runGenerateInActiveContext(options, startTime, otelSpan, otelSpanState) {
|
|
1069
1067
|
try {
|
|
1070
|
-
|
|
1068
|
+
// Single source of truth for "what kind of request is this" — see
|
|
1069
|
+
// resolveRequestKind's doc comment for the full precedence table.
|
|
1070
|
+
const requestKind = resolveRequestKind(options, this.modelName);
|
|
1071
|
+
if (requestKind === "video") {
|
|
1071
1072
|
return await this.handleVideoGeneration(options, startTime);
|
|
1072
1073
|
}
|
|
1073
|
-
|
|
1074
|
-
const requestsNonImageOutput = options.output?.format === "json" ||
|
|
1075
|
-
options.output?.format === "structured" ||
|
|
1076
|
-
options.output?.format === "text";
|
|
1077
|
-
if (isImageModel && !requestsNonImageOutput) {
|
|
1074
|
+
if (requestKind === "image") {
|
|
1078
1075
|
logger.info(`Image generation model detected, routing to executeImageGeneration`, {
|
|
1079
1076
|
provider: this.providerName,
|
|
1080
1077
|
model: this.modelName,
|
|
@@ -1082,7 +1079,7 @@ export class BaseProvider {
|
|
|
1082
1079
|
const imageResult = await this.executeImageGeneration(options);
|
|
1083
1080
|
return await this.enhanceResult(imageResult, options, startTime);
|
|
1084
1081
|
}
|
|
1085
|
-
if (
|
|
1082
|
+
if (requestKind === "tts-direct") {
|
|
1086
1083
|
return this.handleDirectTTSSynthesis(options, startTime);
|
|
1087
1084
|
}
|
|
1088
1085
|
const { tools, model } = await this.prepareGenerationContext(options);
|
|
@@ -2179,7 +2176,12 @@ export class BaseProvider {
|
|
|
2179
2176
|
// shared timeout helper so standard video gen honors the caller's
|
|
2180
2177
|
// timeout the same way director mode does (see above ~Line 2062).
|
|
2181
2178
|
const videoTimeout = options.timeout ?? 600_000; // 10 min default
|
|
2182
|
-
const videoResult = await this.executeWithTimeout(() => VideoProcessor.generate(requestedProvider,
|
|
2179
|
+
const videoResult = await this.executeWithTimeout(() => VideoProcessor.generate(requestedProvider, {
|
|
2180
|
+
...(options.output?.video ?? {}),
|
|
2181
|
+
image: imageBuffer,
|
|
2182
|
+
prompt,
|
|
2183
|
+
region: options.region,
|
|
2184
|
+
}), { timeout: videoTimeout, operationType: "generate" });
|
|
2183
2185
|
// Prefer the handler's own model id (more accurate — it knows the exact
|
|
2184
2186
|
// checkpoint that ran). Fall back to the request-time value, and finally
|
|
2185
2187
|
// to the Vertex default only when we're on the Vertex route.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RequestKind, RequestKindInput } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* The dispatch decision for "what kind of request is this" — text, image,
|
|
4
|
+
* video, music, avatar, direct TTS synthesis, or PPT generation — at the
|
|
5
|
+
* CORE call sites: neurolink.ts's maybeHandleEarlyGenerateResult
|
|
6
|
+
* (music/avatar/ppt/workflow routing) and baseProvider.ts's
|
|
7
|
+
* stream()/runGenerateInActiveContext (image/video/tts-direct routing) call
|
|
8
|
+
* this instead of independently re-deriving the decision.
|
|
9
|
+
*
|
|
10
|
+
* Also the only copy at the provider-override level: replicate.ts's
|
|
11
|
+
* generate() override and googleVertex/client.ts's generate()/stream()
|
|
12
|
+
* overrides (which bypass BaseProvider's paths) call this too, so an edit
|
|
13
|
+
* to this precedence table reaches every dispatch site.
|
|
14
|
+
*
|
|
15
|
+
* Precedence, checked in order:
|
|
16
|
+
* 1. output.mode (music/avatar/video/ppt) — an explicit mode always wins.
|
|
17
|
+
* 2. an image-generation model, unless the caller explicitly asked for a
|
|
18
|
+
* non-image output.format (json/structured/text) — this lets dual-mode
|
|
19
|
+
* models like gemini-3.1-flash-image-preview still perform text or
|
|
20
|
+
* structured generation when requested.
|
|
21
|
+
* 3. tts.enabled without tts.useAiResponse — direct synthesis, bypassing
|
|
22
|
+
* the LLM turn entirely (useAiResponse means the LLM's own text
|
|
23
|
+
* response gets synthesized afterward, which is NOT this branch).
|
|
24
|
+
* 4. otherwise, "text".
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveRequestKind(options: RequestKindInput, modelName?: string): RequestKind;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isImageGenerationModel } from "./constants.js";
|
|
2
|
+
/**
|
|
3
|
+
* The dispatch decision for "what kind of request is this" — text, image,
|
|
4
|
+
* video, music, avatar, direct TTS synthesis, or PPT generation — at the
|
|
5
|
+
* CORE call sites: neurolink.ts's maybeHandleEarlyGenerateResult
|
|
6
|
+
* (music/avatar/ppt/workflow routing) and baseProvider.ts's
|
|
7
|
+
* stream()/runGenerateInActiveContext (image/video/tts-direct routing) call
|
|
8
|
+
* this instead of independently re-deriving the decision.
|
|
9
|
+
*
|
|
10
|
+
* Also the only copy at the provider-override level: replicate.ts's
|
|
11
|
+
* generate() override and googleVertex/client.ts's generate()/stream()
|
|
12
|
+
* overrides (which bypass BaseProvider's paths) call this too, so an edit
|
|
13
|
+
* to this precedence table reaches every dispatch site.
|
|
14
|
+
*
|
|
15
|
+
* Precedence, checked in order:
|
|
16
|
+
* 1. output.mode (music/avatar/video/ppt) — an explicit mode always wins.
|
|
17
|
+
* 2. an image-generation model, unless the caller explicitly asked for a
|
|
18
|
+
* non-image output.format (json/structured/text) — this lets dual-mode
|
|
19
|
+
* models like gemini-3.1-flash-image-preview still perform text or
|
|
20
|
+
* structured generation when requested.
|
|
21
|
+
* 3. tts.enabled without tts.useAiResponse — direct synthesis, bypassing
|
|
22
|
+
* the LLM turn entirely (useAiResponse means the LLM's own text
|
|
23
|
+
* response gets synthesized afterward, which is NOT this branch).
|
|
24
|
+
* 4. otherwise, "text".
|
|
25
|
+
*/
|
|
26
|
+
export function resolveRequestKind(options, modelName) {
|
|
27
|
+
if (options.output?.mode === "music") {
|
|
28
|
+
return "music";
|
|
29
|
+
}
|
|
30
|
+
if (options.output?.mode === "avatar") {
|
|
31
|
+
return "avatar";
|
|
32
|
+
}
|
|
33
|
+
if (options.output?.mode === "video") {
|
|
34
|
+
return "video";
|
|
35
|
+
}
|
|
36
|
+
if (options.output?.mode === "ppt") {
|
|
37
|
+
return "ppt";
|
|
38
|
+
}
|
|
39
|
+
const requestsNonImageOutput = options.output?.format === "json" ||
|
|
40
|
+
options.output?.format === "structured" ||
|
|
41
|
+
options.output?.format === "text";
|
|
42
|
+
if (isImageGenerationModel(modelName) && !requestsNonImageOutput) {
|
|
43
|
+
return "image";
|
|
44
|
+
}
|
|
45
|
+
if (options.tts?.enabled && !options.tts?.useAiResponse) {
|
|
46
|
+
return "tts-direct";
|
|
47
|
+
}
|
|
48
|
+
return "text";
|
|
49
|
+
}
|
package/dist/neurolink.js
CHANGED
|
@@ -34,6 +34,7 @@ import { buildToolRoutingCatalog, buildRoutingQueryFromHistory, resolveToolRouti
|
|
|
34
34
|
import { ToolRoutingCache } from "./core/toolRoutingCache.js";
|
|
35
35
|
import { DEFAULT_RECENT_TURNS, KnowledgeGroundingEngine, } from "./knowledge/index.js";
|
|
36
36
|
import { AIProviderFactory } from "./core/factory.js";
|
|
37
|
+
import { resolveRequestKind } from "./core/resolveRequestKind.js";
|
|
37
38
|
import { createToolEventPayload } from "./core/toolEvents.js";
|
|
38
39
|
import { ProviderFactory } from "./factories/providerFactory.js";
|
|
39
40
|
import { ProviderRegistry } from "./factories/providerRegistry.js";
|
|
@@ -3576,13 +3577,16 @@ Current user's request: ${currentInput}`;
|
|
|
3576
3577
|
}
|
|
3577
3578
|
return this.generateWithWorkflow(options);
|
|
3578
3579
|
}
|
|
3579
|
-
|
|
3580
|
+
// Single source of truth for "what kind of request is this" — see
|
|
3581
|
+
// resolveRequestKind's doc comment for the full precedence table.
|
|
3582
|
+
const requestKind = resolveRequestKind(options, options.model);
|
|
3583
|
+
if (requestKind === "music") {
|
|
3580
3584
|
return this.generateWithMusic(options, generateSpan);
|
|
3581
3585
|
}
|
|
3582
|
-
if (
|
|
3586
|
+
if (requestKind === "avatar") {
|
|
3583
3587
|
return this.generateWithAvatar(options, generateSpan);
|
|
3584
3588
|
}
|
|
3585
|
-
if (
|
|
3589
|
+
if (requestKind !== "ppt") {
|
|
3586
3590
|
return null;
|
|
3587
3591
|
}
|
|
3588
3592
|
if (options.stt?.enabled && options.stt?.audio) {
|
|
@@ -8,7 +8,8 @@ import { BaseProvider } from "../../core/baseProvider.js";
|
|
|
8
8
|
import { unwrapImagePayload } from "../../adapters/imageFormatSupport.js";
|
|
9
9
|
import { appendNativeAudioParts } from "../googleNativeGemini3/utils.js";
|
|
10
10
|
import { getMimeTypeForExtension } from "../../processors/config/mimeConstants.js";
|
|
11
|
-
import { DEFAULT_GEMINI_STREAM_TIMEOUT_MS, DEFAULT_MAX_STEPS, DEFAULT_TOOL_EXECUTION_TIMEOUT_MS, DEFAULT_TOOL_MAX_RETRIES, GLOBAL_LOCATION_MODELS,
|
|
11
|
+
import { DEFAULT_GEMINI_STREAM_TIMEOUT_MS, DEFAULT_MAX_STEPS, DEFAULT_TOOL_EXECUTION_TIMEOUT_MS, DEFAULT_TOOL_MAX_RETRIES, GLOBAL_LOCATION_MODELS, TOOL_STORAGE_TIMEOUT_MS, } from "../../core/constants.js";
|
|
12
|
+
import { resolveRequestKind } from "../../core/resolveRequestKind.js";
|
|
12
13
|
import { ModelConfigurationManager } from "../../core/modelConfiguration.js";
|
|
13
14
|
import { isSchemaComplexityError } from "../../core/modules/structuredOutputPolicy.js";
|
|
14
15
|
import { redactUrlForError, stringifyContentSafe, } from "../../utils/logSanitize.js";
|
|
@@ -5089,9 +5090,11 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5089
5090
|
? { input: { text: optionsOrPrompt } }
|
|
5090
5091
|
: optionsOrPrompt;
|
|
5091
5092
|
const modelName = options.model || this.modelName || getDefaultVertexModel();
|
|
5092
|
-
//
|
|
5093
|
-
|
|
5094
|
-
|
|
5093
|
+
// Image-generation requests can't stream — fall back to generate.
|
|
5094
|
+
// Same single dispatch decision as generate(): resolveRequestKind
|
|
5095
|
+
// keeps dual-mode models streaming text when the caller explicitly
|
|
5096
|
+
// asked for a non-image output.format.
|
|
5097
|
+
if (resolveRequestKind(options, modelName) === "image") {
|
|
5095
5098
|
logger.warn("[GoogleVertex] Image generation models don't support streaming, falling back to generate", { model: modelName });
|
|
5096
5099
|
// Convert stream options to text generation options
|
|
5097
5100
|
const generateOptions = {
|
|
@@ -5150,13 +5153,20 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5150
5153
|
},
|
|
5151
5154
|
}, async (generateSpan) => {
|
|
5152
5155
|
const generateStartTime = Date.now();
|
|
5156
|
+
// One dispatch decision for the whole override: Vertex's generate()
|
|
5157
|
+
// bypasses BaseProvider.generate(), so it re-runs the same
|
|
5158
|
+
// resolveRequestKind() the core call sites use rather than keeping
|
|
5159
|
+
// a hand-rolled copy of the precedence (which had drifted: tts
|
|
5160
|
+
// checked before image, and a cruder case-insensitive startsWith
|
|
5161
|
+
// image match without boundary awareness).
|
|
5162
|
+
const requestKind = resolveRequestKind(options, modelName);
|
|
5153
5163
|
// Video-mode requests must route through BaseProvider's
|
|
5154
5164
|
// handleVideoGeneration (which loads the Veo 3 adapter). Vertex's
|
|
5155
5165
|
// native @google/genai path is text/image only — without this
|
|
5156
5166
|
// gate, video requests fall through to gemini-2.5-flash and the
|
|
5157
5167
|
// model politely declines ("I cannot create animations") instead
|
|
5158
5168
|
// of producing video bytes.
|
|
5159
|
-
if (
|
|
5169
|
+
if (requestKind === "video") {
|
|
5160
5170
|
logger.info("[GoogleVertex] Routing video-mode generate to handleVideoGeneration", { model: modelName });
|
|
5161
5171
|
const videoResult = await this.handleVideoGeneration(options, generateStartTime);
|
|
5162
5172
|
this.attachUsageAndCostAttributes(generateSpan, modelName, videoResult?.usage);
|
|
@@ -5168,15 +5178,17 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5168
5178
|
// (synthesise the input text directly; no LLM call). BaseProvider's
|
|
5169
5179
|
// standard generate() does the same dispatch — we replicate it here
|
|
5170
5180
|
// because Vertex's override bypasses that path.
|
|
5171
|
-
if (
|
|
5181
|
+
if (requestKind === "tts-direct") {
|
|
5172
5182
|
logger.info("[GoogleVertex] Routing TTS direct-synthesis to handleDirectTTSSynthesis", { model: modelName });
|
|
5173
5183
|
const ttsResult = await this.handleDirectTTSSynthesis(options, generateStartTime);
|
|
5174
5184
|
this.emitGenerationEnd(modelName, ttsResult, generateStartTime, true);
|
|
5175
5185
|
return ttsResult;
|
|
5176
5186
|
}
|
|
5177
|
-
//
|
|
5178
|
-
|
|
5179
|
-
|
|
5187
|
+
// Image-generation models route to executeImageGeneration without
|
|
5188
|
+
// tools. resolveRequestKind also carries the dual-mode exception:
|
|
5189
|
+
// an explicit non-image output.format keeps models like
|
|
5190
|
+
// gemini-3.1-flash-image-preview on the text path.
|
|
5191
|
+
if (requestKind === "image") {
|
|
5180
5192
|
logger.info("[GoogleVertex] Routing image generation model to executeImageGeneration", { model: modelName });
|
|
5181
5193
|
const imageResult = await this.executeImageGeneration(options);
|
|
5182
5194
|
this.attachUsageAndCostAttributes(generateSpan, modelName, imageResult?.usage);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ErrorCategory, ErrorSeverity, ReplicateModels, } from "../constants/enums.js";
|
|
2
2
|
import { BaseProvider } from "../core/baseProvider.js";
|
|
3
|
+
import { resolveRequestKind } from "../core/resolveRequestKind.js";
|
|
3
4
|
import { getReplicateAuth } from "../adapters/replicate/auth.js";
|
|
4
5
|
import { downloadPredictionOutput, predict, } from "../adapters/replicate/predictionLifecycle.js";
|
|
5
6
|
import { MAX_IMAGE_BYTES } from "../utils/sizeGuard.js";
|
|
@@ -121,19 +122,18 @@ export class ReplicateProvider extends BaseProvider {
|
|
|
121
122
|
const options = typeof optionsOrPrompt === "string"
|
|
122
123
|
? { prompt: optionsOrPrompt }
|
|
123
124
|
: optionsOrPrompt;
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (isImageModel && !requestsNonImageOutput) {
|
|
125
|
+
// Delegate media kinds to the base class (which never calls
|
|
126
|
+
// getAISDKModel for these). resolveRequestKind owns the precedence —
|
|
127
|
+
// including the dual-mode exception where an explicit non-image
|
|
128
|
+
// output.format keeps an image-gen model on the text path. "ppt" and
|
|
129
|
+
// "tts-direct" deliberately stay on the local text path below:
|
|
130
|
+
// super.generate() would hit prepareGenerationContext() →
|
|
131
|
+
// getAISDKModel(), which throws for Replicate.
|
|
132
|
+
const kind = resolveRequestKind(options, this.modelName);
|
|
133
|
+
if (kind === "video" ||
|
|
134
|
+
kind === "avatar" ||
|
|
135
|
+
kind === "music" ||
|
|
136
|
+
kind === "image") {
|
|
137
137
|
return super.generate(options, _analysisSchema);
|
|
138
138
|
}
|
|
139
139
|
// Structured / JSON output is not natively supported by the Replicate
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types backing resolveRequestKind() (src/lib/core/resolveRequestKind.ts) —
|
|
3
|
+
* the single function that decides which of NeuroLink's output modes a
|
|
4
|
+
* generate/stream request is asking for.
|
|
5
|
+
*/
|
|
6
|
+
export type RequestKind = "text" | "image" | "video" | "music" | "avatar" | "tts-direct" | "ppt";
|
|
7
|
+
/**
|
|
8
|
+
* Narrow structural subset of TextGenerationOptions/GenerateOptions that
|
|
9
|
+
* resolveRequestKind() actually reads. Kept intentionally minimal (rather
|
|
10
|
+
* than importing the full options type) so this module has no dependency
|
|
11
|
+
* on the wider options type graph.
|
|
12
|
+
*/
|
|
13
|
+
export type RequestKindInput = {
|
|
14
|
+
output?: {
|
|
15
|
+
mode?: string;
|
|
16
|
+
format?: string;
|
|
17
|
+
};
|
|
18
|
+
tts?: {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
useAiResponse?: boolean;
|
|
21
|
+
};
|
|
22
|
+
};
|
package/dist/types/generate.d.ts
CHANGED
|
@@ -802,23 +802,12 @@ export type ToolExecutionCaptureOptions = {
|
|
|
802
802
|
*/
|
|
803
803
|
export type GenerateStopReason = "completed" | "step-cap" | "context-cap" | "time-limit" | "stalled" | "aborted" | "provider-error";
|
|
804
804
|
/**
|
|
805
|
-
*
|
|
806
|
-
*
|
|
805
|
+
* Media generation/processing outputs shared by GenerateResult and
|
|
806
|
+
* TextGenerationResult. Extracted so both result types intersect (&) this
|
|
807
|
+
* single definition instead of each declaring its own drifting copy of the
|
|
808
|
+
* same audio/video/avatar/music/ppt/image/transcription fields.
|
|
807
809
|
*/
|
|
808
|
-
export type
|
|
809
|
-
content: string;
|
|
810
|
-
/** Knowledge-grounding diagnostics for this turn (present only when grounding ran). */
|
|
811
|
-
knowledge?: KnowledgeGroundingMetadata;
|
|
812
|
-
/**
|
|
813
|
-
* Parsed structured object when a `schema` was requested. Populated from
|
|
814
|
-
* AI-SDK experimental_output, or from text-mode coercion (balanced-scan +
|
|
815
|
-
* jsonrepair). Prefer this over JSON.parse(content) — it never requires the
|
|
816
|
-
* caller to re-parse hand-escaped model text.
|
|
817
|
-
*/
|
|
818
|
-
structuredData?: unknown;
|
|
819
|
-
outputs?: {
|
|
820
|
-
text: string;
|
|
821
|
-
};
|
|
810
|
+
export type MediaGenerationOutputs = {
|
|
822
811
|
/**
|
|
823
812
|
* Text-to-Speech audio result
|
|
824
813
|
*
|
|
@@ -911,9 +900,31 @@ export type GenerateResult = {
|
|
|
911
900
|
* ```
|
|
912
901
|
*/
|
|
913
902
|
ppt?: PPTGenerationResult;
|
|
903
|
+
/** Standard format for image generation */
|
|
914
904
|
imageOutput?: {
|
|
915
905
|
base64: string;
|
|
916
906
|
} | null;
|
|
907
|
+
/** STT transcription result (present when stt.enabled is true and audio input was provided) */
|
|
908
|
+
transcription?: STTResult;
|
|
909
|
+
};
|
|
910
|
+
/**
|
|
911
|
+
* Generate function result type - Primary output format
|
|
912
|
+
* Future-ready for multi-modal outputs while maintaining text focus
|
|
913
|
+
*/
|
|
914
|
+
export type GenerateResult = {
|
|
915
|
+
content: string;
|
|
916
|
+
/** Knowledge-grounding diagnostics for this turn (present only when grounding ran). */
|
|
917
|
+
knowledge?: KnowledgeGroundingMetadata;
|
|
918
|
+
/**
|
|
919
|
+
* Parsed structured object when a `schema` was requested. Populated from
|
|
920
|
+
* AI-SDK experimental_output, or from text-mode coercion (balanced-scan +
|
|
921
|
+
* jsonrepair). Prefer this over JSON.parse(content) — it never requires the
|
|
922
|
+
* caller to re-parse hand-escaped model text.
|
|
923
|
+
*/
|
|
924
|
+
structuredData?: unknown;
|
|
925
|
+
outputs?: {
|
|
926
|
+
text: string;
|
|
927
|
+
};
|
|
917
928
|
provider?: string;
|
|
918
929
|
model?: string;
|
|
919
930
|
finishReason?: string;
|
|
@@ -1013,8 +1024,6 @@ export type GenerateResult = {
|
|
|
1013
1024
|
reasoning?: string;
|
|
1014
1025
|
/** Token count for reasoning content */
|
|
1015
1026
|
reasoningTokens?: number;
|
|
1016
|
-
/** STT transcription result (present when stt.enabled is true and audio input was provided) */
|
|
1017
|
-
transcription?: STTResult;
|
|
1018
1027
|
retries?: {
|
|
1019
1028
|
count: number;
|
|
1020
1029
|
errors: Array<{
|
|
@@ -1033,7 +1042,7 @@ export type GenerateResult = {
|
|
|
1033
1042
|
* absolute `requestsRemaining` / `tokensRemaining`.
|
|
1034
1043
|
*/
|
|
1035
1044
|
limits?: ClaudeLimitSnapshot;
|
|
1036
|
-
};
|
|
1045
|
+
} & MediaGenerationOutputs;
|
|
1037
1046
|
/**
|
|
1038
1047
|
* Unified options for both generation and streaming
|
|
1039
1048
|
* Supports factory patterns and domain configuration
|
|
@@ -1467,23 +1476,6 @@ export type TextGenerationResult = {
|
|
|
1467
1476
|
}>;
|
|
1468
1477
|
analytics?: AnalyticsData;
|
|
1469
1478
|
evaluation?: EvaluationData;
|
|
1470
|
-
audio?: TTSResult;
|
|
1471
|
-
/** Outcome of TTS synthesis, including the failure reason. */
|
|
1472
|
-
ttsMetadata?: TTSMetadata;
|
|
1473
|
-
/** STT transcription result (present when stt input was processed) */
|
|
1474
|
-
transcription?: STTResult;
|
|
1475
|
-
/** Video generation result */
|
|
1476
|
-
video?: VideoGenerationResult;
|
|
1477
|
-
/** Avatar (talking-head) generation result */
|
|
1478
|
-
avatar?: AvatarResult;
|
|
1479
|
-
/** Music generation result */
|
|
1480
|
-
music?: MusicResult;
|
|
1481
|
-
/** PowerPoint generation result */
|
|
1482
|
-
ppt?: PPTGenerationResult;
|
|
1483
|
-
/** Image generation output */
|
|
1484
|
-
imageOutput?: {
|
|
1485
|
-
base64: string;
|
|
1486
|
-
} | null;
|
|
1487
1479
|
/** Gemini 3 thought signature for reasoning continuity across turns */
|
|
1488
1480
|
thoughtSignature?: string;
|
|
1489
1481
|
/** Thinking/reasoning text from provider (Anthropic thinking blocks, Gemini thought parts, DeepSeek/NIM reasoning_content) */
|
|
@@ -1497,7 +1489,7 @@ export type TextGenerationResult = {
|
|
|
1497
1489
|
message: string;
|
|
1498
1490
|
}>;
|
|
1499
1491
|
};
|
|
1500
|
-
};
|
|
1492
|
+
} & MediaGenerationOutputs;
|
|
1501
1493
|
/**
|
|
1502
1494
|
* Enhanced result type with optional analytics/evaluation
|
|
1503
1495
|
*/
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
package/dist/types/video.d.ts
CHANGED
|
@@ -13,6 +13,19 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { VideoGenerationResult, VideoOutputOptions } from "./multimodal.js";
|
|
15
15
|
export type { VideoGenerationResult, VideoOutputOptions, } from "./multimodal.js";
|
|
16
|
+
/**
|
|
17
|
+
* Bag-form input to `VideoProcessor.generate()` — the primary data (image,
|
|
18
|
+
* prompt, region) alongside the video-specific output options, collapsed
|
|
19
|
+
* into a single object matching Music/Avatar's existing `generate(provider,
|
|
20
|
+
* options)` shape. `VideoHandler.generate()`'s own 4-positional-argument
|
|
21
|
+
* signature is unchanged; `VideoProcessor.generate()` translates between the
|
|
22
|
+
* two internally.
|
|
23
|
+
*/
|
|
24
|
+
export type VideoGenerateOptions = VideoOutputOptions & {
|
|
25
|
+
image: Buffer;
|
|
26
|
+
prompt: string;
|
|
27
|
+
region?: string;
|
|
28
|
+
};
|
|
16
29
|
/**
|
|
17
30
|
* Director-mode transition options.
|
|
18
31
|
*
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @module utils/videoProcessor
|
|
12
12
|
*/
|
|
13
13
|
import { VIDEO_ERROR_CODES } from "../constants/videoErrors.js";
|
|
14
|
-
import type { VideoGenerationResult, VideoHandler, VideoOutputOptions, VideoTransitionOptions } from "../types/index.js";
|
|
14
|
+
import type { VideoGenerateOptions, VideoGenerationResult, VideoHandler, VideoOutputOptions, VideoTransitionOptions } from "../types/index.js";
|
|
15
15
|
import { VideoError } from "../adapters/video/vertexVideoHandler.js";
|
|
16
16
|
export { VideoError, VIDEO_ERROR_CODES };
|
|
17
17
|
/**
|
|
@@ -45,13 +45,19 @@ export declare class VideoProcessor {
|
|
|
45
45
|
* Generate a single video clip via the registered handler.
|
|
46
46
|
*
|
|
47
47
|
* @param provider - Registered provider name (e.g. "vertex", "kling")
|
|
48
|
-
* @param
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
48
|
+
* @param options - Bag of the source image, prompt, optional region
|
|
49
|
+
* override, and resolution / length / aspect-ratio / audio options.
|
|
50
|
+
* Translated internally into the handler-level 4-positional-argument
|
|
51
|
+
* call — `VideoHandler.generate()`'s own signature is unchanged.
|
|
52
52
|
* @throws VideoError on registry miss, handler-not-configured, or
|
|
53
53
|
* generation failure
|
|
54
54
|
*/
|
|
55
|
+
static generate(provider: string, options: VideoGenerateOptions): Promise<VideoGenerationResult>;
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated Positional form kept for backward compatibility with
|
|
58
|
+
* pre-bag callers (VideoProcessor is a public export). Use the
|
|
59
|
+
* options-bag overload.
|
|
60
|
+
*/
|
|
55
61
|
static generate(provider: string, image: Buffer, prompt: string, options: VideoOutputOptions, region?: string): Promise<VideoGenerationResult>;
|
|
56
62
|
/**
|
|
57
63
|
* Generate a transition clip via the registered handler (Director Mode).
|
|
@@ -14,6 +14,11 @@ import { ErrorCategory, ErrorSeverity } from "../constants/enums.js";
|
|
|
14
14
|
import { VIDEO_ERROR_CODES } from "../constants/videoErrors.js";
|
|
15
15
|
import { SpanSerializer, SpanStatus, SpanType, getMetricsAggregator, } from "../observability/index.js";
|
|
16
16
|
import { logger } from "./logger.js";
|
|
17
|
+
import { withTimeout } from "./async/withTimeout.js";
|
|
18
|
+
// Video generation is legitimately minutes-long (Kling/Runway render queues),
|
|
19
|
+
// so the bound is generous — its job is to convert a wedged handler into an
|
|
20
|
+
// error rather than an eternal hang, not to police normal latency.
|
|
21
|
+
const VIDEO_GENERATION_TIMEOUT_MS = 600_000;
|
|
17
22
|
// VideoError is canonical in vertexVideoHandler.ts (existing). Re-export
|
|
18
23
|
// here so consumers of `VideoProcessor` can import the typed error from
|
|
19
24
|
// the same module. Both throws and instanceof checks resolve to the same
|
|
@@ -69,19 +74,17 @@ export class VideoProcessor {
|
|
|
69
74
|
"video.audio": options.audio,
|
|
70
75
|
};
|
|
71
76
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
static async generate(provider, image, prompt, options, region) {
|
|
84
|
-
const span = SpanSerializer.createSpan(SpanType.MEDIA_GENERATION, "video.generate", this.buildSpanAttributes(provider, options));
|
|
77
|
+
static async generate(provider, optionsOrImage, legacyPrompt, legacyOptions, legacyRegion) {
|
|
78
|
+
const bag = Buffer.isBuffer(optionsOrImage)
|
|
79
|
+
? {
|
|
80
|
+
image: optionsOrImage,
|
|
81
|
+
prompt: legacyPrompt ?? "",
|
|
82
|
+
...(legacyRegion !== undefined ? { region: legacyRegion } : {}),
|
|
83
|
+
...(legacyOptions ?? {}),
|
|
84
|
+
}
|
|
85
|
+
: optionsOrImage;
|
|
86
|
+
const { image, prompt, region, ...videoOptions } = bag;
|
|
87
|
+
const span = SpanSerializer.createSpan(SpanType.MEDIA_GENERATION, "video.generate", this.buildSpanAttributes(provider, videoOptions));
|
|
85
88
|
try {
|
|
86
89
|
const handler = this.getHandler(provider);
|
|
87
90
|
if (!handler) {
|
|
@@ -105,7 +108,10 @@ export class VideoProcessor {
|
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
110
|
logger.debug(`[VideoProcessor] Starting video generation with provider: ${provider}`);
|
|
108
|
-
|
|
111
|
+
// Bounded per repo guideline (async provider calls wrap withTimeout):
|
|
112
|
+
// video generation is legitimately slow, so the deadline is generous —
|
|
113
|
+
// but a wedged handler must error, never hang the caller forever.
|
|
114
|
+
const result = await withTimeout(handler.generate(image, prompt, videoOptions, region), VIDEO_GENERATION_TIMEOUT_MS, `Video generation via "${provider}" timed out after ${VIDEO_GENERATION_TIMEOUT_MS}ms`);
|
|
109
115
|
const ended = SpanSerializer.endSpan(span, SpanStatus.OK);
|
|
110
116
|
getMetricsAggregator().recordSpan(ended);
|
|
111
117
|
logger.info(`[VideoProcessor] Generated ${result.data.length} bytes (${provider})`);
|
|
@@ -124,7 +130,7 @@ export class VideoProcessor {
|
|
|
124
130
|
category: ErrorCategory.EXECUTION,
|
|
125
131
|
severity: ErrorSeverity.HIGH,
|
|
126
132
|
retriable: true,
|
|
127
|
-
context: { provider, options, region },
|
|
133
|
+
context: { provider, options: videoOptions, region },
|
|
128
134
|
originalError: err instanceof Error ? err : undefined,
|
|
129
135
|
});
|
|
130
136
|
}
|
|
@@ -169,7 +175,8 @@ export class VideoProcessor {
|
|
|
169
175
|
});
|
|
170
176
|
}
|
|
171
177
|
try {
|
|
172
|
-
|
|
178
|
+
// Same bound as generate(): a wedged transition must error, not hang.
|
|
179
|
+
return await withTimeout(handler.generateTransition(firstFrame, lastFrame, prompt, options, region), VIDEO_GENERATION_TIMEOUT_MS, `Video transition via "${provider}" timed out after ${VIDEO_GENERATION_TIMEOUT_MS}ms`);
|
|
173
180
|
}
|
|
174
181
|
catch (err) {
|
|
175
182
|
if (err instanceof VideoError) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.29.1",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
|
|
6
6
|
"author": {
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"test:context": "pnpm exec tsx test/continuous-test-suite-context.ts",
|
|
74
74
|
"test:evaluation": "pnpm exec tsx test/continuous-test-suite-evaluation.ts",
|
|
75
75
|
"test:handler-registry": "pnpm exec tsx test/continuous-test-suite-handler-registry.ts",
|
|
76
|
+
"test:resolve-request-kind": "pnpm exec tsx test/continuous-test-suite-resolve-request-kind.ts",
|
|
76
77
|
"test:mcp": "pnpm exec tsx test/continuous-test-suite-mcp-infra.ts",
|
|
77
78
|
"test:tool-resolution": "pnpm exec tsx test/continuous-test-suite-tool-resolution.ts",
|
|
78
79
|
"test:mcp:http": "pnpm exec tsx test/continuous-test-suite-mcp-http.ts",
|