@mixio-pro/kalaasetu-mcp 1.1.2 → 1.1.4
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/package.json +1 -1
- package/src/index.ts +24 -19
- package/src/test-context.ts +52 -0
- package/src/test-error-handling.ts +31 -0
- package/src/tools/fal/config.ts +34 -0
- package/src/tools/fal/generate.ts +146 -0
- package/src/tools/fal/index.ts +14 -0
- package/src/tools/fal/models.ts +95 -0
- package/src/tools/fal/storage.ts +119 -0
- package/src/tools/gemini.ts +258 -237
- package/src/tools/image-to-video.ts +206 -190
- package/src/tools/perplexity.ts +192 -154
- package/src/tools/youtube.ts +51 -33
- package/src/utils/index.ts +0 -1
- package/src/utils/tool-wrapper.ts +86 -0
- package/src/tools/hunyuan-avatar.ts +0 -160
- package/src/tools/infinitalk.ts +0 -156
- package/src/utils/fal.utils.ts +0 -53
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { callFalModel } from "../utils/fal.utils";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Calculate number of frames based on audio duration at 25 FPS
|
|
6
|
-
* Adds 1 second buffer to ensure complete audio coverage
|
|
7
|
-
*/
|
|
8
|
-
function calculateFramesFromAudioDuration(
|
|
9
|
-
audioDurationSeconds: number
|
|
10
|
-
): number {
|
|
11
|
-
const totalDuration = audioDurationSeconds + 1; // Add 1 second buffer
|
|
12
|
-
const frames = Math.round(totalDuration * 25); // 25 FPS
|
|
13
|
-
|
|
14
|
-
// Clamp to valid range (129-401 frames)
|
|
15
|
-
return Math.max(129, Math.min(401, frames));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* FAL AI Hunyuan Avatar - High-Fidelity Audio-Driven Human Animation
|
|
20
|
-
*/
|
|
21
|
-
export const hunyuanAvatar = {
|
|
22
|
-
name: "hunyuan_avatar",
|
|
23
|
-
description:
|
|
24
|
-
"Generate high-fidelity audio-driven human animation videos using FAL AI Hunyuan Avatar. Creates realistic talking avatar animations from an image and audio file.",
|
|
25
|
-
parameters: z.object({
|
|
26
|
-
image_url: z
|
|
27
|
-
.string()
|
|
28
|
-
.describe("Public URL of the reference image for the avatar."),
|
|
29
|
-
audio_url: z
|
|
30
|
-
.string()
|
|
31
|
-
.describe("Public URL of the audio file to drive the animation."),
|
|
32
|
-
audio_duration_seconds: z
|
|
33
|
-
.number()
|
|
34
|
-
.optional()
|
|
35
|
-
.describe(
|
|
36
|
-
"Duration of the audio in seconds. If provided, will automatically calculate optimal frames (audio duration + 1 second buffer at 25 FPS)."
|
|
37
|
-
),
|
|
38
|
-
text: z
|
|
39
|
-
.string()
|
|
40
|
-
.optional()
|
|
41
|
-
.describe(
|
|
42
|
-
"Text prompt describing the scene. Default: 'A cat is singing.'"
|
|
43
|
-
),
|
|
44
|
-
num_frames: z
|
|
45
|
-
.number()
|
|
46
|
-
.optional()
|
|
47
|
-
.describe(
|
|
48
|
-
"Number of video frames to generate at 25 FPS. Range: 129 to 401. If not provided and audio_duration_seconds is given, will be calculated automatically. Default: 129"
|
|
49
|
-
),
|
|
50
|
-
num_inference_steps: z
|
|
51
|
-
.number()
|
|
52
|
-
.optional()
|
|
53
|
-
.describe(
|
|
54
|
-
"Number of inference steps for sampling. Higher values give better quality but take longer. Range: 30 to 50. Default: 30"
|
|
55
|
-
),
|
|
56
|
-
turbo_mode: z
|
|
57
|
-
.boolean()
|
|
58
|
-
.optional()
|
|
59
|
-
.describe(
|
|
60
|
-
"If true, the video will be generated faster with no noticeable degradation in visual quality. Default: true"
|
|
61
|
-
),
|
|
62
|
-
seed: z.number().optional().describe("Random seed for generation."),
|
|
63
|
-
fal_key: z
|
|
64
|
-
.string()
|
|
65
|
-
.optional()
|
|
66
|
-
.describe(
|
|
67
|
-
"FAL API key. If not provided, will use FAL_KEY environment variable."
|
|
68
|
-
),
|
|
69
|
-
}),
|
|
70
|
-
execute: async (args: {
|
|
71
|
-
image_url: string;
|
|
72
|
-
audio_url: string;
|
|
73
|
-
audio_duration_seconds?: number;
|
|
74
|
-
text?: string;
|
|
75
|
-
num_frames?: number;
|
|
76
|
-
num_inference_steps?: number;
|
|
77
|
-
turbo_mode?: boolean;
|
|
78
|
-
seed?: number;
|
|
79
|
-
fal_key?: string;
|
|
80
|
-
}) => {
|
|
81
|
-
// Calculate frames from audio duration if provided and num_frames not specified
|
|
82
|
-
let calculatedFrames = args.num_frames;
|
|
83
|
-
if (
|
|
84
|
-
args.audio_duration_seconds !== undefined &&
|
|
85
|
-
args.num_frames === undefined
|
|
86
|
-
) {
|
|
87
|
-
calculatedFrames = calculateFramesFromAudioDuration(
|
|
88
|
-
args.audio_duration_seconds
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Validate num_frames range if provided
|
|
93
|
-
if (
|
|
94
|
-
calculatedFrames !== undefined &&
|
|
95
|
-
(calculatedFrames < 129 || calculatedFrames > 401)
|
|
96
|
-
) {
|
|
97
|
-
throw new Error("num_frames must be between 129 and 401");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Validate num_inference_steps range if provided
|
|
101
|
-
if (
|
|
102
|
-
args.num_inference_steps !== undefined &&
|
|
103
|
-
(args.num_inference_steps < 30 || args.num_inference_steps > 50)
|
|
104
|
-
) {
|
|
105
|
-
throw new Error("num_inference_steps must be between 30 and 50");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Build input payload
|
|
109
|
-
const input: any = {
|
|
110
|
-
image_url: args.image_url,
|
|
111
|
-
audio_url: args.audio_url,
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// Add optional parameters if provided
|
|
115
|
-
if (args.text !== undefined) {
|
|
116
|
-
input.text = args.text;
|
|
117
|
-
}
|
|
118
|
-
if (calculatedFrames !== undefined) {
|
|
119
|
-
input.num_frames = calculatedFrames;
|
|
120
|
-
}
|
|
121
|
-
if (args.num_inference_steps !== undefined) {
|
|
122
|
-
input.num_inference_steps = args.num_inference_steps;
|
|
123
|
-
}
|
|
124
|
-
if (args.turbo_mode !== undefined) {
|
|
125
|
-
input.turbo_mode = args.turbo_mode;
|
|
126
|
-
}
|
|
127
|
-
if (args.seed !== undefined) {
|
|
128
|
-
input.seed = args.seed;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const result = await callFalModel("fal-ai/hunyuan-avatar", input, {
|
|
132
|
-
falKey: args.fal_key,
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Extract video data from the response
|
|
136
|
-
const videoData = result.data?.video;
|
|
137
|
-
|
|
138
|
-
if (!videoData || !videoData.url) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`No video data in completed response: ${JSON.stringify(result.data)}`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const videoUrl = videoData.url;
|
|
145
|
-
const fileName = videoData.file_name || "hunyuan_avatar.mp4";
|
|
146
|
-
|
|
147
|
-
return JSON.stringify({
|
|
148
|
-
videos: [
|
|
149
|
-
{
|
|
150
|
-
url: videoUrl,
|
|
151
|
-
filename: fileName,
|
|
152
|
-
mimeType: "video/mp4",
|
|
153
|
-
filesize: videoData.file_size,
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
message: "Hunyuan Avatar video generated successfully",
|
|
157
|
-
requestId: result.requestId,
|
|
158
|
-
});
|
|
159
|
-
},
|
|
160
|
-
};
|
package/src/tools/infinitalk.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { callFalModel } from "../utils/fal.utils";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Calculate number of frames based on audio duration at 25 FPS
|
|
6
|
-
* Adds 1 second buffer to ensure complete audio coverage
|
|
7
|
-
*/
|
|
8
|
-
function calculateFramesFromAudioDuration(
|
|
9
|
-
audioDurationSeconds: number
|
|
10
|
-
): number {
|
|
11
|
-
const totalDuration = audioDurationSeconds + 1; // Add 1 second buffer
|
|
12
|
-
const frames = Math.round(totalDuration * 25); // 25 FPS
|
|
13
|
-
|
|
14
|
-
// Clamp to valid range (41-721 frames)
|
|
15
|
-
return Math.max(41, Math.min(721, frames));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* FAL AI Infinitalk - Generate talking avatar video from image and audio
|
|
20
|
-
*/
|
|
21
|
-
export const infinitalk = {
|
|
22
|
-
name: "infinitalk",
|
|
23
|
-
description:
|
|
24
|
-
"Generate a talking avatar video from an image and audio file using FAL AI Infinitalk. The avatar lip-syncs to the provided audio with natural facial expressions.",
|
|
25
|
-
parameters: z.object({
|
|
26
|
-
image_url: z
|
|
27
|
-
.string()
|
|
28
|
-
.describe(
|
|
29
|
-
"Public URL of the input image. If the input image does not match the chosen aspect ratio, it is resized and center cropped."
|
|
30
|
-
),
|
|
31
|
-
audio_url: z
|
|
32
|
-
.string()
|
|
33
|
-
.describe("The Public URL of the audio file for lip-sync generation."),
|
|
34
|
-
audio_duration_seconds: z
|
|
35
|
-
.number()
|
|
36
|
-
.optional()
|
|
37
|
-
.describe(
|
|
38
|
-
"Duration of the audio in seconds. If provided, will automatically calculate optimal frames (audio duration + 1 second buffer at 25 FPS)."
|
|
39
|
-
),
|
|
40
|
-
prompt: z
|
|
41
|
-
.string()
|
|
42
|
-
.describe(
|
|
43
|
-
"The text prompt to guide video generation (e.g., 'A woman with colorful hair talking on a podcast')"
|
|
44
|
-
),
|
|
45
|
-
num_frames: z
|
|
46
|
-
.number()
|
|
47
|
-
.optional()
|
|
48
|
-
.describe(
|
|
49
|
-
"Number of frames to generate. Must be between 41 to 721. If not provided and audio_duration_seconds is given, will be calculated automatically. Default: 145"
|
|
50
|
-
),
|
|
51
|
-
resolution: z
|
|
52
|
-
.enum(["480p", "720p"])
|
|
53
|
-
.optional()
|
|
54
|
-
.describe("Resolution of the video to generate. Default: '480p'"),
|
|
55
|
-
seed: z
|
|
56
|
-
.number()
|
|
57
|
-
.optional()
|
|
58
|
-
.describe(
|
|
59
|
-
"Random seed for reproducibility. If not provided, a random seed is chosen. Default: 42"
|
|
60
|
-
),
|
|
61
|
-
acceleration: z
|
|
62
|
-
.enum(["none", "regular", "high"])
|
|
63
|
-
.optional()
|
|
64
|
-
.describe(
|
|
65
|
-
"The acceleration level to use for generation. Default: 'regular'"
|
|
66
|
-
),
|
|
67
|
-
fal_key: z
|
|
68
|
-
.string()
|
|
69
|
-
.optional()
|
|
70
|
-
.describe(
|
|
71
|
-
"FAL API key. If not provided, will use FAL_KEY environment variable."
|
|
72
|
-
),
|
|
73
|
-
}),
|
|
74
|
-
execute: async (args: {
|
|
75
|
-
image_url: string;
|
|
76
|
-
audio_url: string;
|
|
77
|
-
audio_duration_seconds?: number;
|
|
78
|
-
prompt: string;
|
|
79
|
-
num_frames?: number;
|
|
80
|
-
resolution?: "480p" | "720p";
|
|
81
|
-
seed?: number;
|
|
82
|
-
acceleration?: "none" | "regular" | "high";
|
|
83
|
-
fal_key?: string;
|
|
84
|
-
}) => {
|
|
85
|
-
// Calculate frames from audio duration if provided and num_frames not specified
|
|
86
|
-
let calculatedFrames = args.num_frames;
|
|
87
|
-
if (
|
|
88
|
-
args.audio_duration_seconds !== undefined &&
|
|
89
|
-
args.num_frames === undefined
|
|
90
|
-
) {
|
|
91
|
-
calculatedFrames = calculateFramesFromAudioDuration(
|
|
92
|
-
args.audio_duration_seconds
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Validate num_frames range if provided
|
|
97
|
-
if (
|
|
98
|
-
calculatedFrames !== undefined &&
|
|
99
|
-
(calculatedFrames < 41 || calculatedFrames > 721)
|
|
100
|
-
) {
|
|
101
|
-
throw new Error("num_frames must be between 41 and 721");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Build input payload
|
|
105
|
-
const input: any = {
|
|
106
|
-
image_url: args.image_url,
|
|
107
|
-
audio_url: args.audio_url,
|
|
108
|
-
prompt: args.prompt,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// Add optional parameters if provided
|
|
112
|
-
if (calculatedFrames !== undefined) {
|
|
113
|
-
input.num_frames = calculatedFrames;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
input.resolution = args.resolution || "480p";
|
|
117
|
-
|
|
118
|
-
if (args.seed !== undefined) {
|
|
119
|
-
input.seed = args.seed;
|
|
120
|
-
}
|
|
121
|
-
if (args.acceleration !== undefined) {
|
|
122
|
-
input.acceleration = args.acceleration;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const result = await callFalModel("fal-ai/infinitalk", input, {
|
|
126
|
-
falKey: args.fal_key,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Extract video data from the response
|
|
130
|
-
const videoData = result.data?.video;
|
|
131
|
-
const seed = result.data?.seed;
|
|
132
|
-
|
|
133
|
-
if (!videoData || !videoData.url) {
|
|
134
|
-
throw new Error(
|
|
135
|
-
`No video data in completed response: ${JSON.stringify(result.data)}`
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const videoUrl = videoData.url;
|
|
140
|
-
const fileName = videoData.file_name || "infinitalk.mp4";
|
|
141
|
-
|
|
142
|
-
return JSON.stringify({
|
|
143
|
-
videos: [
|
|
144
|
-
{
|
|
145
|
-
url: videoUrl,
|
|
146
|
-
filename: fileName,
|
|
147
|
-
mimeType: "video/mp4",
|
|
148
|
-
filesize: videoData.file_size,
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
message: "Infinitalk video generated successfully",
|
|
152
|
-
seed: seed,
|
|
153
|
-
requestId: result.requestId,
|
|
154
|
-
});
|
|
155
|
-
},
|
|
156
|
-
};
|
package/src/utils/fal.utils.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { fal } from "@fal-ai/client";
|
|
2
|
-
|
|
3
|
-
export async function callFalModel(
|
|
4
|
-
modelName: string,
|
|
5
|
-
input: any,
|
|
6
|
-
options: { falKey?: string; logs?: boolean } = {}
|
|
7
|
-
) {
|
|
8
|
-
const { falKey, logs = true } = options;
|
|
9
|
-
const key = falKey || process.env.FAL_KEY;
|
|
10
|
-
if (!key) {
|
|
11
|
-
throw new Error(
|
|
12
|
-
"FAL_KEY is required. Provide it via fal_key parameter or FAL_KEY environment variable."
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
fal.config({
|
|
17
|
-
credentials: key,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
console.error(`[${modelName}] Submitting request to FAL AI...`);
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const result = await fal.subscribe(modelName, {
|
|
24
|
-
input,
|
|
25
|
-
logs,
|
|
26
|
-
onQueueUpdate: (update) => {
|
|
27
|
-
if (update.status === "IN_PROGRESS") {
|
|
28
|
-
console.error(`[${modelName}] Status: ${update.status}`);
|
|
29
|
-
if (logs && "logs" in update && update.logs) {
|
|
30
|
-
update.logs.forEach((log) => {
|
|
31
|
-
console.error(`[${modelName}] ${log.message}`);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
} else if (update.status === "IN_QUEUE") {
|
|
35
|
-
console.error(
|
|
36
|
-
`[${modelName}] Status: ${update.status} - Waiting in queue...`
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
console.error(`[${modelName}] Generation completed successfully`);
|
|
43
|
-
|
|
44
|
-
return result;
|
|
45
|
-
} catch (error: any) {
|
|
46
|
-
console.error(`[${modelName}] Error:`, error);
|
|
47
|
-
throw new Error(
|
|
48
|
-
`FAL AI ${modelName} generation failed: ${
|
|
49
|
-
error.message || JSON.stringify(error)
|
|
50
|
-
}`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|