vidspotai-shared 1.0.79 → 1.0.81
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/lib/globals/aiModels/enums.d.ts +5 -0
- package/lib/globals/aiModels/enums.d.ts.map +1 -1
- package/lib/globals/aiModels/enums.js +12 -1
- package/lib/globals/aiModels/providers/alibaba.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/alibaba.js +159 -39
- package/lib/services/aiGen/aiGenFactory.service.d.ts +4 -1
- package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.js +13 -1
- package/lib/services/aiGen/index.d.ts +1 -0
- package/lib/services/aiGen/index.d.ts.map +1 -1
- package/lib/services/aiGen/index.js +1 -0
- package/lib/services/aiGen/providers/alibaba/alibaba.d.ts +34 -7
- package/lib/services/aiGen/providers/alibaba/alibaba.d.ts.map +1 -1
- package/lib/services/aiGen/providers/alibaba/alibaba.js +193 -75
- package/lib/services/aiGen/providers/google/google.service.d.ts +1 -0
- package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.js +55 -7
- package/lib/services/aiGen/providers/openai/openai.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/openai/openai.service.js +22 -10
- package/lib/services/aiGen/providers/pixverse/pixverse.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/pixverse/pixverse.service.js +71 -40
- package/lib/services/aiGen/transientRetry.d.ts +35 -0
- package/lib/services/aiGen/transientRetry.d.ts.map +1 -0
- package/lib/services/aiGen/transientRetry.js +106 -0
- package/package.json +6 -6
- package/lib/services/aiGen/providers/azure/azure.service.d.ts +0 -14
- package/lib/services/aiGen/providers/azure/azure.service.d.ts.map +0 -1
- package/lib/services/aiGen/providers/azure/azure.service.js +0 -108
- package/lib/services/aiGen/providers/azure/index.d.ts +0 -2
- package/lib/services/aiGen/providers/azure/index.d.ts.map +0 -1
- package/lib/services/aiGen/providers/azure/index.js +0 -17
|
@@ -12,7 +12,70 @@ const types_1 = require("../../../../globals/types");
|
|
|
12
12
|
const firebase_1 = require("../../../../libs/firebase");
|
|
13
13
|
const helpers_2 = require("../../../../utils/helpers");
|
|
14
14
|
const logger_1 = require("../../../../utils/logger");
|
|
15
|
+
const errors_1 = require("../../../../utils/errors");
|
|
16
|
+
const transientRetry_1 = require("../../transientRetry");
|
|
15
17
|
const crypto_1 = __importDefault(require("crypto"));
|
|
18
|
+
// PixVerse soft-failure ErrCodes (HTTP 200 body). 0 = success.
|
|
19
|
+
// 500090 — insufficient balance on the openapi account (operator must top up)
|
|
20
|
+
// 401xx — auth (apiKey empty/invalid)
|
|
21
|
+
const PIXVERSE_ERR_INSUFFICIENT_BALANCE = 500090;
|
|
22
|
+
/**
|
|
23
|
+
* Classify a PixVerse HTTP failure (non-2xx). Returns:
|
|
24
|
+
* - TransientHttpError for retryable cases (5xx, 429, and 404 — PixVerse's
|
|
25
|
+
* CloudFlare edge has been observed serving "404 page not found" for
|
|
26
|
+
* transiently-misrouted requests that succeed on retry, 2026-06-06 prod log)
|
|
27
|
+
* - UserFacingError for 401/403 auth (deploy/config bug — fail fast, no Slack)
|
|
28
|
+
* - raw Error for true 4xx (validation, etc.) — surface to Slack as bug
|
|
29
|
+
*/
|
|
30
|
+
function classifyPixVerseHttpError(status, body, op) {
|
|
31
|
+
if (status === 401 || status === 403) {
|
|
32
|
+
return new errors_1.UserFacingError(`PixVerse rejected the API key (HTTP ${status}). Ask an operator to verify PIXVERSE_API_KEY.`, errors_1.USER_FACING_ERROR_CODES.PROVIDER_AUTH_ERROR);
|
|
33
|
+
}
|
|
34
|
+
if (status >= 500 || status === 429 || status === 404) {
|
|
35
|
+
return new transientRetry_1.TransientHttpError(status, `PixVerse ${op} transient HTTP ${status}: ${body.slice(0, 200)}`);
|
|
36
|
+
}
|
|
37
|
+
return new Error(`PixVerse ${op} failed (${status}): ${body}`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Classify a PixVerse 200-response with non-zero ErrCode. Insufficient-balance
|
|
41
|
+
* is an operator concern (account top-up), not a per-user bug — we surface it
|
|
42
|
+
* as UserFacingError(ACCOUNT_QUOTA_EXCEEDED) so it logs as warn (no Slack
|
|
43
|
+
* page-storm per job) AND the daily ops-channel digest still picks up that
|
|
44
|
+
* the account is empty. Other ErrCodes are real provider/protocol bugs.
|
|
45
|
+
*/
|
|
46
|
+
function classifyPixVerseApiError(errCode, errMsg, op) {
|
|
47
|
+
if (errCode === PIXVERSE_ERR_INSUFFICIENT_BALANCE) {
|
|
48
|
+
return new errors_1.UserFacingError("Video provider is temporarily unavailable. Please try a different model or retry shortly.", errors_1.USER_FACING_ERROR_CODES.ACCOUNT_QUOTA_EXCEEDED);
|
|
49
|
+
}
|
|
50
|
+
return new Error(`PixVerse ${op} API error (code ${errCode}): ${errMsg || "Unknown error"}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One-shot PixVerse POST/GET helper with shared transient-retry behavior.
|
|
54
|
+
* Returns the parsed JSON body, or throws a classified error.
|
|
55
|
+
*/
|
|
56
|
+
async function pixverseFetch(url, init, op) {
|
|
57
|
+
return (0, transientRetry_1.withTransientRetry)(`pixverse:${op}`, async () => {
|
|
58
|
+
const resp = await fetch(url, init);
|
|
59
|
+
if (!resp.ok) {
|
|
60
|
+
const errText = await resp.text();
|
|
61
|
+
throw classifyPixVerseHttpError(resp.status, errText, op);
|
|
62
|
+
}
|
|
63
|
+
const data = await resp.json();
|
|
64
|
+
if (data.ErrCode !== undefined && data.ErrCode !== 0) {
|
|
65
|
+
throw classifyPixVerseApiError(data.ErrCode, data.ErrMsg ?? "", op);
|
|
66
|
+
}
|
|
67
|
+
return data;
|
|
68
|
+
}, {
|
|
69
|
+
onRetry: ({ attempt, maxAttempts, backoffMs, err }) => {
|
|
70
|
+
logger_1.logger.warn(`PixVerse ${op} transient error — retrying`, {
|
|
71
|
+
attempt,
|
|
72
|
+
maxAttempts,
|
|
73
|
+
backoffMs,
|
|
74
|
+
message: err.message,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
16
79
|
class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderService {
|
|
17
80
|
constructor() {
|
|
18
81
|
super(...arguments);
|
|
@@ -43,7 +106,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
43
106
|
extendBody.negative_prompt = params.negativePrompt;
|
|
44
107
|
if (params.seed !== undefined)
|
|
45
108
|
extendBody.seed = params.seed;
|
|
46
|
-
const
|
|
109
|
+
const data = await pixverseFetch(`${this.baseUrl}/extend/generate`, {
|
|
47
110
|
method: "POST",
|
|
48
111
|
headers: {
|
|
49
112
|
"Content-Type": "application/json",
|
|
@@ -51,15 +114,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
51
114
|
"Ai-trace-id": traceId,
|
|
52
115
|
},
|
|
53
116
|
body: JSON.stringify(extendBody),
|
|
54
|
-
});
|
|
55
|
-
if (!resp.ok) {
|
|
56
|
-
const errText = await resp.text();
|
|
57
|
-
throw new Error(`PixVerse extendVideo failed (${resp.status}): ${errText}`);
|
|
58
|
-
}
|
|
59
|
-
const data = await resp.json();
|
|
60
|
-
if (data.ErrCode !== 0) {
|
|
61
|
-
throw new Error(`PixVerse API error: ${data.ErrMsg || "Unknown error"}`);
|
|
62
|
-
}
|
|
117
|
+
}, "extendVideo");
|
|
63
118
|
const videoId = data?.Resp?.video_id;
|
|
64
119
|
if (!videoId)
|
|
65
120
|
throw new Error("PixVerse extend did not return video_id");
|
|
@@ -94,7 +149,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
94
149
|
effectBody.water_mark = !params.watermark;
|
|
95
150
|
if (params.motionMode)
|
|
96
151
|
effectBody.motion_mode = params.motionMode;
|
|
97
|
-
const
|
|
152
|
+
const data = await pixverseFetch(`${this.baseUrl}/template/generate`, {
|
|
98
153
|
method: "POST",
|
|
99
154
|
headers: {
|
|
100
155
|
"Content-Type": "application/json",
|
|
@@ -102,15 +157,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
102
157
|
"Ai-trace-id": traceId,
|
|
103
158
|
},
|
|
104
159
|
body: JSON.stringify(effectBody),
|
|
105
|
-
});
|
|
106
|
-
if (!resp.ok) {
|
|
107
|
-
const errText = await resp.text();
|
|
108
|
-
throw new Error(`PixVerse effect generation failed (${resp.status}): ${errText}`);
|
|
109
|
-
}
|
|
110
|
-
const data = await resp.json();
|
|
111
|
-
if (data.ErrCode !== 0) {
|
|
112
|
-
throw new Error(`PixVerse API error: ${data.ErrMsg || "Unknown error"}`);
|
|
113
|
-
}
|
|
160
|
+
}, "effectGeneration");
|
|
114
161
|
const videoId = data?.Resp?.video_id;
|
|
115
162
|
if (!videoId)
|
|
116
163
|
throw new Error("PixVerse effect did not return video_id");
|
|
@@ -165,7 +212,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
165
212
|
body.image_url = params.inputImageUrl;
|
|
166
213
|
}
|
|
167
214
|
const endpoint = isImageToVideo ? `${this.baseUrl}/image/generate` : `${this.baseUrl}/text/generate`;
|
|
168
|
-
const
|
|
215
|
+
const data = await pixverseFetch(endpoint, {
|
|
169
216
|
method: "POST",
|
|
170
217
|
headers: {
|
|
171
218
|
"Content-Type": "application/json",
|
|
@@ -173,15 +220,7 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
173
220
|
"Ai-trace-id": traceId,
|
|
174
221
|
},
|
|
175
222
|
body: JSON.stringify(body),
|
|
176
|
-
});
|
|
177
|
-
if (!resp.ok) {
|
|
178
|
-
const errText = await resp.text();
|
|
179
|
-
throw new Error(`PixVerse generateVideo failed (${resp.status}): ${errText}`);
|
|
180
|
-
}
|
|
181
|
-
const data = await resp.json();
|
|
182
|
-
if (data.ErrCode !== 0) {
|
|
183
|
-
throw new Error(`PixVerse API error: ${data.ErrMsg || "Unknown error"}`);
|
|
184
|
-
}
|
|
223
|
+
}, "generateVideo");
|
|
185
224
|
const videoId = data?.Resp?.video_id;
|
|
186
225
|
if (!videoId) {
|
|
187
226
|
throw new Error("PixVerse API did not return video_id");
|
|
@@ -196,21 +235,13 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
|
|
|
196
235
|
// =========================================================
|
|
197
236
|
async checkVideoStatus({ task, outputFilename, outputFilePath = "videos", }) {
|
|
198
237
|
const traceId = crypto_1.default.randomUUID();
|
|
199
|
-
const
|
|
238
|
+
const data = await pixverseFetch(`${this.baseUrl}/result/${task}`, {
|
|
200
239
|
method: "GET",
|
|
201
240
|
headers: {
|
|
202
241
|
"API-KEY": process.env.PIXVERSE_API_KEY,
|
|
203
242
|
"Ai-trace-id": traceId,
|
|
204
243
|
},
|
|
205
|
-
});
|
|
206
|
-
if (!resp.ok) {
|
|
207
|
-
const errText = await resp.text();
|
|
208
|
-
throw new Error(`PixVerse checkVideoStatus failed (${resp.status}): ${errText}`);
|
|
209
|
-
}
|
|
210
|
-
const data = await resp.json();
|
|
211
|
-
if (data.ErrCode !== 0) {
|
|
212
|
-
throw new Error(`PixVerse API error: ${data.ErrMsg || "Unknown error"}`);
|
|
213
|
-
}
|
|
244
|
+
}, "checkVideoStatus");
|
|
214
245
|
const status = data?.Resp?.status;
|
|
215
246
|
// ---- Status Mapping ----
|
|
216
247
|
switch (status) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when an error from `fetch()` / SDK call is a transient network failure
|
|
3
|
+
* worth retrying. False for application-layer errors (4xx, validation, etc.).
|
|
4
|
+
*/
|
|
5
|
+
export declare function isTransientFetchError(err: unknown): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Marker thrown by callers when they detect a transient HTTP response (5xx /
|
|
8
|
+
* 429 / opt-in 404) and want `withTransientRetry` to retry it. Caller is
|
|
9
|
+
* responsible for the classification — this util is response-shape agnostic.
|
|
10
|
+
*/
|
|
11
|
+
export declare class TransientHttpError extends Error {
|
|
12
|
+
readonly isTransientHttpError = true;
|
|
13
|
+
readonly status: number;
|
|
14
|
+
constructor(status: number, message: string);
|
|
15
|
+
}
|
|
16
|
+
export interface TransientRetryOptions {
|
|
17
|
+
/** Max attempts including the first try. Defaults to 3. */
|
|
18
|
+
maxAttempts?: number;
|
|
19
|
+
/** Base backoff in ms; doubled each retry. Defaults to 1000 (1s → 2s → 4s). */
|
|
20
|
+
baseBackoffMs?: number;
|
|
21
|
+
/** Logger function for warn-level retry notices. */
|
|
22
|
+
onRetry?: (info: {
|
|
23
|
+
attempt: number;
|
|
24
|
+
maxAttempts: number;
|
|
25
|
+
backoffMs: number;
|
|
26
|
+
err: Error;
|
|
27
|
+
}) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Wraps `fn` with retry on transient errors. UserFacingError ALWAYS short-
|
|
31
|
+
* circuits (no retry, no logging — caller's responsibility to log appropriately).
|
|
32
|
+
* Non-transient errors short-circuit on the first attempt.
|
|
33
|
+
*/
|
|
34
|
+
export declare function withTransientRetry<T>(label: string, fn: () => Promise<T>, opts?: TransientRetryOptions): Promise<T>;
|
|
35
|
+
//# sourceMappingURL=transientRetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transientRetry.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/transientRetry.ts"],"names":[],"mappings":"AAwCA;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQ3D;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,oBAAoB,QAAQ;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK5C;AAED,MAAM,WAAW,qBAAqB;IACpC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI,CAAC;CACnG;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,CAAC,CAAC,CA2BZ"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared transient-error retry primitive for provider service calls.
|
|
3
|
+
//
|
|
4
|
+
// Background: every provider (Google, PixVerse, Alibaba, ...) makes outbound
|
|
5
|
+
// HTTP/SDK calls that can drop on transient network failures (TCP resets,
|
|
6
|
+
// undici fetch hiccups, upstream 5xx, CloudFlare edge 404 misroutes, etc.).
|
|
7
|
+
// Before this util each provider classified transient errors inline or not at
|
|
8
|
+
// all, so the same edge hiccup that retried OK on Google would fail-fast on
|
|
9
|
+
// PixVerse — fan-out inconsistency that bit us with the 2026-06-06 PixVerse
|
|
10
|
+
// 404 (the request body was valid, the account had a real account-level
|
|
11
|
+
// problem, but the response shape was an HTML "404 page not found" from the
|
|
12
|
+
// edge layer — a one-shot we'd have absorbed silently with a retry).
|
|
13
|
+
//
|
|
14
|
+
// Policy:
|
|
15
|
+
// - retry on undici "fetch failed" + the network-code allowlist below
|
|
16
|
+
// - retry on HTTP 5xx + 429 + 404 (404 ONLY when the URL is known-good and the
|
|
17
|
+
// caller opted in via `retryHttp4xx404`; otherwise 4xx is permanent)
|
|
18
|
+
// - never retry UserFacingError — those are deterministic by construction
|
|
19
|
+
// (content moderation, validation, missing input) and a retry just burns
|
|
20
|
+
// a provider credit for the same outcome
|
|
21
|
+
//
|
|
22
|
+
// Backoff: exponential 1s → 2s → 4s by default. Override via `backoffMs`.
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.TransientHttpError = void 0;
|
|
25
|
+
exports.isTransientFetchError = isTransientFetchError;
|
|
26
|
+
exports.withTransientRetry = withTransientRetry;
|
|
27
|
+
const errors_1 = require("../../utils/errors");
|
|
28
|
+
// Codes from undici / Node net layer that indicate a transient network failure
|
|
29
|
+
// (request never reached server, or server hung up mid-flight). Retrying these
|
|
30
|
+
// is safe at the API layer for our use cases.
|
|
31
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
32
|
+
"ECONNRESET",
|
|
33
|
+
"ECONNREFUSED",
|
|
34
|
+
"ETIMEDOUT",
|
|
35
|
+
"ENOTFOUND",
|
|
36
|
+
"EAI_AGAIN",
|
|
37
|
+
"EPIPE",
|
|
38
|
+
"UND_ERR_SOCKET",
|
|
39
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
40
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
41
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
42
|
+
]);
|
|
43
|
+
/**
|
|
44
|
+
* True when an error from `fetch()` / SDK call is a transient network failure
|
|
45
|
+
* worth retrying. False for application-layer errors (4xx, validation, etc.).
|
|
46
|
+
*/
|
|
47
|
+
function isTransientFetchError(err) {
|
|
48
|
+
if (!err)
|
|
49
|
+
return false;
|
|
50
|
+
const e = err;
|
|
51
|
+
// undici wraps low-level errors as `TypeError: fetch failed` with the real
|
|
52
|
+
// reason on `err.cause`. Treat that exact shape as transient.
|
|
53
|
+
if (e.message === "fetch failed")
|
|
54
|
+
return true;
|
|
55
|
+
const code = e.code ?? e.cause?.code;
|
|
56
|
+
return !!code && TRANSIENT_NETWORK_CODES.has(code);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Marker thrown by callers when they detect a transient HTTP response (5xx /
|
|
60
|
+
* 429 / opt-in 404) and want `withTransientRetry` to retry it. Caller is
|
|
61
|
+
* responsible for the classification — this util is response-shape agnostic.
|
|
62
|
+
*/
|
|
63
|
+
class TransientHttpError extends Error {
|
|
64
|
+
constructor(status, message) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.isTransientHttpError = true;
|
|
67
|
+
this.name = "TransientHttpError";
|
|
68
|
+
this.status = status;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.TransientHttpError = TransientHttpError;
|
|
72
|
+
/**
|
|
73
|
+
* Wraps `fn` with retry on transient errors. UserFacingError ALWAYS short-
|
|
74
|
+
* circuits (no retry, no logging — caller's responsibility to log appropriately).
|
|
75
|
+
* Non-transient errors short-circuit on the first attempt.
|
|
76
|
+
*/
|
|
77
|
+
async function withTransientRetry(label, fn, opts = {}) {
|
|
78
|
+
const maxAttempts = opts.maxAttempts ?? 3;
|
|
79
|
+
const baseBackoffMs = opts.baseBackoffMs ?? 1000;
|
|
80
|
+
let lastErr;
|
|
81
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
return await fn();
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
lastErr = err;
|
|
87
|
+
// UserFacingError is by-construction deterministic — never retry.
|
|
88
|
+
if (err instanceof errors_1.UserFacingError)
|
|
89
|
+
throw err;
|
|
90
|
+
const transient = isTransientFetchError(err) ||
|
|
91
|
+
(err instanceof TransientHttpError);
|
|
92
|
+
if (!transient || attempt === maxAttempts)
|
|
93
|
+
throw err;
|
|
94
|
+
const backoffMs = baseBackoffMs * 2 ** (attempt - 1);
|
|
95
|
+
opts.onRetry?.({
|
|
96
|
+
attempt,
|
|
97
|
+
maxAttempts,
|
|
98
|
+
backoffMs,
|
|
99
|
+
err: err,
|
|
100
|
+
});
|
|
101
|
+
void label; // label is for tracing in caller's onRetry; intentionally unused here
|
|
102
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
throw lastErr;
|
|
106
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vidspotai-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.81",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"lib"
|
|
15
15
|
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"watch": "tsc -p tsconfig.json --watch"
|
|
19
|
+
},
|
|
16
20
|
"dependencies": {
|
|
17
21
|
"@anthropic-ai/sdk": "^0.98.0",
|
|
18
22
|
"@google-cloud/storage": "*",
|
|
@@ -43,9 +47,5 @@
|
|
|
43
47
|
"peerDependencies": {
|
|
44
48
|
"firebase-admin": "^13.5.0",
|
|
45
49
|
"ioredis": "^5.8.0"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"build": "tsc -p tsconfig.json",
|
|
49
|
-
"watch": "tsc -p tsconfig.json --watch"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { BaseAiGenProviderService } from "../baseAiGenProvider.service";
|
|
2
|
-
import { CreditUsageParams, VideoGenerationParams, VideoGenerationResult, VideoStatusParams, VideoStatusResult } from "../types";
|
|
3
|
-
export declare class AzureService extends BaseAiGenProviderService {
|
|
4
|
-
private readonly baseUrl;
|
|
5
|
-
private readonly apiKey;
|
|
6
|
-
private readonly apiVersion;
|
|
7
|
-
private readonly timeout;
|
|
8
|
-
constructor();
|
|
9
|
-
private request;
|
|
10
|
-
generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
|
|
11
|
-
checkVideoStatus({ task, outputFilename, outputFilePath, }: VideoStatusParams): Promise<VideoStatusResult>;
|
|
12
|
-
getCreditUsed({ modelKey, dimensions, duration, multiClip, }: CreditUsageParams): number;
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=azure.service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"azure.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/azure/azure.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAElB,qBAAa,YAAa,SAAQ,wBAAwB;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;;YAiBnB,OAAO;IAwBf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA6B3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAgDjD,aAAa,CAAC,EACZ,QAAQ,EACR,UAAsB,EACtB,QAAY,EACZ,SAAiB,GAClB,EAAE,iBAAiB,GAAG,MAAM;CAS9B"}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AzureService = void 0;
|
|
7
|
-
const axios_1 = __importDefault(require("axios"));
|
|
8
|
-
const aiModels_1 = require("../../../../globals/aiModels");
|
|
9
|
-
const types_1 = require("../../../../globals/types");
|
|
10
|
-
const firebase_1 = require("../../../../libs/firebase");
|
|
11
|
-
const helpers_1 = require("../../../../utils/helpers");
|
|
12
|
-
const logger_1 = require("../../../../utils/logger");
|
|
13
|
-
const helpers_2 = require("../../helpers");
|
|
14
|
-
const baseAiGenProvider_service_1 = require("../baseAiGenProvider.service");
|
|
15
|
-
class AzureService extends baseAiGenProvider_service_1.BaseAiGenProviderService {
|
|
16
|
-
constructor() {
|
|
17
|
-
super();
|
|
18
|
-
this.apiVersion = "preview";
|
|
19
|
-
this.timeout = 60000; // 60 seconds
|
|
20
|
-
if (!process.env.AZURE_OPENAI_ENDPOINT ||
|
|
21
|
-
!process.env.AZURE_OPENAI_API_KEY) {
|
|
22
|
-
throw new Error("Missing AZURE_OPENAI_ENDPOINT or AZURE_OPENAI_API_KEY in environment variables");
|
|
23
|
-
}
|
|
24
|
-
this.baseUrl = process.env.AZURE_OPENAI_ENDPOINT;
|
|
25
|
-
this.apiKey = process.env.AZURE_OPENAI_API_KEY;
|
|
26
|
-
}
|
|
27
|
-
async request(endpoint, method, body) {
|
|
28
|
-
const url = `${this.baseUrl}${endpoint}${endpoint.includes("?") ? "&" : "?"}api-version=${this.apiVersion}`;
|
|
29
|
-
const config = {
|
|
30
|
-
method,
|
|
31
|
-
url,
|
|
32
|
-
headers: {
|
|
33
|
-
"api-key": this.apiKey,
|
|
34
|
-
"Content-Type": "application/json",
|
|
35
|
-
},
|
|
36
|
-
timeout: this.timeout,
|
|
37
|
-
data: body,
|
|
38
|
-
};
|
|
39
|
-
const res = await axios_1.default.request(config);
|
|
40
|
-
return res.data;
|
|
41
|
-
}
|
|
42
|
-
async generateVideo(params) {
|
|
43
|
-
(0, helpers_2.validateParams)(params);
|
|
44
|
-
const modelId = aiModels_1.aiModelConfigs[params.modelKey]?.modelId || "sora";
|
|
45
|
-
const body = {
|
|
46
|
-
prompt: params.prompt,
|
|
47
|
-
width: params.dimensions?.split("x")[0]
|
|
48
|
-
? parseInt(params.dimensions.split("x")[0] ?? "480", 10)
|
|
49
|
-
: 480,
|
|
50
|
-
height: params.dimensions?.split("x")[1]
|
|
51
|
-
? parseInt(params.dimensions.split("x")[1] ?? "480", 10)
|
|
52
|
-
: 480,
|
|
53
|
-
n_seconds: params.duration || 5,
|
|
54
|
-
model: modelId,
|
|
55
|
-
};
|
|
56
|
-
const job = await this.request(`/openai/v1/video/generations/jobs?`, "POST", body);
|
|
57
|
-
return {
|
|
58
|
-
task: job.id,
|
|
59
|
-
status: types_1.EVideoSceneStatus.TRIGGERED,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
async checkVideoStatus({ task, outputFilename, outputFilePath = "videos", }) {
|
|
63
|
-
const result = await this.request(`/openai/v1/video/generations/jobs/${task}?`, "GET");
|
|
64
|
-
if (result.status === "succeeded") {
|
|
65
|
-
if (!result.generations || !(result.generations.length > 0)) {
|
|
66
|
-
return {
|
|
67
|
-
status: types_1.EVideoSceneStatus.FAILED,
|
|
68
|
-
errorMessage: "No video returned from API",
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
const generationId = result.generations[0].id;
|
|
72
|
-
const videoUrl = `${this.baseUrl}/openai/v1/video/generations/${generationId}/content/video?api-version=${this.apiVersion}`;
|
|
73
|
-
const videoResp = await axios_1.default.get(videoUrl, {
|
|
74
|
-
headers: { "api-key": this.apiKey },
|
|
75
|
-
responseType: "arraybuffer",
|
|
76
|
-
timeout: this.timeout,
|
|
77
|
-
});
|
|
78
|
-
if (!videoResp.data)
|
|
79
|
-
throw new Error("No video data received from Azure API");
|
|
80
|
-
const filePath = `${outputFilePath}/${outputFilename}.mp4`;
|
|
81
|
-
const file = (0, firebase_1.getBucket)().file(filePath);
|
|
82
|
-
const buffer = Buffer.from(videoResp.data);
|
|
83
|
-
await file.save(buffer, { contentType: "video/mp4" });
|
|
84
|
-
const [signedUrl] = await file.getSignedUrl({
|
|
85
|
-
action: "read",
|
|
86
|
-
expires: "03-09-2491",
|
|
87
|
-
});
|
|
88
|
-
return { videoUrl: signedUrl, status: types_1.EVideoSceneStatus.COMPLETED };
|
|
89
|
-
}
|
|
90
|
-
else if (result.status === "failed") {
|
|
91
|
-
return {
|
|
92
|
-
status: types_1.EVideoSceneStatus.FAILED,
|
|
93
|
-
errorMessage: `Task failed: ${result.failure_reason || "unknown"}`,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
97
|
-
}
|
|
98
|
-
getCreditUsed({ modelKey, dimensions = "480x480", duration = 5, multiClip = false, }) {
|
|
99
|
-
const modelConfig = aiModels_1.aiModelConfigs[modelKey];
|
|
100
|
-
const cost = modelConfig.cost?.table?.[dimensions]?.[duration];
|
|
101
|
-
if (cost === undefined || cost === null) {
|
|
102
|
-
logger_1.logger.warn(`Azure getCreditUsed: no cost entry for modelKey="${modelKey}" dimensions="${dimensions}" duration=${duration} — returning fallback`, { modelKey, dimensions, duration });
|
|
103
|
-
return (0, helpers_1.getCreditsFromCost)(1.0, !multiClip);
|
|
104
|
-
}
|
|
105
|
-
return (0, helpers_1.getCreditsFromCost)(cost, !multiClip);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
exports.AzureService = AzureService;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/azure/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./azure.service"), exports);
|