vidspotai-shared 1.0.95 → 1.0.97
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 +3 -0
- package/lib/globals/aiModels/enums.d.ts.map +1 -1
- package/lib/globals/aiModels/enums.js +9 -0
- package/lib/globals/aiModels/index.d.ts.map +1 -1
- package/lib/globals/aiModels/index.js +2 -0
- package/lib/globals/aiModels/providers/hostedAvatars.d.ts +22 -0
- package/lib/globals/aiModels/providers/hostedAvatars.d.ts.map +1 -0
- package/lib/globals/aiModels/providers/hostedAvatars.js +129 -0
- package/lib/globals/aiModels/providers/kling.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/kling.js +85 -1
- package/lib/globals/creditUnit.d.ts +29 -0
- package/lib/globals/creditUnit.d.ts.map +1 -1
- package/lib/globals/creditUnit.js +30 -1
- package/lib/globals/types.d.ts +58 -1
- package/lib/globals/types.d.ts.map +1 -1
- package/lib/globals/types.js +61 -1
- package/lib/models/demoVideoProject.model.d.ts +244 -0
- package/lib/models/demoVideoProject.model.d.ts.map +1 -0
- package/lib/models/demoVideoProject.model.js +2 -0
- package/lib/models/githubConnection.model.d.ts +35 -0
- package/lib/models/githubConnection.model.d.ts.map +1 -0
- package/lib/models/githubConnection.model.js +2 -0
- package/lib/models/index.d.ts +3 -0
- package/lib/models/index.d.ts.map +1 -1
- package/lib/models/index.js +3 -0
- package/lib/models/outreach.model.d.ts +175 -0
- package/lib/models/outreach.model.d.ts.map +1 -0
- package/lib/models/outreach.model.js +2 -0
- package/lib/models/video.model.d.ts +4 -0
- package/lib/models/video.model.d.ts.map +1 -1
- package/lib/schemas/brandKit.schema.d.ts +4 -0
- package/lib/schemas/brandKit.schema.d.ts.map +1 -1
- package/lib/schemas/brandKit.schema.js +10 -0
- package/lib/schemas/demoQuestion.schema.d.ts +286 -0
- package/lib/schemas/demoQuestion.schema.d.ts.map +1 -0
- package/lib/schemas/demoQuestion.schema.js +210 -0
- package/lib/schemas/demoSpec.schema.d.ts +109 -6
- package/lib/schemas/demoSpec.schema.d.ts.map +1 -1
- package/lib/schemas/demoSpec.schema.js +95 -7
- package/lib/schemas/index.d.ts +1 -0
- package/lib/schemas/index.d.ts.map +1 -1
- package/lib/schemas/index.js +1 -0
- package/lib/services/agent/providerFallback/chains.d.ts.map +1 -1
- package/lib/services/agent/providerFallback/chains.js +9 -1
- package/lib/services/agent/taskPoller.d.ts.map +1 -1
- package/lib/services/agent/taskPoller.js +17 -6
- package/lib/services/agent/tools/generateAvatarVideo.tool.d.ts +21 -9
- package/lib/services/agent/tools/generateAvatarVideo.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/generateAvatarVideo.tool.js +135 -39
- package/lib/services/aiGen/aiGenFactory.service.d.ts +1 -1
- package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.js +11 -2
- package/lib/services/aiGen/providers/kling/kling.service.d.ts +36 -1
- package/lib/services/aiGen/providers/kling/kling.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/kling.service.js +62 -5
- package/lib/services/aiGen/providers/kling/klingCredits.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/klingCredits.js +12 -0
- package/lib/services/aiGen/providers/kling/klingRequests.d.ts +14 -1
- package/lib/services/aiGen/providers/kling/klingRequests.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/klingRequests.js +44 -0
- package/lib/services/aiGen/providers/kling/klingStatus.d.ts +13 -1
- package/lib/services/aiGen/providers/kling/klingStatus.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/klingStatus.js +111 -0
- package/lib/services/aiGen/providers/kling/types.d.ts +71 -0
- package/lib/services/aiGen/providers/kling/types.d.ts.map +1 -1
- package/lib/services/avatarGen/IAvatarVideoProvider.d.ts +136 -0
- package/lib/services/avatarGen/IAvatarVideoProvider.d.ts.map +1 -0
- package/lib/services/avatarGen/IAvatarVideoProvider.js +42 -0
- package/lib/services/avatarGen/avatarProviderFactory.d.ts +18 -0
- package/lib/services/avatarGen/avatarProviderFactory.d.ts.map +1 -0
- package/lib/services/avatarGen/avatarProviderFactory.js +54 -0
- package/lib/services/avatarGen/index.d.ts +6 -0
- package/lib/services/avatarGen/index.d.ts.map +1 -0
- package/lib/services/avatarGen/index.js +21 -0
- package/lib/services/avatarGen/providers/heygenAvatarProvider.d.ts +45 -0
- package/lib/services/avatarGen/providers/heygenAvatarProvider.d.ts.map +1 -0
- package/lib/services/avatarGen/providers/heygenAvatarProvider.js +190 -0
- package/lib/services/avatarGen/providers/klingAvatarProvider.d.ts +32 -0
- package/lib/services/avatarGen/providers/klingAvatarProvider.d.ts.map +1 -0
- package/lib/services/avatarGen/providers/klingAvatarProvider.js +69 -0
- package/lib/services/avatarGen/providers/tavusAvatarProvider.d.ts +56 -0
- package/lib/services/avatarGen/providers/tavusAvatarProvider.d.ts.map +1 -0
- package/lib/services/avatarGen/providers/tavusAvatarProvider.js +183 -0
- package/lib/services/demo/demoAuthor.d.ts +45 -0
- package/lib/services/demo/demoAuthor.d.ts.map +1 -0
- package/lib/services/demo/demoAuthor.js +75 -0
- package/lib/services/demo/demoAuthorNormalize.d.ts +49 -0
- package/lib/services/demo/demoAuthorNormalize.d.ts.map +1 -0
- package/lib/services/demo/demoAuthorNormalize.js +327 -0
- package/lib/services/demo/demoAuthorPrompt.d.ts +102 -0
- package/lib/services/demo/demoAuthorPrompt.d.ts.map +1 -0
- package/lib/services/demo/demoAuthorPrompt.js +243 -0
- package/lib/services/demo/demoCredits.d.ts +20 -0
- package/lib/services/demo/demoCredits.d.ts.map +1 -0
- package/lib/services/demo/demoCredits.js +51 -0
- package/lib/services/demo/demoMediaGuard.d.ts +43 -0
- package/lib/services/demo/demoMediaGuard.d.ts.map +1 -0
- package/lib/services/demo/demoMediaGuard.js +207 -0
- package/lib/services/demo/demoMusic.d.ts +13 -0
- package/lib/services/demo/demoMusic.d.ts.map +1 -0
- package/lib/services/demo/demoMusic.js +93 -0
- package/lib/services/demo/demoPrefill.d.ts +76 -0
- package/lib/services/demo/demoPrefill.d.ts.map +1 -0
- package/lib/services/demo/demoPrefill.js +435 -0
- package/lib/services/demo/demoRevisePrompt.d.ts +17 -0
- package/lib/services/demo/demoRevisePrompt.d.ts.map +1 -0
- package/lib/services/demo/demoRevisePrompt.js +85 -0
- package/lib/services/demo/demoReviser.d.ts +63 -0
- package/lib/services/demo/demoReviser.d.ts.map +1 -0
- package/lib/services/demo/demoReviser.js +81 -0
- package/lib/services/demo/demoScriptPrompt.d.ts +63 -0
- package/lib/services/demo/demoScriptPrompt.d.ts.map +1 -0
- package/lib/services/demo/demoScriptPrompt.js +234 -0
- package/lib/services/demo/demoScriptWriter.d.ts +65 -0
- package/lib/services/demo/demoScriptWriter.d.ts.map +1 -0
- package/lib/services/demo/demoScriptWriter.js +211 -0
- package/lib/services/demo/demoTriage.d.ts +65 -0
- package/lib/services/demo/demoTriage.d.ts.map +1 -0
- package/lib/services/demo/demoTriage.js +130 -0
- package/lib/services/demo/githubOAuth.d.ts +78 -0
- package/lib/services/demo/githubOAuth.d.ts.map +1 -0
- package/lib/services/demo/githubOAuth.js +209 -0
- package/lib/services/demo/githubOAuthState.d.ts +10 -0
- package/lib/services/demo/githubOAuthState.d.ts.map +1 -0
- package/lib/services/demo/githubOAuthState.js +63 -0
- package/lib/services/demo/index.d.ts +24 -0
- package/lib/services/demo/index.d.ts.map +1 -0
- package/lib/services/demo/index.js +39 -0
- package/lib/services/firestore.service.d.ts +11 -0
- package/lib/services/firestore.service.d.ts.map +1 -1
- package/lib/services/firestore.service.js +24 -0
- package/lib/services/index.d.ts +2 -0
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/index.js +2 -0
- package/lib/services/tts/providers/elevenlabs.service.d.ts.map +1 -1
- package/lib/services/tts/providers/elevenlabs.service.js +16 -8
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.d.ts.map +1 -1
- package/lib/utils/index.js +1 -0
- package/lib/utils/outreachTemplate.d.ts +46 -0
- package/lib/utils/outreachTemplate.d.ts.map +1 -0
- package/lib/utils/outreachTemplate.js +174 -0
- package/package.json +1 -1
|
@@ -0,0 +1,190 @@
|
|
|
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.HeyGenAvatarProvider = 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 logger_1 = require("../../../utils/logger");
|
|
11
|
+
const types_2 = require("../../aiGen/types");
|
|
12
|
+
const IAvatarVideoProvider_1 = require("../IAvatarVideoProvider");
|
|
13
|
+
/**
|
|
14
|
+
* HeyGen avatar engine — a HOSTED engine (the caller picks a HeyGen-hosted
|
|
15
|
+
* avatar + voice and supplies a script; HeyGen does its own TTS + lipsync).
|
|
16
|
+
*
|
|
17
|
+
* Contract (HeyGen API, verified 2026-06-26 against developers.heygen.com +
|
|
18
|
+
* the published OpenAPI spec; v2 endpoints supported through 2026-10-31):
|
|
19
|
+
* • submit : POST /v2/video/generate → { data: { video_id } }
|
|
20
|
+
* • status : GET /v1/video_status.get?video_id=… → { data: { status, video_url } }
|
|
21
|
+
* • avatars: GET /v2/avatars → { data: { avatars: [...] } }
|
|
22
|
+
* • voices : GET /v2/voices → { data: { voices: [...] } }
|
|
23
|
+
* • auth : X-Api-Key header
|
|
24
|
+
*
|
|
25
|
+
* This is a STANDALONE class (not an adapter over an existing service) because
|
|
26
|
+
* HeyGen has no representation in the generic video factory — avatar gen is its
|
|
27
|
+
* own concern (see IAvatarVideoProvider). Transport (auth + timeout + retry) is
|
|
28
|
+
* owned here, mirroring the per-provider pattern KlingService uses.
|
|
29
|
+
*
|
|
30
|
+
* Capabilities: inputMode "hosted", nativeTts true (HeyGen synthesizes), no
|
|
31
|
+
* multi-scene split (one render per script). It also exposes the hosted catalog
|
|
32
|
+
* (ISupportsHostedCatalog) so the UI / agent can list pickable avatars + voices.
|
|
33
|
+
*/
|
|
34
|
+
class HeyGenAvatarProvider {
|
|
35
|
+
constructor(modelKey = aiModels_1.EVideoGenModels.HEYGEN_AVATAR) {
|
|
36
|
+
this.modelKey = modelKey;
|
|
37
|
+
this.engine = IAvatarVideoProvider_1.EAvatarEngine.HEYGEN;
|
|
38
|
+
this.capabilities = HeyGenAvatarProvider.CAPABILITIES;
|
|
39
|
+
this.baseUrl = "https://api.heygen.com";
|
|
40
|
+
this.submitTimeout = 120000; // 2 min — submit can be slow under load
|
|
41
|
+
this.statusTimeout = 60000; // 1 min — status should be fast
|
|
42
|
+
if (!process.env.HEYGEN_API_KEY) {
|
|
43
|
+
throw new Error("Missing HEYGEN_API_KEY in environment variables");
|
|
44
|
+
}
|
|
45
|
+
this.apiKey = process.env.HEYGEN_API_KEY;
|
|
46
|
+
}
|
|
47
|
+
async generate(req) {
|
|
48
|
+
if (req.mode !== "hosted") {
|
|
49
|
+
throw new Error(`HeyGenAvatarProvider only supports hosted requests (got "${req.mode}")`);
|
|
50
|
+
}
|
|
51
|
+
// HeyGen renders a 16:9 720p by default; render mode bumps to 1080p on pro.
|
|
52
|
+
const dimension = req.renderMode === types_2.EVideoMode.PROFESSIONAL
|
|
53
|
+
? { width: 1920, height: 1080 }
|
|
54
|
+
: { width: 1280, height: 720 };
|
|
55
|
+
const body = {
|
|
56
|
+
video_inputs: [
|
|
57
|
+
{
|
|
58
|
+
character: {
|
|
59
|
+
type: "avatar",
|
|
60
|
+
avatar_id: req.hostedAvatarId,
|
|
61
|
+
avatar_style: "normal",
|
|
62
|
+
},
|
|
63
|
+
voice: {
|
|
64
|
+
type: "text",
|
|
65
|
+
input_text: req.script,
|
|
66
|
+
voice_id: req.hostedVoiceId,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
dimension,
|
|
71
|
+
};
|
|
72
|
+
const data = await this.request("/v2/video/generate", "POST", body, this.submitTimeout);
|
|
73
|
+
const videoId = data?.data?.video_id;
|
|
74
|
+
if (!videoId) {
|
|
75
|
+
throw new Error(`HeyGen submit returned no video_id: ${JSON.stringify(data?.error ?? data)}`);
|
|
76
|
+
}
|
|
77
|
+
return { providerTaskId: videoId };
|
|
78
|
+
}
|
|
79
|
+
async checkStatus(query) {
|
|
80
|
+
const data = await this.requestWithRetry(`/v1/video_status.get?video_id=${encodeURIComponent(query.providerTaskId)}`, "GET", undefined, this.statusTimeout);
|
|
81
|
+
const providerStatus = data?.data?.status;
|
|
82
|
+
// HeyGen status enum: pending | waiting | processing | completed | failed.
|
|
83
|
+
if (providerStatus === "completed") {
|
|
84
|
+
return {
|
|
85
|
+
status: types_1.EVideoSceneStatus.COMPLETED,
|
|
86
|
+
videoUrl: data?.data?.video_url,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (providerStatus === "failed") {
|
|
90
|
+
return {
|
|
91
|
+
status: types_1.EVideoSceneStatus.FAILED,
|
|
92
|
+
errorMessage: data?.data?.error?.message ?? data?.message ?? "HeyGen render failed",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
96
|
+
}
|
|
97
|
+
// ─── ISupportsHostedCatalog ───────────────────────────────────────────────
|
|
98
|
+
async listAvatars() {
|
|
99
|
+
const data = await this.requestWithRetry("/v2/avatars", "GET", undefined, this.statusTimeout);
|
|
100
|
+
return (data?.data?.avatars ?? []).map((a) => ({
|
|
101
|
+
id: a.avatar_id,
|
|
102
|
+
name: a.avatar_name ?? a.avatar_id,
|
|
103
|
+
previewUrl: a.preview_image_url,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
async listVoices() {
|
|
107
|
+
const data = await this.requestWithRetry("/v2/voices", "GET", undefined, this.statusTimeout);
|
|
108
|
+
return (data?.data?.voices ?? []).map((v) => ({
|
|
109
|
+
id: v.voice_id,
|
|
110
|
+
name: v.name ?? v.voice_id,
|
|
111
|
+
gender: this.normalizeGender(v.gender),
|
|
112
|
+
accent: v.language,
|
|
113
|
+
previewUrl: v.preview_audio,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
// ─── Transport ────────────────────────────────────────────────────────────
|
|
117
|
+
normalizeGender(g) {
|
|
118
|
+
const lower = (g ?? "").toLowerCase();
|
|
119
|
+
if (lower === "male")
|
|
120
|
+
return "male";
|
|
121
|
+
if (lower === "female")
|
|
122
|
+
return "female";
|
|
123
|
+
return "neutral";
|
|
124
|
+
}
|
|
125
|
+
async request(endpoint, method, body, timeout = this.submitTimeout) {
|
|
126
|
+
const config = {
|
|
127
|
+
method,
|
|
128
|
+
url: `${this.baseUrl}${endpoint}`,
|
|
129
|
+
headers: {
|
|
130
|
+
"X-Api-Key": this.apiKey,
|
|
131
|
+
"Content-Type": "application/json",
|
|
132
|
+
},
|
|
133
|
+
timeout,
|
|
134
|
+
data: body,
|
|
135
|
+
};
|
|
136
|
+
const response = await axios_1.default.request(config);
|
|
137
|
+
if (response.status < 200 || response.status >= 300) {
|
|
138
|
+
throw new Error(`HeyGen API Error: ${response.status} ${response.statusText} — ${JSON.stringify(response.data)}`);
|
|
139
|
+
}
|
|
140
|
+
return response.data;
|
|
141
|
+
}
|
|
142
|
+
/** Retries transient network/timeout/429 failures with a small backoff. */
|
|
143
|
+
async requestWithRetry(endpoint, method, body, timeout = this.statusTimeout, maxAttempts = 4) {
|
|
144
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
145
|
+
try {
|
|
146
|
+
return await this.request(endpoint, method, body, timeout);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
const e = err;
|
|
150
|
+
const httpStatus = e.response?.status;
|
|
151
|
+
const isTimeout = e.code === "ECONNABORTED" || (e.message?.includes("timeout") ?? false);
|
|
152
|
+
const isNetworkError = e.code === "ECONNRESET" ||
|
|
153
|
+
e.code === "ECONNREFUSED" ||
|
|
154
|
+
e.code === "ENOTFOUND" ||
|
|
155
|
+
e.code === "ERR_NETWORK";
|
|
156
|
+
const isRateLimit = httpStatus === 429;
|
|
157
|
+
const isRetryable = isTimeout || isNetworkError || isRateLimit;
|
|
158
|
+
if (!isRetryable || attempt === maxAttempts) {
|
|
159
|
+
logger_1.logger.error("HeyGen request failed (non-retryable or max attempts)", {
|
|
160
|
+
endpoint,
|
|
161
|
+
attempt,
|
|
162
|
+
maxAttempts,
|
|
163
|
+
httpStatus,
|
|
164
|
+
errorCode: e.code,
|
|
165
|
+
errorMessage: e.message,
|
|
166
|
+
});
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
const backoffMs = isRateLimit ? 10000 : 1000;
|
|
170
|
+
logger_1.logger.warn("HeyGen request failed — retrying", {
|
|
171
|
+
endpoint,
|
|
172
|
+
attempt,
|
|
173
|
+
maxAttempts,
|
|
174
|
+
httpStatus,
|
|
175
|
+
backoffMs,
|
|
176
|
+
});
|
|
177
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
throw new Error("Unreachable");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.HeyGenAvatarProvider = HeyGenAvatarProvider;
|
|
184
|
+
/** Static so callers can branch on capability without constructing (which
|
|
185
|
+
* would require HEYGEN_API_KEY just to read the input mode). */
|
|
186
|
+
HeyGenAvatarProvider.CAPABILITIES = {
|
|
187
|
+
inputMode: "hosted",
|
|
188
|
+
nativeTts: true,
|
|
189
|
+
multiSceneSplit: false,
|
|
190
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EVideoGenModels } from "../../../globals/aiModels";
|
|
2
|
+
import { AvatarCapabilities, AvatarGenerateRequest, AvatarStatusQuery, AvatarStatusResult, AvatarSubmitResult, EAvatarEngine, IAvatarVideoProvider } from "../IAvatarVideoProvider";
|
|
3
|
+
/**
|
|
4
|
+
* Kling avatar engine, behind the IAvatarVideoProvider seam.
|
|
5
|
+
*
|
|
6
|
+
* This is intentionally a THIN ADAPTER over the existing `KlingService`, NOT a
|
|
7
|
+
* reimplementation: Kling's avatar endpoint shares the same JWT auth, IPv4-pinned
|
|
8
|
+
* agents, timeout/retry and status-stash transport as regular Kling video, so the
|
|
9
|
+
* avatar submit/poll logic stays exactly where it already lives and is proven
|
|
10
|
+
* (kling.service.ts `generateAvatarVideo` + klingStatus.ts `checkAvatarVideoStatus`).
|
|
11
|
+
* Migrating callers onto this class changes the seam, not Kling's behavior — that
|
|
12
|
+
* byte-for-byte preservation is the Phase 0a regression gate.
|
|
13
|
+
*
|
|
14
|
+
* Kling is "byo-media": caller supplies a face image; WE supply the audio (TTS is
|
|
15
|
+
* run by the orchestrator), so `nativeTts` is false. Long audio is split into
|
|
16
|
+
* scenes and stitched, so `multiSceneSplit` is true. It exposes no hosted catalog,
|
|
17
|
+
* replica training or dubbing — those optional capability interfaces are simply
|
|
18
|
+
* not implemented (ISP).
|
|
19
|
+
*/
|
|
20
|
+
export declare class KlingAvatarProvider implements IAvatarVideoProvider {
|
|
21
|
+
private readonly modelKey;
|
|
22
|
+
/** Static so callers can branch on capability without constructing (which
|
|
23
|
+
* would require Kling API keys just to read the input mode). */
|
|
24
|
+
static readonly CAPABILITIES: AvatarCapabilities;
|
|
25
|
+
readonly engine = EAvatarEngine.KLING;
|
|
26
|
+
readonly capabilities: AvatarCapabilities;
|
|
27
|
+
private readonly kling;
|
|
28
|
+
constructor(modelKey?: EVideoGenModels);
|
|
29
|
+
generate(req: AvatarGenerateRequest): Promise<AvatarSubmitResult>;
|
|
30
|
+
checkStatus(query: AvatarStatusQuery): Promise<AvatarStatusResult>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=klingAvatarProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"klingAvatarProvider.d.ts","sourceRoot":"","sources":["../../../../src/services/avatarGen/providers/klingAvatarProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACrB,MAAM,yBAAyB,CAAC;AAIjC;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,mBAAoB,YAAW,oBAAoB;IAe5D,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAd3B;qEACiE;IACjE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAI9C;IAEF,QAAQ,CAAC,MAAM,uBAAuB;IACtC,QAAQ,CAAC,YAAY,qBAAoC;IAEzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAGlB,QAAQ,GAAE,eAA8C;IAKrE,QAAQ,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAqBjE,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAczE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KlingAvatarProvider = void 0;
|
|
4
|
+
const aiModels_1 = require("../../../globals/aiModels");
|
|
5
|
+
const kling_service_1 = require("../../aiGen/providers/kling/kling.service");
|
|
6
|
+
const IAvatarVideoProvider_1 = require("../IAvatarVideoProvider");
|
|
7
|
+
const DEFAULT_OUTPUT_PATH = "avatar-videos";
|
|
8
|
+
/**
|
|
9
|
+
* Kling avatar engine, behind the IAvatarVideoProvider seam.
|
|
10
|
+
*
|
|
11
|
+
* This is intentionally a THIN ADAPTER over the existing `KlingService`, NOT a
|
|
12
|
+
* reimplementation: Kling's avatar endpoint shares the same JWT auth, IPv4-pinned
|
|
13
|
+
* agents, timeout/retry and status-stash transport as regular Kling video, so the
|
|
14
|
+
* avatar submit/poll logic stays exactly where it already lives and is proven
|
|
15
|
+
* (kling.service.ts `generateAvatarVideo` + klingStatus.ts `checkAvatarVideoStatus`).
|
|
16
|
+
* Migrating callers onto this class changes the seam, not Kling's behavior — that
|
|
17
|
+
* byte-for-byte preservation is the Phase 0a regression gate.
|
|
18
|
+
*
|
|
19
|
+
* Kling is "byo-media": caller supplies a face image; WE supply the audio (TTS is
|
|
20
|
+
* run by the orchestrator), so `nativeTts` is false. Long audio is split into
|
|
21
|
+
* scenes and stitched, so `multiSceneSplit` is true. It exposes no hosted catalog,
|
|
22
|
+
* replica training or dubbing — those optional capability interfaces are simply
|
|
23
|
+
* not implemented (ISP).
|
|
24
|
+
*/
|
|
25
|
+
class KlingAvatarProvider {
|
|
26
|
+
constructor(modelKey = aiModels_1.EVideoGenModels.KLING_AVATAR) {
|
|
27
|
+
this.modelKey = modelKey;
|
|
28
|
+
this.engine = IAvatarVideoProvider_1.EAvatarEngine.KLING;
|
|
29
|
+
this.capabilities = KlingAvatarProvider.CAPABILITIES;
|
|
30
|
+
this.kling = new kling_service_1.KlingService();
|
|
31
|
+
}
|
|
32
|
+
async generate(req) {
|
|
33
|
+
if (req.mode !== "byo-media") {
|
|
34
|
+
throw new Error(`KlingAvatarProvider only supports byo-media requests (got "${req.mode}")`);
|
|
35
|
+
}
|
|
36
|
+
// Delegates to the existing Kling avatar path (routes on modelKey ===
|
|
37
|
+
// "kling-avatar" inside generateVideo, before any type-based dispatch — so
|
|
38
|
+
// `type` is intentionally omitted, matching the prior call site).
|
|
39
|
+
const result = await this.kling.generateVideo({
|
|
40
|
+
modelKey: this.modelKey,
|
|
41
|
+
inputImageUrl: req.faceImageUrl,
|
|
42
|
+
inputAudioUrl: req.audioUrl,
|
|
43
|
+
prompt: req.prompt,
|
|
44
|
+
mode: req.renderMode,
|
|
45
|
+
});
|
|
46
|
+
return { providerTaskId: result.task };
|
|
47
|
+
}
|
|
48
|
+
async checkStatus(query) {
|
|
49
|
+
const result = await this.kling.checkVideoStatus({
|
|
50
|
+
task: query.providerTaskId,
|
|
51
|
+
outputFilename: query.outputFilename,
|
|
52
|
+
outputFilePath: query.outputFilePath ?? DEFAULT_OUTPUT_PATH,
|
|
53
|
+
modelKey: this.modelKey,
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
status: result.status,
|
|
57
|
+
videoUrl: result.videoUrl,
|
|
58
|
+
errorMessage: result.errorMessage,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.KlingAvatarProvider = KlingAvatarProvider;
|
|
63
|
+
/** Static so callers can branch on capability without constructing (which
|
|
64
|
+
* would require Kling API keys just to read the input mode). */
|
|
65
|
+
KlingAvatarProvider.CAPABILITIES = {
|
|
66
|
+
inputMode: "byo-media",
|
|
67
|
+
nativeTts: false,
|
|
68
|
+
multiSceneSplit: true,
|
|
69
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { EVideoGenModels } from "../../../globals/aiModels";
|
|
2
|
+
import { AvatarCapabilities, AvatarGenerateRequest, AvatarOption, AvatarStatusQuery, AvatarStatusResult, AvatarSubmitResult, ConsentRecord, EAvatarEngine, IAvatarVideoProvider, ISupportsHostedCatalog, ISupportsReplica, VoiceOption } from "../IAvatarVideoProvider";
|
|
3
|
+
/**
|
|
4
|
+
* Tavus avatar engine — a HOSTED engine driven by a trained "replica"
|
|
5
|
+
* (Tavus's term for a likeness model). The caller supplies a replica_id + a
|
|
6
|
+
* script; Tavus synthesizes the voice (the replica's cloned voice) and lipsyncs
|
|
7
|
+
* natively. Tavus's differentiator is multilingual / dubbing — see the dubbing
|
|
8
|
+
* note at the bottom of this class.
|
|
9
|
+
*
|
|
10
|
+
* Contract (Tavus API, verified 2026-06-26 against docs.tavus.io):
|
|
11
|
+
* • submit : POST /v2/videos { replica_id, script, video_name? }
|
|
12
|
+
* → { video_id, status, hosted_url }
|
|
13
|
+
* • status : GET /v2/videos/{id} → { status: queued|generating|ready|
|
|
14
|
+
* deleted|error, download_url,
|
|
15
|
+
* stream_url, hosted_url, status_details }
|
|
16
|
+
* • replicas: POST /v2/replicas { train_video_url, consent_video_url?,
|
|
17
|
+
* replica_name, callback_url? }
|
|
18
|
+
* → { replica_id, status }
|
|
19
|
+
* GET /v2/replicas → { data: [{ replica_id, replica_name,
|
|
20
|
+
* status, thumbnail_video_url }] }
|
|
21
|
+
* • auth : x-api-key header
|
|
22
|
+
*
|
|
23
|
+
* For Tavus the hosted "avatar" IS the replica, so a hosted request maps
|
|
24
|
+
* `hostedAvatarId` → `replica_id` (`hostedVoiceId` is unused; the replica owns
|
|
25
|
+
* its voice). Standalone class — Tavus has no place in the generic video
|
|
26
|
+
* factory; avatar gen is its own concern (see IAvatarVideoProvider).
|
|
27
|
+
*/
|
|
28
|
+
export declare class TavusAvatarProvider implements IAvatarVideoProvider, ISupportsHostedCatalog, ISupportsReplica {
|
|
29
|
+
private readonly modelKey;
|
|
30
|
+
/** Static so callers can branch on capability without constructing (which
|
|
31
|
+
* would require TAVUS_API_KEY just to read the input mode). */
|
|
32
|
+
static readonly CAPABILITIES: AvatarCapabilities;
|
|
33
|
+
readonly engine = EAvatarEngine.TAVUS;
|
|
34
|
+
readonly capabilities: AvatarCapabilities;
|
|
35
|
+
private readonly baseUrl;
|
|
36
|
+
private readonly apiKey;
|
|
37
|
+
private readonly submitTimeout;
|
|
38
|
+
private readonly statusTimeout;
|
|
39
|
+
constructor(modelKey?: EVideoGenModels);
|
|
40
|
+
generate(req: AvatarGenerateRequest): Promise<AvatarSubmitResult>;
|
|
41
|
+
checkStatus(query: AvatarStatusQuery): Promise<AvatarStatusResult>;
|
|
42
|
+
createReplica(trainingMediaUrl: string, consent: ConsentRecord): Promise<{
|
|
43
|
+
replicaId: string;
|
|
44
|
+
}>;
|
|
45
|
+
/** For Tavus the catalog of "avatars" is the account's trained replicas. */
|
|
46
|
+
listAvatars(): Promise<AvatarOption[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Tavus voices are bound to the replica (its cloned voice), so there is no
|
|
49
|
+
* standalone voice catalog to pick from — return empty. Hosted requests leave
|
|
50
|
+
* `hostedVoiceId` unused for Tavus.
|
|
51
|
+
*/
|
|
52
|
+
listVoices(): Promise<VoiceOption[]>;
|
|
53
|
+
private request;
|
|
54
|
+
private requestWithRetry;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=tavusAvatarProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tavusAvatarProvider.d.ts","sourceRoot":"","sources":["../../../../src/services/avatarGen/providers/tavusAvatarProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5D,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,WAAW,EACZ,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,mBACX,YAAW,oBAAoB,EAAE,sBAAsB,EAAE,gBAAgB;IAmBvE,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAjB3B;oEACgE;IAChE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAI9C;IAEF,QAAQ,CAAC,MAAM,uBAAuB;IACtC,QAAQ,CAAC,YAAY,qBAAoC;IAEzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;gBAGrB,QAAQ,GAAE,eAA8C;IAQrE,QAAQ,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA4BjE,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiClE,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IA2BjC,4EAA4E;IACtE,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAgB5C;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YAM5B,OAAO;YA0BP,gBAAgB;CAiE/B"}
|
|
@@ -0,0 +1,183 @@
|
|
|
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.TavusAvatarProvider = 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 logger_1 = require("../../../utils/logger");
|
|
11
|
+
const IAvatarVideoProvider_1 = require("../IAvatarVideoProvider");
|
|
12
|
+
/**
|
|
13
|
+
* Tavus avatar engine — a HOSTED engine driven by a trained "replica"
|
|
14
|
+
* (Tavus's term for a likeness model). The caller supplies a replica_id + a
|
|
15
|
+
* script; Tavus synthesizes the voice (the replica's cloned voice) and lipsyncs
|
|
16
|
+
* natively. Tavus's differentiator is multilingual / dubbing — see the dubbing
|
|
17
|
+
* note at the bottom of this class.
|
|
18
|
+
*
|
|
19
|
+
* Contract (Tavus API, verified 2026-06-26 against docs.tavus.io):
|
|
20
|
+
* • submit : POST /v2/videos { replica_id, script, video_name? }
|
|
21
|
+
* → { video_id, status, hosted_url }
|
|
22
|
+
* • status : GET /v2/videos/{id} → { status: queued|generating|ready|
|
|
23
|
+
* deleted|error, download_url,
|
|
24
|
+
* stream_url, hosted_url, status_details }
|
|
25
|
+
* • replicas: POST /v2/replicas { train_video_url, consent_video_url?,
|
|
26
|
+
* replica_name, callback_url? }
|
|
27
|
+
* → { replica_id, status }
|
|
28
|
+
* GET /v2/replicas → { data: [{ replica_id, replica_name,
|
|
29
|
+
* status, thumbnail_video_url }] }
|
|
30
|
+
* • auth : x-api-key header
|
|
31
|
+
*
|
|
32
|
+
* For Tavus the hosted "avatar" IS the replica, so a hosted request maps
|
|
33
|
+
* `hostedAvatarId` → `replica_id` (`hostedVoiceId` is unused; the replica owns
|
|
34
|
+
* its voice). Standalone class — Tavus has no place in the generic video
|
|
35
|
+
* factory; avatar gen is its own concern (see IAvatarVideoProvider).
|
|
36
|
+
*/
|
|
37
|
+
class TavusAvatarProvider {
|
|
38
|
+
constructor(modelKey = aiModels_1.EVideoGenModels.TAVUS_AVATAR) {
|
|
39
|
+
this.modelKey = modelKey;
|
|
40
|
+
this.engine = IAvatarVideoProvider_1.EAvatarEngine.TAVUS;
|
|
41
|
+
this.capabilities = TavusAvatarProvider.CAPABILITIES;
|
|
42
|
+
this.baseUrl = "https://tavusapi.com";
|
|
43
|
+
this.submitTimeout = 120000;
|
|
44
|
+
this.statusTimeout = 60000;
|
|
45
|
+
if (!process.env.TAVUS_API_KEY) {
|
|
46
|
+
throw new Error("Missing TAVUS_API_KEY in environment variables");
|
|
47
|
+
}
|
|
48
|
+
this.apiKey = process.env.TAVUS_API_KEY;
|
|
49
|
+
}
|
|
50
|
+
async generate(req) {
|
|
51
|
+
if (req.mode !== "hosted") {
|
|
52
|
+
throw new Error(`TavusAvatarProvider only supports hosted requests (got "${req.mode}")`);
|
|
53
|
+
}
|
|
54
|
+
const body = {
|
|
55
|
+
replica_id: req.hostedAvatarId, // Tavus: the hosted avatar IS the replica
|
|
56
|
+
script: req.script,
|
|
57
|
+
video_name: `vidspot-${req.hostedAvatarId}-${req.script.slice(0, 24)}`,
|
|
58
|
+
};
|
|
59
|
+
const data = await this.request("/v2/videos", "POST", body, this.submitTimeout);
|
|
60
|
+
const videoId = data?.video_id;
|
|
61
|
+
if (!videoId) {
|
|
62
|
+
throw new Error(`Tavus submit returned no video_id: ${JSON.stringify(data?.message ?? data)}`);
|
|
63
|
+
}
|
|
64
|
+
return { providerTaskId: videoId };
|
|
65
|
+
}
|
|
66
|
+
async checkStatus(query) {
|
|
67
|
+
const data = await this.requestWithRetry(`/v2/videos/${encodeURIComponent(query.providerTaskId)}`, "GET", undefined, this.statusTimeout);
|
|
68
|
+
// Tavus status enum: queued | generating | ready | deleted | error.
|
|
69
|
+
if (data?.status === "ready") {
|
|
70
|
+
return {
|
|
71
|
+
status: types_1.EVideoSceneStatus.COMPLETED,
|
|
72
|
+
// Prefer the direct download URL; fall back to stream/hosted.
|
|
73
|
+
videoUrl: data.download_url ?? data.stream_url ?? data.hosted_url,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (data?.status === "error" || data?.status === "deleted") {
|
|
77
|
+
return {
|
|
78
|
+
status: types_1.EVideoSceneStatus.FAILED,
|
|
79
|
+
errorMessage: data.status_details ?? `Tavus render ${data.status}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { status: types_1.EVideoSceneStatus.PENDING };
|
|
83
|
+
}
|
|
84
|
+
// ─── ISupportsReplica ─────────────────────────────────────────────────────
|
|
85
|
+
async createReplica(trainingMediaUrl, consent) {
|
|
86
|
+
const body = {
|
|
87
|
+
train_video_url: trainingMediaUrl,
|
|
88
|
+
replica_name: `vidspot-${consent.userId}-${consent.capturedAt}`,
|
|
89
|
+
// Tavus accepts a separate consent clip; we pass the same training media
|
|
90
|
+
// when no dedicated consent video exists (the statement is recorded in
|
|
91
|
+
// our own ConsentRecord audit trail regardless).
|
|
92
|
+
consent_video_url: trainingMediaUrl,
|
|
93
|
+
};
|
|
94
|
+
const data = await this.request("/v2/replicas", "POST", body, this.submitTimeout);
|
|
95
|
+
if (!data?.replica_id) {
|
|
96
|
+
throw new Error(`Tavus createReplica returned no replica_id: ${JSON.stringify(data)}`);
|
|
97
|
+
}
|
|
98
|
+
return { replicaId: data.replica_id };
|
|
99
|
+
}
|
|
100
|
+
// ─── ISupportsHostedCatalog ───────────────────────────────────────────────
|
|
101
|
+
/** For Tavus the catalog of "avatars" is the account's trained replicas. */
|
|
102
|
+
async listAvatars() {
|
|
103
|
+
const data = await this.requestWithRetry("/v2/replicas", "GET", undefined, this.statusTimeout);
|
|
104
|
+
return (data?.data ?? []).map((r) => ({
|
|
105
|
+
id: r.replica_id,
|
|
106
|
+
name: r.replica_name ?? r.replica_id,
|
|
107
|
+
previewUrl: r.thumbnail_video_url,
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Tavus voices are bound to the replica (its cloned voice), so there is no
|
|
112
|
+
* standalone voice catalog to pick from — return empty. Hosted requests leave
|
|
113
|
+
* `hostedVoiceId` unused for Tavus.
|
|
114
|
+
*/
|
|
115
|
+
async listVoices() {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
// ─── Transport ────────────────────────────────────────────────────────────
|
|
119
|
+
async request(endpoint, method, body, timeout = this.submitTimeout) {
|
|
120
|
+
const config = {
|
|
121
|
+
method,
|
|
122
|
+
url: `${this.baseUrl}${endpoint}`,
|
|
123
|
+
headers: {
|
|
124
|
+
"x-api-key": this.apiKey,
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
},
|
|
127
|
+
timeout,
|
|
128
|
+
data: body,
|
|
129
|
+
};
|
|
130
|
+
const response = await axios_1.default.request(config);
|
|
131
|
+
if (response.status < 200 || response.status >= 300) {
|
|
132
|
+
throw new Error(`Tavus API Error: ${response.status} ${response.statusText} — ${JSON.stringify(response.data)}`);
|
|
133
|
+
}
|
|
134
|
+
return response.data;
|
|
135
|
+
}
|
|
136
|
+
async requestWithRetry(endpoint, method, body, timeout = this.statusTimeout, maxAttempts = 4) {
|
|
137
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
138
|
+
try {
|
|
139
|
+
return await this.request(endpoint, method, body, timeout);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
const e = err;
|
|
143
|
+
const httpStatus = e.response?.status;
|
|
144
|
+
const isTimeout = e.code === "ECONNABORTED" || (e.message?.includes("timeout") ?? false);
|
|
145
|
+
const isNetworkError = e.code === "ECONNRESET" ||
|
|
146
|
+
e.code === "ECONNREFUSED" ||
|
|
147
|
+
e.code === "ENOTFOUND" ||
|
|
148
|
+
e.code === "ERR_NETWORK";
|
|
149
|
+
const isRateLimit = httpStatus === 429;
|
|
150
|
+
const isRetryable = isTimeout || isNetworkError || isRateLimit;
|
|
151
|
+
if (!isRetryable || attempt === maxAttempts) {
|
|
152
|
+
logger_1.logger.error("Tavus request failed (non-retryable or max attempts)", {
|
|
153
|
+
endpoint,
|
|
154
|
+
attempt,
|
|
155
|
+
maxAttempts,
|
|
156
|
+
httpStatus,
|
|
157
|
+
errorCode: e.code,
|
|
158
|
+
errorMessage: e.message,
|
|
159
|
+
});
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
const backoffMs = isRateLimit ? 10000 : 1000;
|
|
163
|
+
logger_1.logger.warn("Tavus request failed — retrying", {
|
|
164
|
+
endpoint,
|
|
165
|
+
attempt,
|
|
166
|
+
maxAttempts,
|
|
167
|
+
httpStatus,
|
|
168
|
+
backoffMs,
|
|
169
|
+
});
|
|
170
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw new Error("Unreachable");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.TavusAvatarProvider = TavusAvatarProvider;
|
|
177
|
+
/** Static so callers can branch on capability without constructing (which
|
|
178
|
+
* would require TAVUS_API_KEY just to read the input mode). */
|
|
179
|
+
TavusAvatarProvider.CAPABILITIES = {
|
|
180
|
+
inputMode: "hosted",
|
|
181
|
+
nativeTts: true,
|
|
182
|
+
multiSceneSplit: false,
|
|
183
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LlmCaller } from "../agent/llmCaller";
|
|
2
|
+
import { ModelChoice, ModelRouter } from "../agent/modelRouter";
|
|
3
|
+
import { DemoAuthorInput } from "./demoAuthorPrompt";
|
|
4
|
+
import { NormalizeResult } from "./demoAuthorNormalize";
|
|
5
|
+
export interface DemoAuthorDeps {
|
|
6
|
+
/** LLM caller. Defaults to the host-wired dispatcher. */
|
|
7
|
+
llm?: LlmCaller;
|
|
8
|
+
/** Model router. Defaults to DEFAULT_MODEL_ROUTING. */
|
|
9
|
+
router?: ModelRouter;
|
|
10
|
+
/** Route the planner to the premium tier (Opus) for higher-fidelity authoring. */
|
|
11
|
+
premium?: boolean;
|
|
12
|
+
/** Sampling temperature. Creative-but-structured default. */
|
|
13
|
+
temperature?: number;
|
|
14
|
+
/** Max output tokens — a full DemoSpec with rich scenes can be large. */
|
|
15
|
+
maxTokens?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Allow the planner to propose Layer-B `custom` scenes (with an `intent`
|
|
18
|
+
* brief). Threaded from the worker's `DEMO_CODEGEN_ENABLED` gate; default
|
|
19
|
+
* false, so the author never emits a `custom` scene the pipeline can't fill.
|
|
20
|
+
*/
|
|
21
|
+
allowCustomScenes?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Allow the planner to commission AI-generated media (`generatedAssets` +
|
|
24
|
+
* `gen://` refs). Default true; the worker passes false only under the
|
|
25
|
+
* DEMO_GEN_ASSETS_DISABLED kill-switch.
|
|
26
|
+
*/
|
|
27
|
+
allowGeneratedAssets?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface DemoAuthorResult extends NormalizeResult {
|
|
30
|
+
/** Model that produced the spec (primary or fallback). */
|
|
31
|
+
modelUsed: ModelChoice;
|
|
32
|
+
usedFallback?: boolean;
|
|
33
|
+
usage?: {
|
|
34
|
+
promptTokens?: number;
|
|
35
|
+
completionTokens?: number;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Author a render-ready DemoSpec from inputs. Pure orchestration: build the
|
|
40
|
+
* prompt, attach screenshots as vision, call `.structured(DemoSpec)`, then
|
|
41
|
+
* normalize. Throws if the caller is unconfigured or the model returns an
|
|
42
|
+
* unparseable / scene-less spec (the normalize pass is the last guard).
|
|
43
|
+
*/
|
|
44
|
+
export declare function authorDemoSpec(input: DemoAuthorInput, deps?: DemoAuthorDeps): Promise<DemoAuthorResult>;
|
|
45
|
+
//# sourceMappingURL=demoAuthor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demoAuthor.d.ts","sourceRoot":"","sources":["../../../src/services/demo/demoAuthor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EACL,eAAe,EAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAqB,MAAM,uBAAuB,CAAC;AAqB3E,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,uDAAuD;IACvD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,0DAA0D;IAC1D,SAAS,EAAE,WAAW,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9D;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAgD3B"}
|