@mixio-pro/kalaasetu-mcp 2.3.31 → 2.3.33
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/fal-config.json +34 -0
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/tools/get-status.ts +24 -7
- package/src/tools/image-to-video.ts +12 -13
- package/src/tools/ingredients-to-video.ts +10 -46
- package/src/tools/mixio/multi-angle-batch.ts +359 -64
- package/src/tools/mixio/multi-angle.ts +339 -62
- package/src/utils/tool-credits.ts +15 -5
- package/src/utils/vertex-endpoint.ts +85 -0
- package/src/.cache/fal-config.json +0 -539
package/fal-config.json
CHANGED
|
@@ -143,6 +143,40 @@
|
|
|
143
143
|
"default": "replace_audio_and_video"
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"presetName": "multi_angle",
|
|
149
|
+
"intent": "Generate a single multi-angle image edit using Qwen Image Edit with the Mixio multi-angle LoRA.",
|
|
150
|
+
"modelId": "fal-ai/qwen-image-edit-2509-lora",
|
|
151
|
+
"inputType": "image",
|
|
152
|
+
"outputType": "image",
|
|
153
|
+
"enabled": false,
|
|
154
|
+
"pricing": {
|
|
155
|
+
"baseCredits": 15,
|
|
156
|
+
"rounding": "ceil"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"presetName": "multi_angle_batch",
|
|
161
|
+
"intent": "Generate multiple multi-angle image edits using Qwen Image Edit with the Mixio multi-angle LoRA.",
|
|
162
|
+
"modelId": "fal-ai/qwen-image-edit-2509-lora",
|
|
163
|
+
"inputType": "image",
|
|
164
|
+
"outputType": "image",
|
|
165
|
+
"enabled": false,
|
|
166
|
+
"pricing": {
|
|
167
|
+
"baseCredits": 15,
|
|
168
|
+
"modifiers": [
|
|
169
|
+
{
|
|
170
|
+
"field": "batch_count",
|
|
171
|
+
"mode": "step",
|
|
172
|
+
"operation": "add",
|
|
173
|
+
"step": 1,
|
|
174
|
+
"offset": 1,
|
|
175
|
+
"valuePerStep": 15
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
"rounding": "ceil"
|
|
179
|
+
}
|
|
146
180
|
}
|
|
147
181
|
]
|
|
148
182
|
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -100,6 +100,8 @@ async function main() {
|
|
|
100
100
|
server.addTool(falGetPresetDetails);
|
|
101
101
|
server.addTool(falUploadFile);
|
|
102
102
|
server.addTool(falGenerate);
|
|
103
|
+
server.addTool(mixioMultiAngle);
|
|
104
|
+
server.addTool(mixioMultiAngleBatch);
|
|
103
105
|
// 4. Register Dynamic FAL AI Tools
|
|
104
106
|
// These are now based on potentially synced remote config
|
|
105
107
|
const falTools = createAllFalTools();
|
|
@@ -109,8 +111,6 @@ async function main() {
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
// 4. Add Mixio Tools
|
|
112
|
-
// server.addTool(mixioMultiAngle);
|
|
113
|
-
// server.addTool(mixioMultiAngleBatch);
|
|
114
114
|
// server.addTool(mixioNextScene);
|
|
115
115
|
// server.addTool(mixioFaceSwap);
|
|
116
116
|
if (process.env.GROK_ENABLED === "true") {
|
package/src/tools/get-status.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { getGoogleAccessToken } from "../utils/google-auth";
|
|
|
10
10
|
import { sanitizeResponse } from "../utils/sanitize";
|
|
11
11
|
import { getStorage } from "../storage";
|
|
12
12
|
import * as path from "path";
|
|
13
|
+
import { parseVertexEndpoint, buildVertexCompositeEndpoint } from "../utils/vertex-endpoint";
|
|
13
14
|
|
|
14
15
|
function getFalKey(): string {
|
|
15
16
|
const falKey = process.env.FAL_KEY;
|
|
@@ -352,9 +353,12 @@ export const getGenerationStatus = {
|
|
|
352
353
|
if (resume_endpoint.includes("||")) {
|
|
353
354
|
const parts = resume_endpoint.split("||");
|
|
354
355
|
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
356
|
+
// Domain-specific detection
|
|
357
|
+
const isFal = parts?.[0]?.includes("fal.run");
|
|
358
|
+
const isVertex = parts?.[0]?.includes("googleapis.com") || parts?.[1]?.includes("v1/projects/");
|
|
359
|
+
|
|
360
|
+
if (isFal) {
|
|
361
|
+
originalEndpoint = parts[0]!;
|
|
358
362
|
// Part 1 is tracking context (base64)
|
|
359
363
|
if (parts.length >= 2 && parts[1]) {
|
|
360
364
|
try {
|
|
@@ -370,27 +374,40 @@ export const getGenerationStatus = {
|
|
|
370
374
|
if (parts.length >= 3 && parts[2] && !outputPath) {
|
|
371
375
|
outputPath = parts[2];
|
|
372
376
|
}
|
|
377
|
+
} else if (isVertex) {
|
|
378
|
+
// New robust Vertex parsing
|
|
379
|
+
// For get_generation_status, we don't necessarily know the modelId here,
|
|
380
|
+
// but parseVertexEndpoint uses it mainly for constructing the default op name.
|
|
381
|
+
// Since we HAVE a composite endpoint, the modelId passed to parseVertexEndpoint won't be used for the op name.
|
|
382
|
+
const parsed = parseVertexEndpoint(resume_endpoint, "veo-3.1-lite-generate-001");
|
|
383
|
+
originalEndpoint = buildVertexCompositeEndpoint(parsed.fetchUrl, parsed.operationName);
|
|
384
|
+
outputPath = args.output_path || parsed.outputPath || outputPath;
|
|
385
|
+
|
|
386
|
+
if (parsed.trackingContext) {
|
|
387
|
+
try {
|
|
388
|
+
const { decodeTrackingContext } = await import("../utils/endpoint-encoder");
|
|
389
|
+
trackingContext = decodeTrackingContext(parsed.trackingContext);
|
|
390
|
+
} catch (err) {}
|
|
391
|
+
}
|
|
373
392
|
} else {
|
|
374
|
-
// Vertex logic
|
|
393
|
+
// Legacy/Fallback Vertex logic
|
|
375
394
|
const lastPart = parts[parts.length - 1];
|
|
376
395
|
if (
|
|
377
396
|
lastPart &&
|
|
378
397
|
!lastPart.startsWith("http") &&
|
|
379
398
|
!lastPart.includes("/") &&
|
|
380
|
-
!lastPart.includes("mixio-pro")
|
|
399
|
+
!lastPart.includes("mixio-pro")
|
|
381
400
|
) {
|
|
382
401
|
try {
|
|
383
402
|
const { decodeTrackingContext } =
|
|
384
403
|
await import("../utils/endpoint-encoder");
|
|
385
404
|
trackingContext = decodeTrackingContext(lastPart);
|
|
386
405
|
if (trackingContext?.toolName) {
|
|
387
|
-
// Remove the tracking piece from the endpoint we pass to handlers
|
|
388
406
|
originalEndpoint = parts.slice(0, -1).join("||");
|
|
389
407
|
}
|
|
390
408
|
} catch (err) {}
|
|
391
409
|
}
|
|
392
410
|
|
|
393
|
-
// For Vertex, if outputPath is not explicitly provided, it might be in the 3rd part of originalEndpoint
|
|
394
411
|
if (!outputPath) {
|
|
395
412
|
const vertexParts = originalEndpoint.split("||");
|
|
396
413
|
if (vertexParts.length >= 3) {
|
|
@@ -5,6 +5,7 @@ import { safeToolExecute, extractPrimitiveArgs } from "../utils/tool-wrapper";
|
|
|
5
5
|
|
|
6
6
|
import { getGoogleAccessToken } from "../utils/google-auth";
|
|
7
7
|
import { checkVertexStatus } from "./get-status";
|
|
8
|
+
import { parseVertexEndpoint, buildVertexCompositeEndpoint, buildVertexFetchUrl } from "../utils/vertex-endpoint";
|
|
8
9
|
|
|
9
10
|
async function wait(ms: number): Promise<void> {
|
|
10
11
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -222,18 +223,17 @@ export const imageToVideo = {
|
|
|
222
223
|
throw new Error(`Google Cloud authentication failed: ${errorMsg}`);
|
|
223
224
|
}
|
|
224
225
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// If resuming, reconstruct the full operation path from the UUID
|
|
226
|
+
let fetchUrl = buildVertexFetchUrl(modelId, projectId, location);
|
|
228
227
|
let operationName: string | undefined;
|
|
228
|
+
let outputPath = args.output_path || "";
|
|
229
|
+
let preservedTrackingContext: string | undefined;
|
|
230
|
+
|
|
229
231
|
if (args.resume_endpoint) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
operationName = `projects/${projectId}/locations/${location}/publishers/google/models/${modelId}/operations/${args.resume_endpoint}`;
|
|
236
|
-
}
|
|
232
|
+
const parsed = parseVertexEndpoint(args.resume_endpoint, modelId, projectId, location);
|
|
233
|
+
fetchUrl = parsed.fetchUrl;
|
|
234
|
+
operationName = parsed.operationName;
|
|
235
|
+
outputPath = parsed.outputPath || outputPath;
|
|
236
|
+
preservedTrackingContext = parsed.trackingContext;
|
|
237
237
|
}
|
|
238
238
|
let current: any;
|
|
239
239
|
|
|
@@ -367,8 +367,7 @@ export const imageToVideo = {
|
|
|
367
367
|
|
|
368
368
|
// Construct the composite resume_endpoint: fetchUrl||operationName||outputPath
|
|
369
369
|
// This allows get_generation_status to use the URL directly and preserve output_path
|
|
370
|
-
const
|
|
371
|
-
const compositeResumeEndpoint = `${fetchUrl}||${operationName}||${outputPathPart}`;
|
|
370
|
+
const compositeResumeEndpoint = buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath);
|
|
372
371
|
|
|
373
372
|
// Stream the resume_endpoint to the LLM immediately (before polling starts)
|
|
374
373
|
// This way the LLM has it even if MCP client times out during polling
|
|
@@ -428,7 +427,7 @@ export const imageToVideo = {
|
|
|
428
427
|
return JSON.stringify({
|
|
429
428
|
status: "IN_PROGRESS",
|
|
430
429
|
request_id: operationName,
|
|
431
|
-
resume_endpoint:
|
|
430
|
+
resume_endpoint: buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath, packedTracking),
|
|
432
431
|
message:
|
|
433
432
|
"Still in progress. Call this tool again with resume_endpoint to continue checking.",
|
|
434
433
|
});
|
|
@@ -6,6 +6,11 @@ import { getGoogleAccessToken } from "../utils/google-auth";
|
|
|
6
6
|
import { ensureLocalFile } from "../utils/url-file";
|
|
7
7
|
import { encodeTrackingContext } from "../utils/endpoint-encoder";
|
|
8
8
|
import { checkVertexStatus } from "./get-status";
|
|
9
|
+
import {
|
|
10
|
+
parseVertexEndpoint,
|
|
11
|
+
buildVertexCompositeEndpoint,
|
|
12
|
+
buildVertexFetchUrl
|
|
13
|
+
} from "../utils/vertex-endpoint";
|
|
9
14
|
|
|
10
15
|
const DEFAULT_PROJECT_ID = "mixio-pro";
|
|
11
16
|
const DEFAULT_LOCATION = "us-central1";
|
|
@@ -24,12 +29,7 @@ type ToolContext = {
|
|
|
24
29
|
requestId?: string;
|
|
25
30
|
};
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
fetchUrl: string;
|
|
29
|
-
operationName?: string;
|
|
30
|
-
outputPath?: string;
|
|
31
|
-
trackingContext?: string;
|
|
32
|
-
}
|
|
32
|
+
|
|
33
33
|
|
|
34
34
|
async function wait(ms: number): Promise<void> {
|
|
35
35
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -84,43 +84,7 @@ async function fileToBase64(
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
function buildFetchUrl(modelId: string): string {
|
|
88
|
-
return `https://${DEFAULT_LOCATION}-aiplatform.googleapis.com/v1/projects/${DEFAULT_PROJECT_ID}/locations/${DEFAULT_LOCATION}/publishers/google/models/${modelId}:fetchPredictOperation`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function parseResumeEndpoint(
|
|
92
|
-
resumeEndpoint: string,
|
|
93
|
-
modelId: string,
|
|
94
|
-
): ParsedResumeEndpoint {
|
|
95
|
-
const defaultFetchUrl = buildFetchUrl(modelId);
|
|
96
|
-
|
|
97
|
-
if (resumeEndpoint.includes("||")) {
|
|
98
|
-
const parts = resumeEndpoint.split("||");
|
|
99
|
-
const fetchUrl = parts[0] || "";
|
|
100
|
-
const operationName = parts[1] || "";
|
|
101
|
-
|
|
102
|
-
if (fetchUrl.startsWith("http") && operationName) {
|
|
103
|
-
return {
|
|
104
|
-
fetchUrl,
|
|
105
|
-
operationName,
|
|
106
|
-
outputPath: parts[2] || "",
|
|
107
|
-
trackingContext: parts.length > 3 ? parts.slice(3).join("||") : undefined,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (resumeEndpoint.includes("/")) {
|
|
113
|
-
return {
|
|
114
|
-
fetchUrl: defaultFetchUrl,
|
|
115
|
-
operationName: resumeEndpoint,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
87
|
|
|
119
|
-
return {
|
|
120
|
-
fetchUrl: defaultFetchUrl,
|
|
121
|
-
operationName: `projects/${DEFAULT_PROJECT_ID}/locations/${DEFAULT_LOCATION}/publishers/google/models/${modelId}/operations/${resumeEndpoint}`,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
88
|
|
|
125
89
|
function normalizeDurationSeconds(durationSecondsRaw?: string): number {
|
|
126
90
|
const requestedDuration = parseInt(durationSecondsRaw || "6");
|
|
@@ -343,14 +307,14 @@ export const ingredientsToVideo = {
|
|
|
343
307
|
throw new Error(`Google Cloud authentication failed: ${errorMessage}`);
|
|
344
308
|
}
|
|
345
309
|
|
|
346
|
-
let fetchUrl =
|
|
310
|
+
let fetchUrl = buildVertexFetchUrl(modelId);
|
|
347
311
|
let operationName: string | undefined;
|
|
348
312
|
let outputPath = args.output_path || "";
|
|
349
313
|
let preservedTrackingContext: string | undefined;
|
|
350
314
|
let current: any;
|
|
351
315
|
|
|
352
316
|
if (args.resume_endpoint) {
|
|
353
|
-
const parsedResume =
|
|
317
|
+
const parsedResume = parseVertexEndpoint(args.resume_endpoint, modelId);
|
|
354
318
|
fetchUrl = parsedResume.fetchUrl;
|
|
355
319
|
operationName = parsedResume.operationName;
|
|
356
320
|
outputPath = parsedResume.outputPath || outputPath;
|
|
@@ -418,7 +382,7 @@ export const ingredientsToVideo = {
|
|
|
418
382
|
);
|
|
419
383
|
}
|
|
420
384
|
|
|
421
|
-
const compositeResumeEndpoint =
|
|
385
|
+
const compositeResumeEndpoint = buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath);
|
|
422
386
|
|
|
423
387
|
if (context?.streamContent) {
|
|
424
388
|
await context.streamContent({
|
|
@@ -471,7 +435,7 @@ export const ingredientsToVideo = {
|
|
|
471
435
|
return JSON.stringify({
|
|
472
436
|
status: "IN_PROGRESS",
|
|
473
437
|
request_id: operationName,
|
|
474
|
-
resume_endpoint:
|
|
438
|
+
resume_endpoint: buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath, packedTracking),
|
|
475
439
|
message:
|
|
476
440
|
"Still in progress. Call this tool again with resume_endpoint to continue checking.",
|
|
477
441
|
});
|