@use-lattice/litmus 0.121.3
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/LICENSE +19 -0
- package/dist/src/accounts-Bt1oJb1Z.cjs +219 -0
- package/dist/src/accounts-DjOU8Rm3.js +178 -0
- package/dist/src/agentic-utils-D03IiXQc.js +153 -0
- package/dist/src/agentic-utils-Dh7xaMQM.cjs +180 -0
- package/dist/src/agents-C6BIMlZa.js +231 -0
- package/dist/src/agents-DvIpNX1L.cjs +666 -0
- package/dist/src/agents-ZP0RP9vV.cjs +231 -0
- package/dist/src/agents-maJXdjbR.js +665 -0
- package/dist/src/aimlapi-BTbQjG2E.cjs +30 -0
- package/dist/src/aimlapi-CwMxqfXP.js +30 -0
- package/dist/src/audio-BBUdvsde.cjs +97 -0
- package/dist/src/audio-D5DPZ7I-.js +97 -0
- package/dist/src/base-BEysXrkq.cjs +222 -0
- package/dist/src/base-C451JQfq.js +193 -0
- package/dist/src/blobs-BY8MDmpo.js +230 -0
- package/dist/src/blobs-BgcNn97m.cjs +256 -0
- package/dist/src/cache-BBE_lsTA.cjs +4 -0
- package/dist/src/cache-BkrqU5Ba.js +237 -0
- package/dist/src/cache-DsCxFlsZ.cjs +297 -0
- package/dist/src/chat-CPJWDP6a.cjs +289 -0
- package/dist/src/chat-CXX3xzkk.cjs +811 -0
- package/dist/src/chat-CcDgZFJ4.js +787 -0
- package/dist/src/chat-Dz5ZeGO2.js +289 -0
- package/dist/src/chatkit-Dw0mKkML.cjs +1158 -0
- package/dist/src/chatkit-swAIVuea.js +1157 -0
- package/dist/src/chunk-DEq-mXcV.js +15 -0
- package/dist/src/claude-agent-sdk-BXZJtOg6.js +379 -0
- package/dist/src/claude-agent-sdk-CkfyjDoG.cjs +383 -0
- package/dist/src/cloudflare-ai-BzpJcqUH.js +161 -0
- package/dist/src/cloudflare-ai-Cmy_R1y2.cjs +161 -0
- package/dist/src/cloudflare-gateway-B9tVQKok.cjs +272 -0
- package/dist/src/cloudflare-gateway-DrD3ew3H.js +272 -0
- package/dist/src/codex-sdk-Dezj9Nwm.js +1056 -0
- package/dist/src/codex-sdk-Dl9D4k5B.cjs +1060 -0
- package/dist/src/cometapi-C-9YvCHC.js +54 -0
- package/dist/src/cometapi-DHgDKoO2.cjs +54 -0
- package/dist/src/completion-B8Ctyxpr.js +120 -0
- package/dist/src/completion-Cxrt08sj.cjs +131 -0
- package/dist/src/createHash-BwgE13yv.cjs +27 -0
- package/dist/src/createHash-DmPQkvBh.js +15 -0
- package/dist/src/docker-BiqcTwLv.js +80 -0
- package/dist/src/docker-C7tEJnP-.cjs +80 -0
- package/dist/src/esm-C62Zofr1.cjs +409 -0
- package/dist/src/esm-DMVc93eh.js +379 -0
- package/dist/src/evalResult-C3NJPQOo.cjs +301 -0
- package/dist/src/evalResult-C7JJAPBb.js +295 -0
- package/dist/src/evalResult-DoVTZZWI.cjs +2 -0
- package/dist/src/extractor-DnMD3fwt.cjs +391 -0
- package/dist/src/extractor-DtlL28vL.js +374 -0
- package/dist/src/fetch-BTxakTSg.cjs +1133 -0
- package/dist/src/fetch-DQckpUFz.js +928 -0
- package/dist/src/fileExtensions-DnqA1y9x.js +85 -0
- package/dist/src/fileExtensions-bYh77CN8.cjs +114 -0
- package/dist/src/genaiTracer-CyZrmaK0.cjs +268 -0
- package/dist/src/genaiTracer-D3fD9dNV.js +256 -0
- package/dist/src/graders-BNscxFrU.js +13644 -0
- package/dist/src/graders-D2oE9Msq.js +2 -0
- package/dist/src/graders-c0Ez_w-9.cjs +2 -0
- package/dist/src/graders-d0F2M3e9.cjs +14056 -0
- package/dist/src/image-0ZhE0VlR.cjs +280 -0
- package/dist/src/image-CWE1pdNv.js +257 -0
- package/dist/src/image-D9ZK6hwL.js +163 -0
- package/dist/src/image-DKZgZITg.cjs +163 -0
- package/dist/src/index.cjs +11366 -0
- package/dist/src/index.d.cts +19640 -0
- package/dist/src/index.d.ts +19641 -0
- package/dist/src/index.js +11306 -0
- package/dist/src/invariant-Ddh24eXh.js +25 -0
- package/dist/src/invariant-kfQ8Bu82.cjs +30 -0
- package/dist/src/knowledgeBase-BgPyGFUd.cjs +122 -0
- package/dist/src/knowledgeBase-DyHilYaP.js +122 -0
- package/dist/src/litellm-CyMeneHS.js +135 -0
- package/dist/src/litellm-DWDF73yF.cjs +135 -0
- package/dist/src/logger-C40ZGil9.js +717 -0
- package/dist/src/logger-DyfK9PBt.cjs +917 -0
- package/dist/src/luma-ray-BAU9X_ep.cjs +315 -0
- package/dist/src/luma-ray-nwVseBbv.js +313 -0
- package/dist/src/messages-B5ADWTTv.js +245 -0
- package/dist/src/messages-BCnZfqrS.cjs +257 -0
- package/dist/src/meteor-DLZZ3osF.cjs +134 -0
- package/dist/src/meteor-DUiCJRC-.js +134 -0
- package/dist/src/modelslab-00cveB8L.cjs +163 -0
- package/dist/src/modelslab-D9sCU_L7.js +163 -0
- package/dist/src/nova-reel-CTapvqYH.js +276 -0
- package/dist/src/nova-reel-DlWuuroF.cjs +278 -0
- package/dist/src/nova-sonic-5UPWfeMv.cjs +363 -0
- package/dist/src/nova-sonic-BhSwQNym.js +363 -0
- package/dist/src/openai-BWrJK9d8.cjs +52 -0
- package/dist/src/openai-DumO8WQn.js +47 -0
- package/dist/src/openclaw-B8brrjC_.cjs +577 -0
- package/dist/src/openclaw-Bkayww9q.js +571 -0
- package/dist/src/opencode-sdk-7xjoDNiM.cjs +562 -0
- package/dist/src/opencode-sdk-SGwAPxht.js +558 -0
- package/dist/src/otlpReceiver-CoAHfAN9.cjs +15 -0
- package/dist/src/otlpReceiver-oO3EQwI9.js +14 -0
- package/dist/src/providerRegistry-4yjhaEM8.js +45 -0
- package/dist/src/providerRegistry-DhV4rJIc.cjs +50 -0
- package/dist/src/providers-B5RJVG-7.cjs +33609 -0
- package/dist/src/providers-BdmZCLzV.js +33262 -0
- package/dist/src/providers-CxtRxn8e.js +2 -0
- package/dist/src/providers-DnQLNbx1.cjs +3 -0
- package/dist/src/pythonUtils-BD0druiM.cjs +275 -0
- package/dist/src/pythonUtils-IBhn5YGR.js +249 -0
- package/dist/src/quiverai-BDOwZBsM.cjs +213 -0
- package/dist/src/quiverai-D3JTF5lD.js +213 -0
- package/dist/src/responses-B2LCDCXZ.js +667 -0
- package/dist/src/responses-BvNm4Xv9.cjs +685 -0
- package/dist/src/rubyUtils-B0NwnfpY.cjs +245 -0
- package/dist/src/rubyUtils-BroxzZ7c.cjs +2 -0
- package/dist/src/rubyUtils-hqVw5UvJ.js +222 -0
- package/dist/src/sagemaker-Cno2V-Sx.js +689 -0
- package/dist/src/sagemaker-fV_KUgs5.cjs +691 -0
- package/dist/src/server-BOuAXb06.cjs +238 -0
- package/dist/src/server-CtI-EWzm.cjs +2 -0
- package/dist/src/server-Cy3DZymt.js +189 -0
- package/dist/src/slack-CP8xBePa.js +135 -0
- package/dist/src/slack-DSQ1yXVb.cjs +135 -0
- package/dist/src/store-BwDDaBjb.cjs +246 -0
- package/dist/src/store-DcbLC593.cjs +2 -0
- package/dist/src/store-IGpqMIkv.js +240 -0
- package/dist/src/tables-3Q2cL7So.cjs +373 -0
- package/dist/src/tables-Bi2fjr4W.js +288 -0
- package/dist/src/telemetry-Bg2WqF79.js +161 -0
- package/dist/src/telemetry-D0x6u5kX.cjs +166 -0
- package/dist/src/telemetry-DXNimrI0.cjs +2 -0
- package/dist/src/text-B_UCRPp2.js +22 -0
- package/dist/src/text-CW1cyrwj.cjs +33 -0
- package/dist/src/tokenUsageUtils-NYT-WKS6.js +138 -0
- package/dist/src/tokenUsageUtils-bVa1ga6f.cjs +173 -0
- package/dist/src/transcription-Cl_W16Pr.js +122 -0
- package/dist/src/transcription-yt1EecY8.cjs +124 -0
- package/dist/src/transform-BCtGrl_W.cjs +228 -0
- package/dist/src/transform-Bv6gG2MJ.cjs +1688 -0
- package/dist/src/transform-CY1wbpRy.js +1507 -0
- package/dist/src/transform-DU8rUL9P.cjs +2 -0
- package/dist/src/transform-yWaShiKr.js +216 -0
- package/dist/src/transformersAvailability-BGkzavwb.js +35 -0
- package/dist/src/transformersAvailability-DKoRtQLy.cjs +35 -0
- package/dist/src/types-5aqHpBwE.cjs +3769 -0
- package/dist/src/types-Bn6D9c4U.js +3300 -0
- package/dist/src/util-BkKlTkI2.js +293 -0
- package/dist/src/util-CTh0bfOm.cjs +1119 -0
- package/dist/src/util-D17oBwo7.cjs +328 -0
- package/dist/src/util-DsS_-v4p.js +613 -0
- package/dist/src/util-DuntT1Ga.js +951 -0
- package/dist/src/util-aWjdCYMI.cjs +667 -0
- package/dist/src/utils-CisQwpjA.js +94 -0
- package/dist/src/utils-yWamDvmz.cjs +123 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/drizzle/0000_lush_hellion.sql +36 -0
- package/drizzle/0001_wide_calypso.sql +3 -0
- package/drizzle/0002_tidy_juggernaut.sql +1 -0
- package/drizzle/0003_lively_naoko.sql +8 -0
- package/drizzle/0004_minor_peter_quill.sql +19 -0
- package/drizzle/0005_silky_millenium_guard.sql +2 -0
- package/drizzle/0006_harsh_caretaker.sql +42 -0
- package/drizzle/0007_cloudy_wong.sql +1 -0
- package/drizzle/0008_broad_boomer.sql +2 -0
- package/drizzle/0009_strong_marten_broadcloak.sql +19 -0
- package/drizzle/0010_needy_bishop.sql +11 -0
- package/drizzle/0011_moaning_millenium_guard.sql +1 -0
- package/drizzle/0012_late_marten_broadcloak.sql +2 -0
- package/drizzle/0013_previous_dormammu.sql +9 -0
- package/drizzle/0014_lazy_captain_universe.sql +2 -0
- package/drizzle/0015_zippy_wallop.sql +29 -0
- package/drizzle/0016_jazzy_zemo.sql +2 -0
- package/drizzle/0017_reflective_praxagora.sql +4 -0
- package/drizzle/0018_fat_vanisher.sql +22 -0
- package/drizzle/0019_new_clint_barton.sql +8 -0
- package/drizzle/0020_skinny_maverick.sql +1 -0
- package/drizzle/0021_mysterious_madelyne_pryor.sql +13 -0
- package/drizzle/0022_sleepy_ultimo.sql +25 -0
- package/drizzle/0023_wooden_mandrill.sql +2 -0
- package/drizzle/AGENTS.md +68 -0
- package/drizzle/CLAUDE.md +1 -0
- package/drizzle/meta/0000_snapshot.json +221 -0
- package/drizzle/meta/0001_snapshot.json +214 -0
- package/drizzle/meta/0002_snapshot.json +221 -0
- package/drizzle/meta/0005_snapshot.json +369 -0
- package/drizzle/meta/0006_snapshot.json +638 -0
- package/drizzle/meta/0007_snapshot.json +640 -0
- package/drizzle/meta/0008_snapshot.json +649 -0
- package/drizzle/meta/0009_snapshot.json +554 -0
- package/drizzle/meta/0010_snapshot.json +619 -0
- package/drizzle/meta/0011_snapshot.json +627 -0
- package/drizzle/meta/0012_snapshot.json +639 -0
- package/drizzle/meta/0013_snapshot.json +717 -0
- package/drizzle/meta/0014_snapshot.json +717 -0
- package/drizzle/meta/0015_snapshot.json +897 -0
- package/drizzle/meta/0016_snapshot.json +1031 -0
- package/drizzle/meta/0018_snapshot.json +1210 -0
- package/drizzle/meta/0019_snapshot.json +1165 -0
- package/drizzle/meta/0020_snapshot.json +1232 -0
- package/drizzle/meta/0021_snapshot.json +1311 -0
- package/drizzle/meta/0022_snapshot.json +1481 -0
- package/drizzle/meta/0023_snapshot.json +1496 -0
- package/drizzle/meta/_journal.json +174 -0
- package/package.json +240 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
const require_logger = require("./logger-DyfK9PBt.cjs");
|
|
2
|
+
const require_fetch = require("./fetch-BTxakTSg.cjs");
|
|
3
|
+
const require_blobs = require("./blobs-BgcNn97m.cjs");
|
|
4
|
+
const require_text = require("./text-CW1cyrwj.cjs");
|
|
5
|
+
const require_base = require("./base-BEysXrkq.cjs");
|
|
6
|
+
let fs = require("fs");
|
|
7
|
+
fs = require_logger.__toESM(fs);
|
|
8
|
+
let path = require("path");
|
|
9
|
+
path = require_logger.__toESM(path);
|
|
10
|
+
//#region src/providers/bedrock/luma-ray.ts
|
|
11
|
+
/**
|
|
12
|
+
* Luma Ray 2 Video Generation Provider
|
|
13
|
+
*
|
|
14
|
+
* Supports text-to-video and image-to-video generation using AWS Bedrock's
|
|
15
|
+
* async invoke API. Videos can be 5 or 9 seconds with various aspect ratios.
|
|
16
|
+
*/
|
|
17
|
+
const MODEL_ID = "luma.ray-v2:0";
|
|
18
|
+
const DEFAULT_DURATION = "5s";
|
|
19
|
+
const DEFAULT_RESOLUTION = "720p";
|
|
20
|
+
const DEFAULT_ASPECT_RATIO = "16:9";
|
|
21
|
+
const DEFAULT_POLL_INTERVAL_MS = 1e4;
|
|
22
|
+
const DEFAULT_MAX_POLL_TIME_MS = 6e5;
|
|
23
|
+
const VALID_ASPECT_RATIOS = [
|
|
24
|
+
"1:1",
|
|
25
|
+
"16:9",
|
|
26
|
+
"9:16",
|
|
27
|
+
"4:3",
|
|
28
|
+
"3:4",
|
|
29
|
+
"21:9",
|
|
30
|
+
"9:21"
|
|
31
|
+
];
|
|
32
|
+
const VALID_DURATIONS = ["5s", "9s"];
|
|
33
|
+
const VALID_RESOLUTIONS = ["540p", "720p"];
|
|
34
|
+
var LumaRayVideoProvider = class extends require_base.AwsBedrockGenericProvider {
|
|
35
|
+
videoConfig;
|
|
36
|
+
providerId;
|
|
37
|
+
constructor(modelName = MODEL_ID, options = {}) {
|
|
38
|
+
super(modelName, options);
|
|
39
|
+
this.videoConfig = options.config || {};
|
|
40
|
+
this.providerId = options.id;
|
|
41
|
+
}
|
|
42
|
+
id() {
|
|
43
|
+
return this.providerId || `bedrock:video:${this.modelName}`;
|
|
44
|
+
}
|
|
45
|
+
toString() {
|
|
46
|
+
return `[Luma Ray 2 Video Provider ${this.modelName}]`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load image data from file:// path or return as-is if base64
|
|
50
|
+
*/
|
|
51
|
+
loadImageData(imagePath) {
|
|
52
|
+
if (imagePath.startsWith("file://")) {
|
|
53
|
+
const filePath = imagePath.slice(7);
|
|
54
|
+
const resolvedPath = path.resolve(filePath);
|
|
55
|
+
if (filePath.includes("..") && resolvedPath !== path.resolve(path.normalize(filePath))) return { error: `Invalid image path (path traversal detected): ${filePath}` };
|
|
56
|
+
if (!fs.existsSync(resolvedPath)) return { error: `Image file not found: ${resolvedPath}` };
|
|
57
|
+
return { data: fs.readFileSync(resolvedPath).toString("base64") };
|
|
58
|
+
}
|
|
59
|
+
return { data: imagePath };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Detect image format from path or data
|
|
63
|
+
*/
|
|
64
|
+
detectImageFormat(imagePath) {
|
|
65
|
+
const lowerPath = imagePath.toLowerCase();
|
|
66
|
+
if (lowerPath.includes(".png") || lowerPath.startsWith("ivborw")) return "image/png";
|
|
67
|
+
return "image/jpeg";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build a keyframe from image path
|
|
71
|
+
*/
|
|
72
|
+
buildKeyframe(imagePath) {
|
|
73
|
+
const { data, error } = this.loadImageData(imagePath);
|
|
74
|
+
if (error || !data) return { error: error || "Failed to load image" };
|
|
75
|
+
return { keyframe: {
|
|
76
|
+
type: "image",
|
|
77
|
+
source: {
|
|
78
|
+
type: "base64",
|
|
79
|
+
media_type: this.detectImageFormat(imagePath),
|
|
80
|
+
data
|
|
81
|
+
}
|
|
82
|
+
} };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build keyframes from config convenience properties
|
|
86
|
+
*/
|
|
87
|
+
buildKeyframes(config) {
|
|
88
|
+
if (config.keyframes) return { keyframes: config.keyframes };
|
|
89
|
+
const keyframes = {};
|
|
90
|
+
if (config.startImage) {
|
|
91
|
+
const { keyframe, error } = this.buildKeyframe(config.startImage);
|
|
92
|
+
if (error) return { error: `Start image error: ${error}` };
|
|
93
|
+
keyframes.frame0 = keyframe;
|
|
94
|
+
}
|
|
95
|
+
if (config.endImage) {
|
|
96
|
+
const { keyframe, error } = this.buildKeyframe(config.endImage);
|
|
97
|
+
if (error) return { error: `End image error: ${error}` };
|
|
98
|
+
keyframes.frame1 = keyframe;
|
|
99
|
+
}
|
|
100
|
+
if (Object.keys(keyframes).length === 0) return {};
|
|
101
|
+
return { keyframes };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build model input for Luma Ray
|
|
105
|
+
*/
|
|
106
|
+
buildModelInput(prompt, config) {
|
|
107
|
+
const aspectRatio = config.aspectRatio || DEFAULT_ASPECT_RATIO;
|
|
108
|
+
if (!VALID_ASPECT_RATIOS.includes(aspectRatio)) return { error: `Invalid aspect_ratio: ${aspectRatio}. Must be one of: ${VALID_ASPECT_RATIOS.join(", ")}` };
|
|
109
|
+
const duration = config.duration || DEFAULT_DURATION;
|
|
110
|
+
if (!VALID_DURATIONS.includes(duration)) return { error: `Invalid duration: ${duration}. Must be one of: ${VALID_DURATIONS.join(", ")}` };
|
|
111
|
+
const resolution = config.resolution || DEFAULT_RESOLUTION;
|
|
112
|
+
if (!VALID_RESOLUTIONS.includes(resolution)) return { error: `Invalid resolution: ${resolution}. Must be one of: ${VALID_RESOLUTIONS.join(", ")}` };
|
|
113
|
+
const { keyframes, error: keyframeError } = this.buildKeyframes(config);
|
|
114
|
+
if (keyframeError) return { error: keyframeError };
|
|
115
|
+
const modelInput = {
|
|
116
|
+
prompt,
|
|
117
|
+
aspect_ratio: aspectRatio,
|
|
118
|
+
duration,
|
|
119
|
+
resolution
|
|
120
|
+
};
|
|
121
|
+
if (config.loop !== void 0) modelInput.loop = config.loop;
|
|
122
|
+
if (keyframes) modelInput.keyframes = keyframes;
|
|
123
|
+
return { input: modelInput };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Start async video generation job
|
|
127
|
+
*/
|
|
128
|
+
async startVideoGeneration(modelInput, s3OutputUri) {
|
|
129
|
+
try {
|
|
130
|
+
const { BedrockRuntimeClient, StartAsyncInvokeCommand } = await import("@aws-sdk/client-bedrock-runtime");
|
|
131
|
+
const credentials = await this.getCredentials();
|
|
132
|
+
const client = new BedrockRuntimeClient({
|
|
133
|
+
region: this.getRegion(),
|
|
134
|
+
...credentials ? { credentials } : {}
|
|
135
|
+
});
|
|
136
|
+
const command = new StartAsyncInvokeCommand({
|
|
137
|
+
modelId: this.modelName,
|
|
138
|
+
modelInput,
|
|
139
|
+
outputDataConfig: { s3OutputDataConfig: { s3Uri: s3OutputUri } }
|
|
140
|
+
});
|
|
141
|
+
return { invocationArn: (await client.send(command)).invocationArn };
|
|
142
|
+
} catch (err) {
|
|
143
|
+
const error = err;
|
|
144
|
+
require_logger.logger.error("[Luma Ray] Failed to start video generation", { error });
|
|
145
|
+
return { error: `Failed to start video generation: ${error.message || String(err)}` };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Poll for job completion
|
|
150
|
+
*/
|
|
151
|
+
async pollForCompletion(invocationArn, pollIntervalMs, maxPollTimeMs) {
|
|
152
|
+
const startTime = Date.now();
|
|
153
|
+
try {
|
|
154
|
+
const { BedrockRuntimeClient, GetAsyncInvokeCommand } = await import("@aws-sdk/client-bedrock-runtime");
|
|
155
|
+
const credentials = await this.getCredentials();
|
|
156
|
+
const client = new BedrockRuntimeClient({
|
|
157
|
+
region: this.getRegion(),
|
|
158
|
+
...credentials ? { credentials } : {}
|
|
159
|
+
});
|
|
160
|
+
while (Date.now() - startTime < maxPollTimeMs) {
|
|
161
|
+
const command = new GetAsyncInvokeCommand({ invocationArn });
|
|
162
|
+
const invocation = await client.send(command);
|
|
163
|
+
require_logger.logger.debug(`[Luma Ray] Job status: ${invocation.status}`, {
|
|
164
|
+
invocationArn,
|
|
165
|
+
elapsedMs: Date.now() - startTime
|
|
166
|
+
});
|
|
167
|
+
if (invocation.status === "Completed") return { response: {
|
|
168
|
+
invocationArn: invocation.invocationArn || invocationArn,
|
|
169
|
+
status: "Completed",
|
|
170
|
+
submitTime: invocation.submitTime?.toISOString(),
|
|
171
|
+
endTime: invocation.endTime?.toISOString(),
|
|
172
|
+
outputDataConfig: invocation.outputDataConfig
|
|
173
|
+
} };
|
|
174
|
+
if (invocation.status === "Failed") return { error: `Video generation failed: ${invocation.failureMessage}` };
|
|
175
|
+
await require_fetch.sleep(pollIntervalMs);
|
|
176
|
+
}
|
|
177
|
+
return { error: `Video generation timed out after ${maxPollTimeMs / 1e3} seconds` };
|
|
178
|
+
} catch (err) {
|
|
179
|
+
const error = err;
|
|
180
|
+
require_logger.logger.error("[Luma Ray] Polling error", {
|
|
181
|
+
error,
|
|
182
|
+
invocationArn
|
|
183
|
+
});
|
|
184
|
+
return { error: `Polling error: ${error.message || String(err)}` };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Download video from S3 and store to blob storage
|
|
189
|
+
*/
|
|
190
|
+
async downloadAndStoreVideo(s3Uri) {
|
|
191
|
+
try {
|
|
192
|
+
const match = s3Uri.match(/^s3:\/\/([^/]+)\/(.+)$/);
|
|
193
|
+
if (!match) return { error: `Invalid S3 URI: ${s3Uri}` };
|
|
194
|
+
const [, bucket, keyPrefix] = match;
|
|
195
|
+
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
196
|
+
const credentials = await this.getCredentials();
|
|
197
|
+
const s3 = new S3Client({
|
|
198
|
+
region: this.getRegion(),
|
|
199
|
+
...credentials ? { credentials } : {}
|
|
200
|
+
});
|
|
201
|
+
const videoKey = keyPrefix.endsWith("/") ? `${keyPrefix}output.mp4` : `${keyPrefix}/output.mp4`;
|
|
202
|
+
require_logger.logger.debug("[Luma Ray] Downloading video from S3", {
|
|
203
|
+
bucket,
|
|
204
|
+
key: videoKey
|
|
205
|
+
});
|
|
206
|
+
const response = await s3.send(new GetObjectCommand({
|
|
207
|
+
Bucket: bucket,
|
|
208
|
+
Key: videoKey
|
|
209
|
+
}));
|
|
210
|
+
if (!response.Body) return { error: "Empty response from S3" };
|
|
211
|
+
const { ref } = await require_blobs.storeBlob(Buffer.from(await response.Body.transformToByteArray()), "video/mp4", {
|
|
212
|
+
kind: "video",
|
|
213
|
+
location: "response.video"
|
|
214
|
+
});
|
|
215
|
+
require_logger.logger.debug("[Luma Ray] Stored video to blob storage", {
|
|
216
|
+
uri: ref.uri,
|
|
217
|
+
hash: ref.hash
|
|
218
|
+
});
|
|
219
|
+
return { blobRef: ref };
|
|
220
|
+
} catch (err) {
|
|
221
|
+
const error = err;
|
|
222
|
+
require_logger.logger.error("[Luma Ray] S3 download error", {
|
|
223
|
+
error,
|
|
224
|
+
s3Uri
|
|
225
|
+
});
|
|
226
|
+
if (error.name === "MODULE_NOT_FOUND" || String(err).includes("Cannot find module")) return { error: "The @aws-sdk/client-s3 package is required for Luma Ray video downloads. Install it with: npm install @aws-sdk/client-s3" };
|
|
227
|
+
return { error: `S3 download error: ${error.message || String(err)}` };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get video dimensions from aspect ratio
|
|
232
|
+
*/
|
|
233
|
+
getVideoDimensions(aspectRatio, resolution) {
|
|
234
|
+
const height = resolution === "540p" ? 540 : 720;
|
|
235
|
+
const aspectParts = aspectRatio.split(":").map(Number);
|
|
236
|
+
return `${Math.round(height * aspectParts[0] / aspectParts[1])}x${height}`;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get duration in seconds from duration string
|
|
240
|
+
*/
|
|
241
|
+
getDurationSeconds(duration) {
|
|
242
|
+
return duration === "9s" ? 9 : 5;
|
|
243
|
+
}
|
|
244
|
+
async callApi(prompt, context) {
|
|
245
|
+
const s3OutputUri = this.videoConfig.s3OutputUri;
|
|
246
|
+
if (!s3OutputUri) return { error: "Luma Ray requires s3OutputUri in provider config. Example: s3://my-bucket/videos/" };
|
|
247
|
+
if (!s3OutputUri.startsWith("s3://")) return { error: `Invalid s3OutputUri: ${s3OutputUri}. Must start with s3://` };
|
|
248
|
+
if (!prompt || prompt.trim() === "") return { error: "Prompt is required for video generation" };
|
|
249
|
+
if (prompt.length > 5e3) return { error: `Prompt exceeds 5000 character limit. Got: ${prompt.length}` };
|
|
250
|
+
const config = {
|
|
251
|
+
...this.videoConfig,
|
|
252
|
+
...context?.prompt?.config
|
|
253
|
+
};
|
|
254
|
+
const startTime = Date.now();
|
|
255
|
+
const { input: modelInput, error: buildError } = this.buildModelInput(prompt, config);
|
|
256
|
+
if (buildError || !modelInput) return { error: buildError || "Failed to build model input" };
|
|
257
|
+
require_logger.logger.info("[Luma Ray] Starting video generation job...", {
|
|
258
|
+
duration: config.duration || DEFAULT_DURATION,
|
|
259
|
+
resolution: config.resolution || DEFAULT_RESOLUTION,
|
|
260
|
+
aspectRatio: config.aspectRatio || DEFAULT_ASPECT_RATIO,
|
|
261
|
+
s3OutputUri
|
|
262
|
+
});
|
|
263
|
+
const { invocationArn, error: startError } = await this.startVideoGeneration(modelInput, s3OutputUri);
|
|
264
|
+
if (startError || !invocationArn) return { error: startError || "Failed to start video generation" };
|
|
265
|
+
require_logger.logger.info("[Luma Ray] Job started", { invocationArn });
|
|
266
|
+
const pollIntervalMs = config.pollIntervalMs || DEFAULT_POLL_INTERVAL_MS;
|
|
267
|
+
const maxPollTimeMs = config.maxPollTimeMs || DEFAULT_MAX_POLL_TIME_MS;
|
|
268
|
+
const { response, error: pollError } = await this.pollForCompletion(invocationArn, pollIntervalMs, maxPollTimeMs);
|
|
269
|
+
if (pollError || !response) return { error: pollError || "Polling failed" };
|
|
270
|
+
const outputS3Uri = response.outputDataConfig?.s3OutputDataConfig?.s3Uri;
|
|
271
|
+
if (!outputS3Uri) return { error: "No output location in response" };
|
|
272
|
+
let blobRef;
|
|
273
|
+
const outputUrl = `${outputS3Uri}/output.mp4`;
|
|
274
|
+
if (config.downloadFromS3 !== false) {
|
|
275
|
+
const { blobRef: ref, error: downloadError } = await this.downloadAndStoreVideo(outputS3Uri);
|
|
276
|
+
if (downloadError) require_logger.logger.warn(`[Luma Ray] Failed to download video: ${downloadError}. Using S3 URL.`);
|
|
277
|
+
else blobRef = ref;
|
|
278
|
+
}
|
|
279
|
+
const latencyMs = Date.now() - startTime;
|
|
280
|
+
const duration = config.duration || DEFAULT_DURATION;
|
|
281
|
+
const resolution = config.resolution || DEFAULT_RESOLUTION;
|
|
282
|
+
const aspectRatio = config.aspectRatio || DEFAULT_ASPECT_RATIO;
|
|
283
|
+
const durationSeconds = this.getDurationSeconds(duration);
|
|
284
|
+
const dimensions = this.getVideoDimensions(aspectRatio, resolution);
|
|
285
|
+
return {
|
|
286
|
+
output: `[Video: ${require_text.ellipsize(prompt.replace(/\r?\n|\r/g, " ").replace(/\[/g, "(").replace(/\]/g, ")"), 50)}](${blobRef?.uri || outputUrl})`,
|
|
287
|
+
cached: false,
|
|
288
|
+
latencyMs,
|
|
289
|
+
video: {
|
|
290
|
+
id: invocationArn,
|
|
291
|
+
blobRef,
|
|
292
|
+
url: blobRef ? void 0 : outputUrl,
|
|
293
|
+
format: "mp4",
|
|
294
|
+
size: dimensions,
|
|
295
|
+
duration: durationSeconds,
|
|
296
|
+
model: this.modelName,
|
|
297
|
+
resolution: dimensions
|
|
298
|
+
},
|
|
299
|
+
metadata: {
|
|
300
|
+
invocationArn,
|
|
301
|
+
model: this.modelName,
|
|
302
|
+
duration,
|
|
303
|
+
resolution,
|
|
304
|
+
aspectRatio,
|
|
305
|
+
loop: config.loop,
|
|
306
|
+
s3OutputUri: outputS3Uri,
|
|
307
|
+
...blobRef && { blobHash: blobRef.hash }
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
//#endregion
|
|
313
|
+
exports.LumaRayVideoProvider = LumaRayVideoProvider;
|
|
314
|
+
|
|
315
|
+
//# sourceMappingURL=luma-ray-BAU9X_ep.cjs.map
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { r as logger } from "./logger-C40ZGil9.js";
|
|
2
|
+
import { d as sleep } from "./fetch-DQckpUFz.js";
|
|
3
|
+
import { n as storeBlob } from "./blobs-BY8MDmpo.js";
|
|
4
|
+
import { t as ellipsize } from "./text-B_UCRPp2.js";
|
|
5
|
+
import { t as AwsBedrockGenericProvider } from "./base-C451JQfq.js";
|
|
6
|
+
import * as fs$1 from "fs";
|
|
7
|
+
import * as path$1 from "path";
|
|
8
|
+
//#region src/providers/bedrock/luma-ray.ts
|
|
9
|
+
/**
|
|
10
|
+
* Luma Ray 2 Video Generation Provider
|
|
11
|
+
*
|
|
12
|
+
* Supports text-to-video and image-to-video generation using AWS Bedrock's
|
|
13
|
+
* async invoke API. Videos can be 5 or 9 seconds with various aspect ratios.
|
|
14
|
+
*/
|
|
15
|
+
const MODEL_ID = "luma.ray-v2:0";
|
|
16
|
+
const DEFAULT_DURATION = "5s";
|
|
17
|
+
const DEFAULT_RESOLUTION = "720p";
|
|
18
|
+
const DEFAULT_ASPECT_RATIO = "16:9";
|
|
19
|
+
const DEFAULT_POLL_INTERVAL_MS = 1e4;
|
|
20
|
+
const DEFAULT_MAX_POLL_TIME_MS = 6e5;
|
|
21
|
+
const VALID_ASPECT_RATIOS = [
|
|
22
|
+
"1:1",
|
|
23
|
+
"16:9",
|
|
24
|
+
"9:16",
|
|
25
|
+
"4:3",
|
|
26
|
+
"3:4",
|
|
27
|
+
"21:9",
|
|
28
|
+
"9:21"
|
|
29
|
+
];
|
|
30
|
+
const VALID_DURATIONS = ["5s", "9s"];
|
|
31
|
+
const VALID_RESOLUTIONS = ["540p", "720p"];
|
|
32
|
+
var LumaRayVideoProvider = class extends AwsBedrockGenericProvider {
|
|
33
|
+
videoConfig;
|
|
34
|
+
providerId;
|
|
35
|
+
constructor(modelName = MODEL_ID, options = {}) {
|
|
36
|
+
super(modelName, options);
|
|
37
|
+
this.videoConfig = options.config || {};
|
|
38
|
+
this.providerId = options.id;
|
|
39
|
+
}
|
|
40
|
+
id() {
|
|
41
|
+
return this.providerId || `bedrock:video:${this.modelName}`;
|
|
42
|
+
}
|
|
43
|
+
toString() {
|
|
44
|
+
return `[Luma Ray 2 Video Provider ${this.modelName}]`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Load image data from file:// path or return as-is if base64
|
|
48
|
+
*/
|
|
49
|
+
loadImageData(imagePath) {
|
|
50
|
+
if (imagePath.startsWith("file://")) {
|
|
51
|
+
const filePath = imagePath.slice(7);
|
|
52
|
+
const resolvedPath = path$1.resolve(filePath);
|
|
53
|
+
if (filePath.includes("..") && resolvedPath !== path$1.resolve(path$1.normalize(filePath))) return { error: `Invalid image path (path traversal detected): ${filePath}` };
|
|
54
|
+
if (!fs$1.existsSync(resolvedPath)) return { error: `Image file not found: ${resolvedPath}` };
|
|
55
|
+
return { data: fs$1.readFileSync(resolvedPath).toString("base64") };
|
|
56
|
+
}
|
|
57
|
+
return { data: imagePath };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Detect image format from path or data
|
|
61
|
+
*/
|
|
62
|
+
detectImageFormat(imagePath) {
|
|
63
|
+
const lowerPath = imagePath.toLowerCase();
|
|
64
|
+
if (lowerPath.includes(".png") || lowerPath.startsWith("ivborw")) return "image/png";
|
|
65
|
+
return "image/jpeg";
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build a keyframe from image path
|
|
69
|
+
*/
|
|
70
|
+
buildKeyframe(imagePath) {
|
|
71
|
+
const { data, error } = this.loadImageData(imagePath);
|
|
72
|
+
if (error || !data) return { error: error || "Failed to load image" };
|
|
73
|
+
return { keyframe: {
|
|
74
|
+
type: "image",
|
|
75
|
+
source: {
|
|
76
|
+
type: "base64",
|
|
77
|
+
media_type: this.detectImageFormat(imagePath),
|
|
78
|
+
data
|
|
79
|
+
}
|
|
80
|
+
} };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build keyframes from config convenience properties
|
|
84
|
+
*/
|
|
85
|
+
buildKeyframes(config) {
|
|
86
|
+
if (config.keyframes) return { keyframes: config.keyframes };
|
|
87
|
+
const keyframes = {};
|
|
88
|
+
if (config.startImage) {
|
|
89
|
+
const { keyframe, error } = this.buildKeyframe(config.startImage);
|
|
90
|
+
if (error) return { error: `Start image error: ${error}` };
|
|
91
|
+
keyframes.frame0 = keyframe;
|
|
92
|
+
}
|
|
93
|
+
if (config.endImage) {
|
|
94
|
+
const { keyframe, error } = this.buildKeyframe(config.endImage);
|
|
95
|
+
if (error) return { error: `End image error: ${error}` };
|
|
96
|
+
keyframes.frame1 = keyframe;
|
|
97
|
+
}
|
|
98
|
+
if (Object.keys(keyframes).length === 0) return {};
|
|
99
|
+
return { keyframes };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build model input for Luma Ray
|
|
103
|
+
*/
|
|
104
|
+
buildModelInput(prompt, config) {
|
|
105
|
+
const aspectRatio = config.aspectRatio || DEFAULT_ASPECT_RATIO;
|
|
106
|
+
if (!VALID_ASPECT_RATIOS.includes(aspectRatio)) return { error: `Invalid aspect_ratio: ${aspectRatio}. Must be one of: ${VALID_ASPECT_RATIOS.join(", ")}` };
|
|
107
|
+
const duration = config.duration || DEFAULT_DURATION;
|
|
108
|
+
if (!VALID_DURATIONS.includes(duration)) return { error: `Invalid duration: ${duration}. Must be one of: ${VALID_DURATIONS.join(", ")}` };
|
|
109
|
+
const resolution = config.resolution || DEFAULT_RESOLUTION;
|
|
110
|
+
if (!VALID_RESOLUTIONS.includes(resolution)) return { error: `Invalid resolution: ${resolution}. Must be one of: ${VALID_RESOLUTIONS.join(", ")}` };
|
|
111
|
+
const { keyframes, error: keyframeError } = this.buildKeyframes(config);
|
|
112
|
+
if (keyframeError) return { error: keyframeError };
|
|
113
|
+
const modelInput = {
|
|
114
|
+
prompt,
|
|
115
|
+
aspect_ratio: aspectRatio,
|
|
116
|
+
duration,
|
|
117
|
+
resolution
|
|
118
|
+
};
|
|
119
|
+
if (config.loop !== void 0) modelInput.loop = config.loop;
|
|
120
|
+
if (keyframes) modelInput.keyframes = keyframes;
|
|
121
|
+
return { input: modelInput };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Start async video generation job
|
|
125
|
+
*/
|
|
126
|
+
async startVideoGeneration(modelInput, s3OutputUri) {
|
|
127
|
+
try {
|
|
128
|
+
const { BedrockRuntimeClient, StartAsyncInvokeCommand } = await import("@aws-sdk/client-bedrock-runtime");
|
|
129
|
+
const credentials = await this.getCredentials();
|
|
130
|
+
const client = new BedrockRuntimeClient({
|
|
131
|
+
region: this.getRegion(),
|
|
132
|
+
...credentials ? { credentials } : {}
|
|
133
|
+
});
|
|
134
|
+
const command = new StartAsyncInvokeCommand({
|
|
135
|
+
modelId: this.modelName,
|
|
136
|
+
modelInput,
|
|
137
|
+
outputDataConfig: { s3OutputDataConfig: { s3Uri: s3OutputUri } }
|
|
138
|
+
});
|
|
139
|
+
return { invocationArn: (await client.send(command)).invocationArn };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
const error = err;
|
|
142
|
+
logger.error("[Luma Ray] Failed to start video generation", { error });
|
|
143
|
+
return { error: `Failed to start video generation: ${error.message || String(err)}` };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Poll for job completion
|
|
148
|
+
*/
|
|
149
|
+
async pollForCompletion(invocationArn, pollIntervalMs, maxPollTimeMs) {
|
|
150
|
+
const startTime = Date.now();
|
|
151
|
+
try {
|
|
152
|
+
const { BedrockRuntimeClient, GetAsyncInvokeCommand } = await import("@aws-sdk/client-bedrock-runtime");
|
|
153
|
+
const credentials = await this.getCredentials();
|
|
154
|
+
const client = new BedrockRuntimeClient({
|
|
155
|
+
region: this.getRegion(),
|
|
156
|
+
...credentials ? { credentials } : {}
|
|
157
|
+
});
|
|
158
|
+
while (Date.now() - startTime < maxPollTimeMs) {
|
|
159
|
+
const command = new GetAsyncInvokeCommand({ invocationArn });
|
|
160
|
+
const invocation = await client.send(command);
|
|
161
|
+
logger.debug(`[Luma Ray] Job status: ${invocation.status}`, {
|
|
162
|
+
invocationArn,
|
|
163
|
+
elapsedMs: Date.now() - startTime
|
|
164
|
+
});
|
|
165
|
+
if (invocation.status === "Completed") return { response: {
|
|
166
|
+
invocationArn: invocation.invocationArn || invocationArn,
|
|
167
|
+
status: "Completed",
|
|
168
|
+
submitTime: invocation.submitTime?.toISOString(),
|
|
169
|
+
endTime: invocation.endTime?.toISOString(),
|
|
170
|
+
outputDataConfig: invocation.outputDataConfig
|
|
171
|
+
} };
|
|
172
|
+
if (invocation.status === "Failed") return { error: `Video generation failed: ${invocation.failureMessage}` };
|
|
173
|
+
await sleep(pollIntervalMs);
|
|
174
|
+
}
|
|
175
|
+
return { error: `Video generation timed out after ${maxPollTimeMs / 1e3} seconds` };
|
|
176
|
+
} catch (err) {
|
|
177
|
+
const error = err;
|
|
178
|
+
logger.error("[Luma Ray] Polling error", {
|
|
179
|
+
error,
|
|
180
|
+
invocationArn
|
|
181
|
+
});
|
|
182
|
+
return { error: `Polling error: ${error.message || String(err)}` };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Download video from S3 and store to blob storage
|
|
187
|
+
*/
|
|
188
|
+
async downloadAndStoreVideo(s3Uri) {
|
|
189
|
+
try {
|
|
190
|
+
const match = s3Uri.match(/^s3:\/\/([^/]+)\/(.+)$/);
|
|
191
|
+
if (!match) return { error: `Invalid S3 URI: ${s3Uri}` };
|
|
192
|
+
const [, bucket, keyPrefix] = match;
|
|
193
|
+
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
194
|
+
const credentials = await this.getCredentials();
|
|
195
|
+
const s3 = new S3Client({
|
|
196
|
+
region: this.getRegion(),
|
|
197
|
+
...credentials ? { credentials } : {}
|
|
198
|
+
});
|
|
199
|
+
const videoKey = keyPrefix.endsWith("/") ? `${keyPrefix}output.mp4` : `${keyPrefix}/output.mp4`;
|
|
200
|
+
logger.debug("[Luma Ray] Downloading video from S3", {
|
|
201
|
+
bucket,
|
|
202
|
+
key: videoKey
|
|
203
|
+
});
|
|
204
|
+
const response = await s3.send(new GetObjectCommand({
|
|
205
|
+
Bucket: bucket,
|
|
206
|
+
Key: videoKey
|
|
207
|
+
}));
|
|
208
|
+
if (!response.Body) return { error: "Empty response from S3" };
|
|
209
|
+
const { ref } = await storeBlob(Buffer.from(await response.Body.transformToByteArray()), "video/mp4", {
|
|
210
|
+
kind: "video",
|
|
211
|
+
location: "response.video"
|
|
212
|
+
});
|
|
213
|
+
logger.debug("[Luma Ray] Stored video to blob storage", {
|
|
214
|
+
uri: ref.uri,
|
|
215
|
+
hash: ref.hash
|
|
216
|
+
});
|
|
217
|
+
return { blobRef: ref };
|
|
218
|
+
} catch (err) {
|
|
219
|
+
const error = err;
|
|
220
|
+
logger.error("[Luma Ray] S3 download error", {
|
|
221
|
+
error,
|
|
222
|
+
s3Uri
|
|
223
|
+
});
|
|
224
|
+
if (error.name === "MODULE_NOT_FOUND" || String(err).includes("Cannot find module")) return { error: "The @aws-sdk/client-s3 package is required for Luma Ray video downloads. Install it with: npm install @aws-sdk/client-s3" };
|
|
225
|
+
return { error: `S3 download error: ${error.message || String(err)}` };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get video dimensions from aspect ratio
|
|
230
|
+
*/
|
|
231
|
+
getVideoDimensions(aspectRatio, resolution) {
|
|
232
|
+
const height = resolution === "540p" ? 540 : 720;
|
|
233
|
+
const aspectParts = aspectRatio.split(":").map(Number);
|
|
234
|
+
return `${Math.round(height * aspectParts[0] / aspectParts[1])}x${height}`;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get duration in seconds from duration string
|
|
238
|
+
*/
|
|
239
|
+
getDurationSeconds(duration) {
|
|
240
|
+
return duration === "9s" ? 9 : 5;
|
|
241
|
+
}
|
|
242
|
+
async callApi(prompt, context) {
|
|
243
|
+
const s3OutputUri = this.videoConfig.s3OutputUri;
|
|
244
|
+
if (!s3OutputUri) return { error: "Luma Ray requires s3OutputUri in provider config. Example: s3://my-bucket/videos/" };
|
|
245
|
+
if (!s3OutputUri.startsWith("s3://")) return { error: `Invalid s3OutputUri: ${s3OutputUri}. Must start with s3://` };
|
|
246
|
+
if (!prompt || prompt.trim() === "") return { error: "Prompt is required for video generation" };
|
|
247
|
+
if (prompt.length > 5e3) return { error: `Prompt exceeds 5000 character limit. Got: ${prompt.length}` };
|
|
248
|
+
const config = {
|
|
249
|
+
...this.videoConfig,
|
|
250
|
+
...context?.prompt?.config
|
|
251
|
+
};
|
|
252
|
+
const startTime = Date.now();
|
|
253
|
+
const { input: modelInput, error: buildError } = this.buildModelInput(prompt, config);
|
|
254
|
+
if (buildError || !modelInput) return { error: buildError || "Failed to build model input" };
|
|
255
|
+
logger.info("[Luma Ray] Starting video generation job...", {
|
|
256
|
+
duration: config.duration || DEFAULT_DURATION,
|
|
257
|
+
resolution: config.resolution || DEFAULT_RESOLUTION,
|
|
258
|
+
aspectRatio: config.aspectRatio || DEFAULT_ASPECT_RATIO,
|
|
259
|
+
s3OutputUri
|
|
260
|
+
});
|
|
261
|
+
const { invocationArn, error: startError } = await this.startVideoGeneration(modelInput, s3OutputUri);
|
|
262
|
+
if (startError || !invocationArn) return { error: startError || "Failed to start video generation" };
|
|
263
|
+
logger.info("[Luma Ray] Job started", { invocationArn });
|
|
264
|
+
const pollIntervalMs = config.pollIntervalMs || DEFAULT_POLL_INTERVAL_MS;
|
|
265
|
+
const maxPollTimeMs = config.maxPollTimeMs || DEFAULT_MAX_POLL_TIME_MS;
|
|
266
|
+
const { response, error: pollError } = await this.pollForCompletion(invocationArn, pollIntervalMs, maxPollTimeMs);
|
|
267
|
+
if (pollError || !response) return { error: pollError || "Polling failed" };
|
|
268
|
+
const outputS3Uri = response.outputDataConfig?.s3OutputDataConfig?.s3Uri;
|
|
269
|
+
if (!outputS3Uri) return { error: "No output location in response" };
|
|
270
|
+
let blobRef;
|
|
271
|
+
const outputUrl = `${outputS3Uri}/output.mp4`;
|
|
272
|
+
if (config.downloadFromS3 !== false) {
|
|
273
|
+
const { blobRef: ref, error: downloadError } = await this.downloadAndStoreVideo(outputS3Uri);
|
|
274
|
+
if (downloadError) logger.warn(`[Luma Ray] Failed to download video: ${downloadError}. Using S3 URL.`);
|
|
275
|
+
else blobRef = ref;
|
|
276
|
+
}
|
|
277
|
+
const latencyMs = Date.now() - startTime;
|
|
278
|
+
const duration = config.duration || DEFAULT_DURATION;
|
|
279
|
+
const resolution = config.resolution || DEFAULT_RESOLUTION;
|
|
280
|
+
const aspectRatio = config.aspectRatio || DEFAULT_ASPECT_RATIO;
|
|
281
|
+
const durationSeconds = this.getDurationSeconds(duration);
|
|
282
|
+
const dimensions = this.getVideoDimensions(aspectRatio, resolution);
|
|
283
|
+
return {
|
|
284
|
+
output: `[Video: ${ellipsize(prompt.replace(/\r?\n|\r/g, " ").replace(/\[/g, "(").replace(/\]/g, ")"), 50)}](${blobRef?.uri || outputUrl})`,
|
|
285
|
+
cached: false,
|
|
286
|
+
latencyMs,
|
|
287
|
+
video: {
|
|
288
|
+
id: invocationArn,
|
|
289
|
+
blobRef,
|
|
290
|
+
url: blobRef ? void 0 : outputUrl,
|
|
291
|
+
format: "mp4",
|
|
292
|
+
size: dimensions,
|
|
293
|
+
duration: durationSeconds,
|
|
294
|
+
model: this.modelName,
|
|
295
|
+
resolution: dimensions
|
|
296
|
+
},
|
|
297
|
+
metadata: {
|
|
298
|
+
invocationArn,
|
|
299
|
+
model: this.modelName,
|
|
300
|
+
duration,
|
|
301
|
+
resolution,
|
|
302
|
+
aspectRatio,
|
|
303
|
+
loop: config.loop,
|
|
304
|
+
s3OutputUri: outputS3Uri,
|
|
305
|
+
...blobRef && { blobHash: blobRef.hash }
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
//#endregion
|
|
311
|
+
export { LumaRayVideoProvider };
|
|
312
|
+
|
|
313
|
+
//# sourceMappingURL=luma-ray-nwVseBbv.js.map
|