@juspay/neurolink 9.64.0 → 9.65.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 +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 +33 -9
- package/dist/agent/directTools.js +11 -3
- 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 +624 -601
- package/dist/cli/commands/mcp.js +29 -0
- package/dist/cli/commands/proxy.js +24 -5
- package/dist/cli/factories/commandFactory.d.ts +11 -1
- package/dist/cli/factories/commandFactory.js +291 -38
- package/dist/constants/contextWindows.js +101 -0
- package/dist/constants/enums.d.ts +273 -2
- package/dist/constants/enums.js +290 -1
- package/dist/constants/videoErrors.d.ts +4 -0
- package/dist/constants/videoErrors.js +4 -0
- package/dist/core/baseProvider.d.ts +22 -2
- package/dist/core/baseProvider.js +217 -11
- package/dist/core/constants.d.ts +12 -0
- package/dist/core/constants.js +72 -1
- package/dist/evaluation/index.d.ts +2 -0
- package/dist/evaluation/index.js +4 -0
- package/dist/factories/providerFactory.js +7 -1
- package/dist/factories/providerRegistry.js +202 -5
- package/dist/features/ppt/contentPlanner.js +42 -14
- package/dist/index.d.ts +9 -1
- package/dist/index.js +16 -1
- package/dist/lib/adapters/providerImageAdapter.js +29 -1
- package/dist/lib/adapters/replicate/auth.d.ts +19 -0
- package/dist/lib/adapters/replicate/auth.js +33 -0
- package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
- package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
- package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
- package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
- package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
- package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
- package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
- package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
- package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
- package/dist/lib/adapters/video/vertexVideoHandler.js +33 -9
- package/dist/lib/agent/directTools.js +11 -3
- package/dist/lib/autoresearch/runner.js +8 -2
- package/dist/lib/avatar/index.d.ts +13 -0
- package/dist/lib/avatar/index.js +14 -0
- package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
- package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
- package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
- package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
- package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
- package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
- package/dist/lib/constants/contextWindows.js +101 -0
- package/dist/lib/constants/enums.d.ts +273 -2
- package/dist/lib/constants/enums.js +290 -1
- package/dist/lib/constants/videoErrors.d.ts +4 -0
- package/dist/lib/constants/videoErrors.js +4 -0
- package/dist/lib/core/baseProvider.d.ts +22 -2
- package/dist/lib/core/baseProvider.js +217 -11
- package/dist/lib/core/constants.d.ts +12 -0
- package/dist/lib/core/constants.js +72 -1
- package/dist/lib/evaluation/index.d.ts +2 -0
- package/dist/lib/evaluation/index.js +4 -0
- package/dist/lib/factories/providerFactory.js +7 -1
- package/dist/lib/factories/providerRegistry.js +202 -5
- package/dist/lib/features/ppt/contentPlanner.js +42 -14
- package/dist/lib/index.d.ts +9 -1
- package/dist/lib/index.js +16 -1
- package/dist/lib/middleware/builtin/lifecycle.js +39 -9
- package/dist/lib/music/index.d.ts +13 -0
- package/dist/lib/music/index.js +14 -0
- package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
- package/dist/lib/music/providers/BeatovenMusic.js +334 -0
- package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
- package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
- package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
- package/dist/lib/music/providers/LyriaMusic.js +173 -0
- package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
- package/dist/lib/music/providers/ReplicateMusic.js +262 -0
- package/dist/lib/neurolink.d.ts +30 -0
- package/dist/lib/neurolink.js +323 -77
- package/dist/lib/providers/amazonBedrock.d.ts +10 -0
- package/dist/lib/providers/amazonBedrock.js +94 -39
- package/dist/lib/providers/anthropic.js +55 -7
- package/dist/lib/providers/anthropicBaseProvider.js +1 -1
- package/dist/lib/providers/azureOpenai.js +66 -17
- package/dist/lib/providers/cloudflare.d.ts +35 -0
- package/dist/lib/providers/cloudflare.js +174 -0
- package/dist/lib/providers/cohere.d.ts +52 -0
- package/dist/lib/providers/cohere.js +253 -0
- package/dist/lib/providers/deepseek.js +72 -17
- package/dist/lib/providers/fireworks.d.ts +33 -0
- package/dist/lib/providers/fireworks.js +164 -0
- package/dist/lib/providers/googleAiStudio.js +126 -10
- package/dist/lib/providers/googleNativeGemini3.d.ts +26 -6
- package/dist/lib/providers/googleNativeGemini3.js +276 -29
- package/dist/lib/providers/googleVertex.js +639 -181
- package/dist/lib/providers/groq.d.ts +33 -0
- package/dist/lib/providers/groq.js +181 -0
- package/dist/lib/providers/huggingFace.js +9 -8
- package/dist/lib/providers/ideogram.d.ts +34 -0
- package/dist/lib/providers/ideogram.js +184 -0
- package/dist/lib/providers/index.d.ts +13 -0
- package/dist/lib/providers/index.js +13 -0
- package/dist/lib/providers/jina.d.ts +59 -0
- package/dist/lib/providers/jina.js +218 -0
- package/dist/lib/providers/llamaCpp.js +14 -46
- package/dist/lib/providers/lmStudio.js +14 -47
- package/dist/lib/providers/mistral.js +7 -7
- package/dist/lib/providers/nvidiaNim.js +160 -19
- package/dist/lib/providers/ollama.js +7 -7
- package/dist/lib/providers/openAI.d.ts +22 -1
- package/dist/lib/providers/openAI.js +181 -0
- package/dist/lib/providers/openRouter.js +35 -23
- package/dist/lib/providers/openaiCompatible.js +9 -8
- package/dist/lib/providers/perplexity.d.ts +33 -0
- package/dist/lib/providers/perplexity.js +179 -0
- package/dist/lib/providers/recraft.d.ts +34 -0
- package/dist/lib/providers/recraft.js +197 -0
- package/dist/lib/providers/replicate.d.ts +75 -0
- package/dist/lib/providers/replicate.js +403 -0
- package/dist/lib/providers/stability.d.ts +37 -0
- package/dist/lib/providers/stability.js +191 -0
- package/dist/lib/providers/togetherAi.d.ts +33 -0
- package/dist/lib/providers/togetherAi.js +176 -0
- package/dist/lib/providers/voyage.d.ts +47 -0
- package/dist/lib/providers/voyage.js +177 -0
- package/dist/lib/providers/xai.d.ts +33 -0
- package/dist/lib/providers/xai.js +172 -0
- package/dist/lib/telemetry/index.d.ts +1 -1
- package/dist/lib/telemetry/index.js +1 -1
- package/dist/lib/telemetry/tracers.d.ts +19 -0
- package/dist/lib/telemetry/tracers.js +19 -0
- package/dist/lib/telemetry/withSpan.d.ts +35 -0
- package/dist/lib/telemetry/withSpan.js +103 -0
- package/dist/lib/types/avatar.d.ts +143 -0
- package/dist/lib/types/avatar.js +20 -0
- package/dist/lib/types/cli.d.ts +6 -0
- package/dist/lib/types/conversation.d.ts +16 -0
- package/dist/lib/types/generate.d.ts +62 -5
- package/dist/lib/types/index.d.ts +5 -0
- package/dist/lib/types/index.js +7 -0
- package/dist/lib/types/middleware.d.ts +27 -0
- package/dist/lib/types/multimodal.d.ts +35 -2
- package/dist/lib/types/music.d.ts +165 -0
- package/dist/lib/types/music.js +21 -0
- package/dist/lib/types/providers.d.ts +144 -1
- package/dist/lib/types/replicate.d.ts +67 -0
- package/dist/lib/types/replicate.js +10 -0
- package/dist/lib/types/safeFetch.d.ts +15 -0
- package/dist/lib/types/safeFetch.js +7 -0
- package/dist/lib/types/stream.d.ts +2 -1
- package/dist/lib/types/tools.d.ts +13 -0
- package/dist/lib/types/video.d.ts +89 -0
- package/dist/lib/types/video.js +15 -0
- package/dist/lib/utils/avatarProcessor.d.ts +68 -0
- package/dist/lib/utils/avatarProcessor.js +172 -0
- package/dist/lib/utils/cloneOptions.d.ts +36 -0
- package/dist/lib/utils/cloneOptions.js +62 -0
- package/dist/lib/utils/lifecycleCallbacks.d.ts +51 -8
- package/dist/lib/utils/lifecycleCallbacks.js +82 -26
- package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
- package/dist/lib/utils/lifecycleTimeout.js +39 -0
- package/dist/lib/utils/logSanitize.d.ts +49 -0
- package/dist/lib/utils/logSanitize.js +170 -0
- package/dist/lib/utils/loggingFetch.d.ts +29 -0
- package/dist/lib/utils/loggingFetch.js +60 -0
- package/dist/lib/utils/messageBuilder.js +43 -25
- package/dist/lib/utils/modelChoices.js +236 -3
- package/dist/lib/utils/musicProcessor.d.ts +67 -0
- package/dist/lib/utils/musicProcessor.js +189 -0
- package/dist/lib/utils/optionsConversion.js +3 -2
- package/dist/lib/utils/parameterValidation.js +14 -4
- package/dist/lib/utils/pricing.js +193 -0
- package/dist/lib/utils/providerConfig.d.ts +55 -0
- package/dist/lib/utils/providerConfig.js +224 -0
- package/dist/lib/utils/safeFetch.d.ts +26 -0
- package/dist/lib/utils/safeFetch.js +83 -0
- package/dist/lib/utils/sizeGuard.d.ts +34 -0
- package/dist/lib/utils/sizeGuard.js +45 -0
- package/dist/lib/utils/ssrfGuard.d.ts +52 -0
- package/dist/lib/utils/ssrfGuard.js +411 -0
- package/dist/lib/utils/videoProcessor.d.ts +60 -0
- package/dist/lib/utils/videoProcessor.js +201 -0
- package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
- package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
- package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
- package/dist/middleware/builtin/lifecycle.js +39 -9
- package/dist/music/index.d.ts +13 -0
- package/dist/music/index.js +13 -0
- package/dist/music/providers/BeatovenMusic.d.ts +31 -0
- package/dist/music/providers/BeatovenMusic.js +333 -0
- package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
- package/dist/music/providers/ElevenLabsMusic.js +168 -0
- package/dist/music/providers/LyriaMusic.d.ts +29 -0
- package/dist/music/providers/LyriaMusic.js +172 -0
- package/dist/music/providers/ReplicateMusic.d.ts +31 -0
- package/dist/music/providers/ReplicateMusic.js +261 -0
- package/dist/neurolink.d.ts +30 -0
- package/dist/neurolink.js +323 -77
- package/dist/providers/amazonBedrock.d.ts +10 -0
- package/dist/providers/amazonBedrock.js +94 -39
- package/dist/providers/anthropic.js +55 -7
- package/dist/providers/anthropicBaseProvider.js +1 -1
- package/dist/providers/azureOpenai.js +66 -17
- package/dist/providers/cloudflare.d.ts +35 -0
- package/dist/providers/cloudflare.js +173 -0
- package/dist/providers/cohere.d.ts +52 -0
- package/dist/providers/cohere.js +252 -0
- package/dist/providers/deepseek.js +72 -17
- package/dist/providers/fireworks.d.ts +33 -0
- package/dist/providers/fireworks.js +163 -0
- package/dist/providers/googleAiStudio.js +126 -10
- package/dist/providers/googleNativeGemini3.d.ts +26 -6
- package/dist/providers/googleNativeGemini3.js +276 -29
- package/dist/providers/googleVertex.js +639 -181
- package/dist/providers/groq.d.ts +33 -0
- package/dist/providers/groq.js +180 -0
- package/dist/providers/huggingFace.js +9 -8
- package/dist/providers/ideogram.d.ts +34 -0
- package/dist/providers/ideogram.js +183 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.js +13 -0
- package/dist/providers/jina.d.ts +59 -0
- package/dist/providers/jina.js +217 -0
- package/dist/providers/llamaCpp.js +14 -46
- package/dist/providers/lmStudio.js +14 -47
- package/dist/providers/mistral.js +7 -7
- package/dist/providers/nvidiaNim.js +160 -19
- package/dist/providers/ollama.js +7 -7
- package/dist/providers/openAI.d.ts +22 -1
- package/dist/providers/openAI.js +181 -0
- package/dist/providers/openRouter.js +35 -23
- package/dist/providers/openaiCompatible.js +9 -8
- package/dist/providers/perplexity.d.ts +33 -0
- package/dist/providers/perplexity.js +178 -0
- package/dist/providers/recraft.d.ts +34 -0
- package/dist/providers/recraft.js +196 -0
- package/dist/providers/replicate.d.ts +75 -0
- package/dist/providers/replicate.js +402 -0
- package/dist/providers/stability.d.ts +37 -0
- package/dist/providers/stability.js +190 -0
- package/dist/providers/togetherAi.d.ts +33 -0
- package/dist/providers/togetherAi.js +175 -0
- package/dist/providers/voyage.d.ts +47 -0
- package/dist/providers/voyage.js +176 -0
- package/dist/providers/xai.d.ts +33 -0
- package/dist/providers/xai.js +171 -0
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/tracers.d.ts +19 -0
- package/dist/telemetry/tracers.js +19 -0
- package/dist/telemetry/withSpan.d.ts +35 -0
- package/dist/telemetry/withSpan.js +103 -0
- package/dist/types/avatar.d.ts +143 -0
- package/dist/types/avatar.js +19 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/conversation.d.ts +16 -0
- package/dist/types/generate.d.ts +62 -5
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +7 -0
- package/dist/types/middleware.d.ts +27 -0
- package/dist/types/multimodal.d.ts +35 -2
- package/dist/types/music.d.ts +165 -0
- package/dist/types/music.js +20 -0
- package/dist/types/providers.d.ts +144 -1
- package/dist/types/replicate.d.ts +67 -0
- package/dist/types/replicate.js +9 -0
- package/dist/types/safeFetch.d.ts +15 -0
- package/dist/types/safeFetch.js +6 -0
- package/dist/types/stream.d.ts +2 -1
- package/dist/types/tools.d.ts +13 -0
- package/dist/types/video.d.ts +89 -0
- package/dist/types/video.js +14 -0
- package/dist/utils/avatarProcessor.d.ts +68 -0
- package/dist/utils/avatarProcessor.js +171 -0
- package/dist/utils/cloneOptions.d.ts +36 -0
- package/dist/utils/cloneOptions.js +61 -0
- package/dist/utils/lifecycleCallbacks.d.ts +51 -8
- package/dist/utils/lifecycleCallbacks.js +82 -26
- package/dist/utils/lifecycleTimeout.d.ts +25 -0
- package/dist/utils/lifecycleTimeout.js +38 -0
- package/dist/utils/logSanitize.d.ts +49 -0
- package/dist/utils/logSanitize.js +169 -0
- package/dist/utils/loggingFetch.d.ts +29 -0
- package/dist/utils/loggingFetch.js +59 -0
- package/dist/utils/messageBuilder.js +43 -25
- package/dist/utils/modelChoices.js +236 -3
- package/dist/utils/musicProcessor.d.ts +67 -0
- package/dist/utils/musicProcessor.js +188 -0
- package/dist/utils/optionsConversion.js +3 -2
- package/dist/utils/parameterValidation.js +14 -4
- package/dist/utils/pricing.js +193 -0
- package/dist/utils/providerConfig.d.ts +55 -0
- package/dist/utils/providerConfig.js +224 -0
- package/dist/utils/safeFetch.d.ts +26 -0
- package/dist/utils/safeFetch.js +82 -0
- package/dist/utils/sizeGuard.d.ts +34 -0
- package/dist/utils/sizeGuard.js +44 -0
- package/dist/utils/ssrfGuard.d.ts +52 -0
- package/dist/utils/ssrfGuard.js +410 -0
- package/dist/utils/videoProcessor.d.ts +60 -0
- package/dist/utils/videoProcessor.js +200 -0
- package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
- package/dist/voice/providers/FishAudioTTS.js +182 -0
- package/dist/workflow/core/ensembleExecutor.js +26 -9
- package/package.json +32 -5
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replicate Prediction Lifecycle
|
|
3
|
+
*
|
|
4
|
+
* Shared async-job helpers used by every Replicate-backed handler. Submits
|
|
5
|
+
* a prediction, polls until terminal status, downloads the binary output.
|
|
6
|
+
*
|
|
7
|
+
* Usage pattern (for handlers — LLM, video, avatar, music):
|
|
8
|
+
*
|
|
9
|
+
* const auth = getReplicateAuth(credentials);
|
|
10
|
+
* if (!auth) throw new XxxError({ code: PROVIDER_NOT_CONFIGURED, ... });
|
|
11
|
+
* const prediction = await predict(auth, { model, input });
|
|
12
|
+
* const buffer = await downloadPredictionOutput(prediction);
|
|
13
|
+
*
|
|
14
|
+
* @module adapters/replicate/predictionLifecycle
|
|
15
|
+
*/
|
|
16
|
+
import { ErrorCategory, ErrorSeverity } from "../../constants/enums.js";
|
|
17
|
+
import { logger } from "../../utils/logger.js";
|
|
18
|
+
import { NeuroLinkError, ERROR_CODES } from "../../utils/errorHandling.js";
|
|
19
|
+
import { sanitizeForLog } from "../../utils/logSanitize.js";
|
|
20
|
+
import { safeDownload } from "../../utils/safeFetch.js";
|
|
21
|
+
import { MAX_VIDEO_BYTES } from "../../utils/sizeGuard.js";
|
|
22
|
+
// The submit path sends `Prefer: wait=60`, so Replicate can legitimately
|
|
23
|
+
// hold the connection for up to 60s while the prediction warms / runs.
|
|
24
|
+
// A 30s client-side timeout aborts in the middle of that window and
|
|
25
|
+
// produces spurious "submit timed out" errors on cold models (e.g. the
|
|
26
|
+
// MusicGen ones). Bump to 90s to cover the server window plus headroom.
|
|
27
|
+
const REQUEST_TIMEOUT_MS = 90_000;
|
|
28
|
+
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
29
|
+
const DEFAULT_TOTAL_TIMEOUT_MS = 5 * 60_000;
|
|
30
|
+
/**
|
|
31
|
+
* Submit a Replicate prediction. Uses `Prefer: wait=60` so quick
|
|
32
|
+
* predictions complete in the initial POST and skip polling entirely.
|
|
33
|
+
*/
|
|
34
|
+
export async function createPrediction(auth, input) {
|
|
35
|
+
const baseUrl = auth.baseUrl ?? "https://api.replicate.com";
|
|
36
|
+
const [modelPath, version] = input.model.split(":", 2);
|
|
37
|
+
const endpoint = version
|
|
38
|
+
? `${baseUrl}/v1/predictions`
|
|
39
|
+
: `${baseUrl}/v1/models/${modelPath}/predictions`;
|
|
40
|
+
const body = version
|
|
41
|
+
? { version, input: input.input }
|
|
42
|
+
: { input: input.input };
|
|
43
|
+
if (input.webhook) {
|
|
44
|
+
body.webhook = input.webhook;
|
|
45
|
+
}
|
|
46
|
+
if (input.webhookEventsFilter && input.webhookEventsFilter.length > 0) {
|
|
47
|
+
body.webhook_events_filter = input.webhookEventsFilter;
|
|
48
|
+
}
|
|
49
|
+
const controller = new AbortController();
|
|
50
|
+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
51
|
+
let response;
|
|
52
|
+
try {
|
|
53
|
+
response = await fetch(endpoint, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: {
|
|
56
|
+
Authorization: `Token ${auth.apiToken}`,
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
Prefer: "wait=60",
|
|
59
|
+
},
|
|
60
|
+
body: JSON.stringify(body),
|
|
61
|
+
signal: controller.signal,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof NeuroLinkError) {
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
69
|
+
throw new NeuroLinkError({
|
|
70
|
+
code: ERROR_CODES.OPERATION_ABORTED,
|
|
71
|
+
message: `Replicate predictions submit timed out after ${REQUEST_TIMEOUT_MS / 1000}s`,
|
|
72
|
+
category: ErrorCategory.TIMEOUT,
|
|
73
|
+
severity: ErrorSeverity.HIGH,
|
|
74
|
+
retriable: true,
|
|
75
|
+
originalError: err,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
clearTimeout(timeoutId);
|
|
82
|
+
}
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const raw = await response.text();
|
|
85
|
+
throw new NeuroLinkError({
|
|
86
|
+
code: ERROR_CODES.PROVIDER_NOT_AVAILABLE,
|
|
87
|
+
message: `Replicate predictions submit failed: ${response.status} — ${sanitizeForLog(raw, 500)}`,
|
|
88
|
+
category: ErrorCategory.NETWORK,
|
|
89
|
+
severity: ErrorSeverity.HIGH,
|
|
90
|
+
retriable: response.status >= 500,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return (await response.json());
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Poll a Replicate prediction until it reaches a terminal status
|
|
97
|
+
* (succeeded / failed / canceled) or the total timeout elapses.
|
|
98
|
+
*/
|
|
99
|
+
export async function pollPrediction(auth, predictionId, options = {}) {
|
|
100
|
+
const baseUrl = auth.baseUrl ?? "https://api.replicate.com";
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
const totalTimeout = options.timeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS;
|
|
103
|
+
const pollInterval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
104
|
+
while (Date.now() - startTime < totalTimeout) {
|
|
105
|
+
if (options.abortSignal?.aborted) {
|
|
106
|
+
throw new NeuroLinkError({
|
|
107
|
+
code: ERROR_CODES.OPERATION_ABORTED,
|
|
108
|
+
message: "Replicate poll aborted by caller",
|
|
109
|
+
category: ErrorCategory.ABORT,
|
|
110
|
+
severity: ErrorSeverity.LOW,
|
|
111
|
+
retriable: false,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const controller = new AbortController();
|
|
115
|
+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
116
|
+
// Forward caller abort into the in-flight fetch request.
|
|
117
|
+
const onCallerAbort = () => controller.abort();
|
|
118
|
+
options.abortSignal?.addEventListener("abort", onCallerAbort, {
|
|
119
|
+
once: true,
|
|
120
|
+
});
|
|
121
|
+
let response;
|
|
122
|
+
try {
|
|
123
|
+
response = await fetch(`${baseUrl}/v1/predictions/${predictionId}`, {
|
|
124
|
+
method: "GET",
|
|
125
|
+
headers: { Authorization: `Token ${auth.apiToken}` },
|
|
126
|
+
signal: controller.signal,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
if (err instanceof NeuroLinkError) {
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
134
|
+
// Distinguish caller abort from internal timeout abort.
|
|
135
|
+
if (options.abortSignal?.aborted) {
|
|
136
|
+
throw new NeuroLinkError({
|
|
137
|
+
code: ERROR_CODES.OPERATION_ABORTED,
|
|
138
|
+
message: "Replicate poll aborted by caller",
|
|
139
|
+
category: ErrorCategory.ABORT,
|
|
140
|
+
severity: ErrorSeverity.LOW,
|
|
141
|
+
retriable: false,
|
|
142
|
+
originalError: err,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
throw new NeuroLinkError({
|
|
146
|
+
code: ERROR_CODES.OPERATION_ABORTED,
|
|
147
|
+
message: `Replicate poll request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`,
|
|
148
|
+
category: ErrorCategory.TIMEOUT,
|
|
149
|
+
severity: ErrorSeverity.HIGH,
|
|
150
|
+
retriable: true,
|
|
151
|
+
originalError: err,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
options.abortSignal?.removeEventListener("abort", onCallerAbort);
|
|
158
|
+
clearTimeout(timeoutId);
|
|
159
|
+
}
|
|
160
|
+
if (!response.ok) {
|
|
161
|
+
const raw = await response.text();
|
|
162
|
+
throw new NeuroLinkError({
|
|
163
|
+
code: ERROR_CODES.PROVIDER_NOT_AVAILABLE,
|
|
164
|
+
message: `Replicate poll failed: ${response.status} — ${sanitizeForLog(raw, 500)}`,
|
|
165
|
+
category: ErrorCategory.NETWORK,
|
|
166
|
+
severity: ErrorSeverity.HIGH,
|
|
167
|
+
retriable: response.status >= 500,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const pred = (await response.json());
|
|
171
|
+
if (pred.status === "succeeded") {
|
|
172
|
+
return pred;
|
|
173
|
+
}
|
|
174
|
+
if (pred.status === "failed" || pred.status === "canceled") {
|
|
175
|
+
throw new NeuroLinkError({
|
|
176
|
+
code: ERROR_CODES.PROVIDER_NOT_AVAILABLE,
|
|
177
|
+
message: `Replicate prediction ${pred.id} ${pred.status}: ${pred.error ?? "unknown"}`,
|
|
178
|
+
category: ErrorCategory.EXECUTION,
|
|
179
|
+
severity: ErrorSeverity.HIGH,
|
|
180
|
+
retriable: pred.status === "failed",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// Abortable sleep: resolves after pollInterval but rejects immediately
|
|
184
|
+
// if the caller's AbortSignal fires, preventing up to pollInterval ms
|
|
185
|
+
// of unnecessary blocking.
|
|
186
|
+
await new Promise((resolve, reject) => {
|
|
187
|
+
const onAbort = () => {
|
|
188
|
+
clearTimeout(timer);
|
|
189
|
+
reject(new NeuroLinkError({
|
|
190
|
+
code: ERROR_CODES.OPERATION_ABORTED,
|
|
191
|
+
message: "Replicate poll aborted by caller",
|
|
192
|
+
category: ErrorCategory.ABORT,
|
|
193
|
+
severity: ErrorSeverity.LOW,
|
|
194
|
+
retriable: false,
|
|
195
|
+
}));
|
|
196
|
+
};
|
|
197
|
+
const timer = setTimeout(() => {
|
|
198
|
+
// Remove the abort listener when the timer fires normally so stale
|
|
199
|
+
// listeners do not accumulate across many poll iterations.
|
|
200
|
+
options.abortSignal?.removeEventListener("abort", onAbort);
|
|
201
|
+
resolve();
|
|
202
|
+
}, pollInterval);
|
|
203
|
+
options.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
throw new NeuroLinkError({
|
|
207
|
+
code: ERROR_CODES.OPERATION_ABORTED,
|
|
208
|
+
message: `Replicate prediction ${predictionId} did not complete within ${Math.round(totalTimeout / 1000)}s`,
|
|
209
|
+
category: ErrorCategory.TIMEOUT,
|
|
210
|
+
severity: ErrorSeverity.HIGH,
|
|
211
|
+
retriable: true,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Submit + poll. Combines `createPrediction` + `pollPrediction`.
|
|
216
|
+
*
|
|
217
|
+
* Uses `Prefer: wait=60` in the submit call so short jobs complete in the
|
|
218
|
+
* initial POST and bypass polling entirely.
|
|
219
|
+
*/
|
|
220
|
+
export async function predict(auth, input, options = {}) {
|
|
221
|
+
const submitted = await createPrediction(auth, input);
|
|
222
|
+
if (submitted.status === "succeeded") {
|
|
223
|
+
return submitted;
|
|
224
|
+
}
|
|
225
|
+
if (submitted.status === "failed" || submitted.status === "canceled") {
|
|
226
|
+
throw new NeuroLinkError({
|
|
227
|
+
code: ERROR_CODES.PROVIDER_NOT_AVAILABLE,
|
|
228
|
+
message: `Replicate prediction ${submitted.id} ${submitted.status} on submit: ${submitted.error ?? "unknown"}`,
|
|
229
|
+
category: ErrorCategory.EXECUTION,
|
|
230
|
+
severity: ErrorSeverity.HIGH,
|
|
231
|
+
retriable: submitted.status === "failed",
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return pollPrediction(auth, submitted.id, options);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Download a Replicate prediction's binary output.
|
|
238
|
+
*
|
|
239
|
+
* Replicate models return either a single URL string or an array of URL
|
|
240
|
+
* strings (multi-output models). For models that return base64 directly,
|
|
241
|
+
* use a model-specific helper instead.
|
|
242
|
+
*
|
|
243
|
+
* @param prediction The completed prediction to download.
|
|
244
|
+
* @param maxBytes Maximum bytes allowed. Defaults to {@link MAX_VIDEO_BYTES}
|
|
245
|
+
* (256 MiB). Pass {@link MAX_AUDIO_BYTES} for music/TTS
|
|
246
|
+
* outputs or {@link MAX_IMAGE_BYTES} for image outputs.
|
|
247
|
+
*/
|
|
248
|
+
export async function downloadPredictionOutput(prediction, maxBytes = MAX_VIDEO_BYTES) {
|
|
249
|
+
const output = prediction.output;
|
|
250
|
+
const url = Array.isArray(output) ? output[0] : output;
|
|
251
|
+
if (typeof url !== "string") {
|
|
252
|
+
throw new NeuroLinkError({
|
|
253
|
+
code: ERROR_CODES.PROVIDER_NOT_AVAILABLE,
|
|
254
|
+
message: `Replicate prediction ${prediction.id} output is not a URL: ${typeof output}`,
|
|
255
|
+
category: ErrorCategory.EXECUTION,
|
|
256
|
+
severity: ErrorSeverity.HIGH,
|
|
257
|
+
retriable: false,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const buffer = await safeDownload(url, {
|
|
262
|
+
maxBytes,
|
|
263
|
+
label: `Replicate prediction ${prediction.id}`,
|
|
264
|
+
timeoutMs: REQUEST_TIMEOUT_MS,
|
|
265
|
+
});
|
|
266
|
+
logger.debug(`[Replicate] Downloaded prediction ${prediction.id} output: ${buffer.length} bytes`);
|
|
267
|
+
return buffer;
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
if (err instanceof NeuroLinkError) {
|
|
271
|
+
throw err;
|
|
272
|
+
}
|
|
273
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
274
|
+
throw new NeuroLinkError({
|
|
275
|
+
code: ERROR_CODES.PROVIDER_NOT_AVAILABLE,
|
|
276
|
+
message: `Replicate output download failed: ${message} — ${url}`,
|
|
277
|
+
category: ErrorCategory.NETWORK,
|
|
278
|
+
severity: ErrorSeverity.HIGH,
|
|
279
|
+
retriable: true,
|
|
280
|
+
originalError: err instanceof Error ? err : undefined,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=predictionLifecycle.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kling Video Handler (PiAPI)
|
|
3
|
+
*
|
|
4
|
+
* Image-to-video generation via PiAPI's Kling endpoint. Async job model:
|
|
5
|
+
* POST /image-to-video → poll /task/{id} until completed.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: PiAPI Kling requires a publicly accessible image URL, not inline
|
|
8
|
+
* base64 data. Callers must supply `options.imageUrl` (a URL string) when
|
|
9
|
+
* using KlingVideoHandler. The `image` Buffer parameter is still accepted
|
|
10
|
+
* for interface compatibility (e.g., metadata / downstream use) but is not
|
|
11
|
+
* sent to the API. A clear error is thrown if no URL is provided.
|
|
12
|
+
*
|
|
13
|
+
* @module adapters/video/klingVideoHandler
|
|
14
|
+
* @see https://piapi.ai/docs/kling-api
|
|
15
|
+
*/
|
|
16
|
+
import type { VideoGenerationResult, VideoHandler, VideoOutputOptions } from "../../types/index.js";
|
|
17
|
+
/**
|
|
18
|
+
* Kling Video Handler.
|
|
19
|
+
*
|
|
20
|
+
* Auth: `Authorization: Bearer ${KLING_API_KEY}` (PiAPI / Kling key).
|
|
21
|
+
* Models: kling-1.6-i2v (default), kling-1.5-i2v, kling-1.0.
|
|
22
|
+
*/
|
|
23
|
+
export declare class KlingVideoHandler implements VideoHandler {
|
|
24
|
+
readonly maxDurationSeconds = 10;
|
|
25
|
+
readonly supportedAspectRatios: readonly ("9:16" | "16:9" | "1:1")[];
|
|
26
|
+
readonly supportedResolutions: readonly ("720p" | "1080p")[];
|
|
27
|
+
private readonly apiKey;
|
|
28
|
+
private readonly baseUrl;
|
|
29
|
+
constructor(apiKey?: string);
|
|
30
|
+
isConfigured(): boolean;
|
|
31
|
+
generate(image: Buffer, prompt: string, options: VideoOutputOptions): Promise<VideoGenerationResult>;
|
|
32
|
+
private submitJob;
|
|
33
|
+
private pollUntilComplete;
|
|
34
|
+
private downloadVideo;
|
|
35
|
+
private fetchWithTimeout;
|
|
36
|
+
private calculateDimensions;
|
|
37
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kling Video Handler (PiAPI)
|
|
3
|
+
*
|
|
4
|
+
* Image-to-video generation via PiAPI's Kling endpoint. Async job model:
|
|
5
|
+
* POST /image-to-video → poll /task/{id} until completed.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: PiAPI Kling requires a publicly accessible image URL, not inline
|
|
8
|
+
* base64 data. Callers must supply `options.imageUrl` (a URL string) when
|
|
9
|
+
* using KlingVideoHandler. The `image` Buffer parameter is still accepted
|
|
10
|
+
* for interface compatibility (e.g., metadata / downstream use) but is not
|
|
11
|
+
* sent to the API. A clear error is thrown if no URL is provided.
|
|
12
|
+
*
|
|
13
|
+
* @module adapters/video/klingVideoHandler
|
|
14
|
+
* @see https://piapi.ai/docs/kling-api
|
|
15
|
+
*/
|
|
16
|
+
import { ErrorCategory, ErrorSeverity } from "../../constants/enums.js";
|
|
17
|
+
import { VIDEO_ERROR_CODES } from "../../constants/videoErrors.js";
|
|
18
|
+
import { logger } from "../../utils/logger.js";
|
|
19
|
+
import { sanitizeForLog } from "../../utils/logSanitize.js";
|
|
20
|
+
import { safeDownload } from "../../utils/safeFetch.js";
|
|
21
|
+
import { VideoError } from "../../utils/videoProcessor.js";
|
|
22
|
+
import { MAX_VIDEO_BYTES } from "../../utils/sizeGuard.js";
|
|
23
|
+
const DEFAULT_BASE_URL = "https://api.piapi.ai/api/kling/v1";
|
|
24
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
25
|
+
const POLL_INTERVAL_MS = 5_000;
|
|
26
|
+
const TOTAL_TIMEOUT_MS = 5 * 60_000;
|
|
27
|
+
/**
|
|
28
|
+
* Kling Video Handler.
|
|
29
|
+
*
|
|
30
|
+
* Auth: `Authorization: Bearer ${KLING_API_KEY}` (PiAPI / Kling key).
|
|
31
|
+
* Models: kling-1.6-i2v (default), kling-1.5-i2v, kling-1.0.
|
|
32
|
+
*/
|
|
33
|
+
export class KlingVideoHandler {
|
|
34
|
+
maxDurationSeconds = 10;
|
|
35
|
+
supportedAspectRatios = ["16:9", "9:16", "1:1"];
|
|
36
|
+
supportedResolutions = [
|
|
37
|
+
"720p",
|
|
38
|
+
"1080p",
|
|
39
|
+
];
|
|
40
|
+
apiKey;
|
|
41
|
+
baseUrl;
|
|
42
|
+
constructor(apiKey) {
|
|
43
|
+
const resolved = (apiKey ?? process.env.KLING_API_KEY ?? "").trim();
|
|
44
|
+
this.apiKey = resolved.length > 0 ? resolved : null;
|
|
45
|
+
this.baseUrl = (process.env.KLING_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
46
|
+
}
|
|
47
|
+
isConfigured() {
|
|
48
|
+
return this.apiKey !== null;
|
|
49
|
+
}
|
|
50
|
+
async generate(image, prompt, options) {
|
|
51
|
+
if (!this.apiKey) {
|
|
52
|
+
throw new VideoError({
|
|
53
|
+
code: VIDEO_ERROR_CODES.PROVIDER_NOT_CONFIGURED,
|
|
54
|
+
message: "KLING_API_KEY not configured",
|
|
55
|
+
category: ErrorCategory.CONFIGURATION,
|
|
56
|
+
severity: ErrorSeverity.HIGH,
|
|
57
|
+
retriable: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!options.imageUrl) {
|
|
61
|
+
throw new VideoError({
|
|
62
|
+
code: VIDEO_ERROR_CODES.INVALID_INPUT,
|
|
63
|
+
message: "KlingVideoHandler requires a publicly accessible image URL. " +
|
|
64
|
+
"Pass options.imageUrl with a URL string pointing to the input image. " +
|
|
65
|
+
"The PiAPI Kling API does not accept inline base64 image data.",
|
|
66
|
+
category: ErrorCategory.CONFIGURATION,
|
|
67
|
+
severity: ErrorSeverity.HIGH,
|
|
68
|
+
retriable: false,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
const abortSignal = options.abortSignal;
|
|
73
|
+
// 1. Submit job.
|
|
74
|
+
const taskId = await this.submitJob(image, prompt, options);
|
|
75
|
+
// 2. Poll until complete.
|
|
76
|
+
const videoUrl = await this.pollUntilComplete(taskId, abortSignal);
|
|
77
|
+
// 3. Download video.
|
|
78
|
+
const buffer = await this.downloadVideo(videoUrl);
|
|
79
|
+
const processingTime = Date.now() - startTime;
|
|
80
|
+
logger.info(`[KlingVideoHandler] Generated ${buffer.length} bytes in ${processingTime}ms — task ${taskId}`);
|
|
81
|
+
return {
|
|
82
|
+
data: buffer,
|
|
83
|
+
mediaType: "video/mp4",
|
|
84
|
+
metadata: {
|
|
85
|
+
duration: options.length ?? 4,
|
|
86
|
+
dimensions: this.calculateDimensions(options),
|
|
87
|
+
model: options.model ?? "kling",
|
|
88
|
+
provider: "kling",
|
|
89
|
+
aspectRatio: options.aspectRatio ?? "16:9",
|
|
90
|
+
audioEnabled: false,
|
|
91
|
+
processingTime,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async submitJob(_image, prompt, options) {
|
|
96
|
+
// PiAPI Kling requires a publicly accessible URL for the input image.
|
|
97
|
+
// options.imageUrl is validated (non-null) before submitJob is called.
|
|
98
|
+
const body = {
|
|
99
|
+
model: options.model ?? "kling",
|
|
100
|
+
task_type: "video_generation",
|
|
101
|
+
input: {
|
|
102
|
+
image_url: options.imageUrl,
|
|
103
|
+
prompt,
|
|
104
|
+
duration: options.length ?? 4,
|
|
105
|
+
aspect_ratio: options.aspectRatio ?? "16:9",
|
|
106
|
+
cfg_scale: 0.5,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const response = await this.fetchWithTimeout(`${this.baseUrl}/image-to-video`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
113
|
+
"Content-Type": "application/json",
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify(body),
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
const raw = await response.text();
|
|
119
|
+
const retriable = response.status === 408 ||
|
|
120
|
+
response.status === 429 ||
|
|
121
|
+
response.status >= 500;
|
|
122
|
+
throw new VideoError({
|
|
123
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
124
|
+
message: `Kling submit failed: ${response.status} — ${sanitizeForLog(raw, 500)}`,
|
|
125
|
+
category: retriable ? ErrorCategory.NETWORK : ErrorCategory.EXECUTION,
|
|
126
|
+
severity: ErrorSeverity.HIGH,
|
|
127
|
+
retriable,
|
|
128
|
+
context: { status: response.status },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const json = (await response.json());
|
|
132
|
+
const taskId = json.task_id ?? json.data?.task_id;
|
|
133
|
+
if (!taskId) {
|
|
134
|
+
throw new VideoError({
|
|
135
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
136
|
+
message: "Kling submit response missing task_id",
|
|
137
|
+
category: ErrorCategory.EXECUTION,
|
|
138
|
+
severity: ErrorSeverity.HIGH,
|
|
139
|
+
retriable: false,
|
|
140
|
+
context: { response: json },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return taskId;
|
|
144
|
+
}
|
|
145
|
+
async pollUntilComplete(taskId, abortSignal) {
|
|
146
|
+
const startTime = Date.now();
|
|
147
|
+
while (Date.now() - startTime < TOTAL_TIMEOUT_MS) {
|
|
148
|
+
if (abortSignal?.aborted) {
|
|
149
|
+
throw new VideoError({
|
|
150
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
151
|
+
message: `Kling poll for task ${taskId} aborted by caller`,
|
|
152
|
+
category: ErrorCategory.NETWORK,
|
|
153
|
+
severity: ErrorSeverity.MEDIUM,
|
|
154
|
+
retriable: false,
|
|
155
|
+
context: { taskId },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const response = await this.fetchWithTimeout(`${this.baseUrl}/task/${taskId}`, {
|
|
159
|
+
method: "GET",
|
|
160
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
161
|
+
}, abortSignal);
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
const raw = await response.text();
|
|
164
|
+
throw new VideoError({
|
|
165
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
166
|
+
message: `Kling poll failed: ${response.status} — ${sanitizeForLog(raw, 500)}`,
|
|
167
|
+
category: ErrorCategory.NETWORK,
|
|
168
|
+
severity: ErrorSeverity.MEDIUM,
|
|
169
|
+
retriable: response.status >= 500,
|
|
170
|
+
context: { status: response.status, taskId },
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const data = (await response.json());
|
|
174
|
+
if (data.status === "completed" || data.status === "succeeded") {
|
|
175
|
+
const videoUrl = data.video_url ?? data.output?.video_url;
|
|
176
|
+
if (!videoUrl) {
|
|
177
|
+
throw new VideoError({
|
|
178
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
179
|
+
message: `Kling task ${taskId} completed but no video URL returned`,
|
|
180
|
+
category: ErrorCategory.EXECUTION,
|
|
181
|
+
severity: ErrorSeverity.HIGH,
|
|
182
|
+
retriable: false,
|
|
183
|
+
context: { taskId, data },
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return videoUrl;
|
|
187
|
+
}
|
|
188
|
+
if (data.status === "failed") {
|
|
189
|
+
throw new VideoError({
|
|
190
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
191
|
+
message: `Kling task ${taskId} failed: ${data.error ?? "unknown"}`,
|
|
192
|
+
category: ErrorCategory.EXECUTION,
|
|
193
|
+
severity: ErrorSeverity.HIGH,
|
|
194
|
+
retriable: false,
|
|
195
|
+
context: { taskId, data },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
// Abortable sleep.
|
|
199
|
+
await new Promise((resolve, reject) => {
|
|
200
|
+
const onAbort = () => {
|
|
201
|
+
clearTimeout(timer);
|
|
202
|
+
reject(new VideoError({
|
|
203
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
204
|
+
message: `Kling poll for task ${taskId} aborted by caller`,
|
|
205
|
+
category: ErrorCategory.NETWORK,
|
|
206
|
+
severity: ErrorSeverity.MEDIUM,
|
|
207
|
+
retriable: false,
|
|
208
|
+
context: { taskId },
|
|
209
|
+
}));
|
|
210
|
+
};
|
|
211
|
+
const timer = setTimeout(() => {
|
|
212
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
213
|
+
resolve();
|
|
214
|
+
}, POLL_INTERVAL_MS);
|
|
215
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
throw new VideoError({
|
|
219
|
+
code: VIDEO_ERROR_CODES.POLL_TIMEOUT,
|
|
220
|
+
message: `Kling task ${taskId} did not complete within ${TOTAL_TIMEOUT_MS / 1000}s`,
|
|
221
|
+
category: ErrorCategory.TIMEOUT,
|
|
222
|
+
severity: ErrorSeverity.MEDIUM,
|
|
223
|
+
retriable: true,
|
|
224
|
+
context: { taskId },
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async downloadVideo(url) {
|
|
228
|
+
try {
|
|
229
|
+
return await safeDownload(url, {
|
|
230
|
+
maxBytes: MAX_VIDEO_BYTES,
|
|
231
|
+
label: "Kling video",
|
|
232
|
+
timeoutMs: REQUEST_TIMEOUT_MS,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
throw new VideoError({
|
|
237
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
238
|
+
message: `Kling video download rejected: ${err instanceof Error ? err.message : String(err)}`,
|
|
239
|
+
category: ErrorCategory.NETWORK,
|
|
240
|
+
severity: ErrorSeverity.HIGH,
|
|
241
|
+
retriable: false,
|
|
242
|
+
context: { url },
|
|
243
|
+
originalError: err instanceof Error ? err : undefined,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async fetchWithTimeout(url, init, callerAbortSignal) {
|
|
248
|
+
const controller = new AbortController();
|
|
249
|
+
let timedOut = false;
|
|
250
|
+
const timeoutId = setTimeout(() => {
|
|
251
|
+
timedOut = true;
|
|
252
|
+
controller.abort();
|
|
253
|
+
}, REQUEST_TIMEOUT_MS);
|
|
254
|
+
const onCallerAbort = () => controller.abort();
|
|
255
|
+
callerAbortSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
256
|
+
try {
|
|
257
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
261
|
+
if (timedOut) {
|
|
262
|
+
throw new VideoError({
|
|
263
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
264
|
+
message: `Kling request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`,
|
|
265
|
+
category: ErrorCategory.NETWORK,
|
|
266
|
+
severity: ErrorSeverity.HIGH,
|
|
267
|
+
retriable: true,
|
|
268
|
+
originalError: err,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
throw new VideoError({
|
|
272
|
+
code: VIDEO_ERROR_CODES.GENERATION_FAILED,
|
|
273
|
+
message: "Kling request aborted by caller",
|
|
274
|
+
category: ErrorCategory.NETWORK,
|
|
275
|
+
severity: ErrorSeverity.MEDIUM,
|
|
276
|
+
retriable: false,
|
|
277
|
+
originalError: err,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
callerAbortSignal?.removeEventListener("abort", onCallerAbort);
|
|
284
|
+
clearTimeout(timeoutId);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
calculateDimensions(options) {
|
|
288
|
+
const aspectRatio = options.aspectRatio ?? "16:9";
|
|
289
|
+
const resolution = options.resolution ?? "720p";
|
|
290
|
+
if (resolution === "1080p") {
|
|
291
|
+
if (aspectRatio === "1:1") {
|
|
292
|
+
return { width: 1080, height: 1080 };
|
|
293
|
+
}
|
|
294
|
+
return aspectRatio === "9:16"
|
|
295
|
+
? { width: 1080, height: 1920 }
|
|
296
|
+
: { width: 1920, height: 1080 };
|
|
297
|
+
}
|
|
298
|
+
if (aspectRatio === "1:1") {
|
|
299
|
+
return { width: 720, height: 720 };
|
|
300
|
+
}
|
|
301
|
+
return aspectRatio === "9:16"
|
|
302
|
+
? { width: 720, height: 1280 }
|
|
303
|
+
: { width: 1280, height: 720 };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=klingVideoHandler.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replicate Video Handler
|
|
3
|
+
*
|
|
4
|
+
* Routes video generation through the universal Replicate prediction
|
|
5
|
+
* lifecycle. Default model is Wan-Alpha (RGBA video); callers can specify
|
|
6
|
+
* any image-to-video model on Replicate via `options.model`.
|
|
7
|
+
*
|
|
8
|
+
* @module adapters/video/replicateVideoHandler
|
|
9
|
+
* @see https://replicate.com/atonamy/wan-alpha
|
|
10
|
+
*/
|
|
11
|
+
import type { NeurolinkCredentials, VideoGenerationResult, VideoHandler, VideoOutputOptions } from "../../types/index.js";
|
|
12
|
+
/**
|
|
13
|
+
* Replicate Video Handler.
|
|
14
|
+
*
|
|
15
|
+
* Capabilities depend on the specific Replicate model — this handler
|
|
16
|
+
* advertises conservative bounds (any provider-supported aspect ratio /
|
|
17
|
+
* resolution; up to 10s typical for Wan-Alpha).
|
|
18
|
+
*/
|
|
19
|
+
export declare class ReplicateVideoHandler implements VideoHandler {
|
|
20
|
+
readonly maxDurationSeconds = 10;
|
|
21
|
+
readonly supportedAspectRatios: readonly ("9:16" | "16:9" | "1:1")[];
|
|
22
|
+
readonly supportedResolutions: readonly ("720p" | "1080p")[];
|
|
23
|
+
private readonly instanceCredentials;
|
|
24
|
+
constructor(credentials?: NeurolinkCredentials["replicate"]);
|
|
25
|
+
isConfigured(): boolean;
|
|
26
|
+
generate(image: Buffer, prompt: string, options: VideoOutputOptions): Promise<VideoGenerationResult>;
|
|
27
|
+
private detectImageType;
|
|
28
|
+
private calculateDimensions;
|
|
29
|
+
}
|