@kolbo/mcp 1.44.0 ā 1.46.0
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/apps/widgets/generation.js +1 -1
- package/src/index.js +2 -6
- package/src/tools/chat.js +1 -1
- package/src/tools/generate.js +212 -40
- package/src/tools/models.js +6 -12
package/package.json
CHANGED
|
@@ -438,7 +438,7 @@ function renderActions(sc) {
|
|
|
438
438
|
openPromptRow('Describe the motion (optional)ā¦', function (text) {
|
|
439
439
|
window.kolbo.sendMessage('Animate this image into a short video' +
|
|
440
440
|
'\\nš¬ Reference image: ' + currentUrl() +
|
|
441
|
-
'\\nModel:
|
|
441
|
+
'\\nModel: pick a specific image-to-video model that best fits this image (do NOT use Smart Select / auto)' +
|
|
442
442
|
(text ? '\\nMotion prompt: ' + text : '\\nMotion prompt: subtle cinematic motion, slow push-in'));
|
|
443
443
|
});
|
|
444
444
|
});
|
package/src/index.js
CHANGED
|
@@ -66,7 +66,6 @@ const { registerVisualDnaTools } = require('./tools/visual_dna');
|
|
|
66
66
|
const { registerMoodboardTools } = require('./tools/moodboards');
|
|
67
67
|
const { registerMediaTools } = require('./tools/media');
|
|
68
68
|
const { registerPresetTools } = require('./tools/presets');
|
|
69
|
-
const { registerAppBuilderTools } = require('./tools/app_builder');
|
|
70
69
|
const { registerArtifactTools } = require('./tools/artifacts');
|
|
71
70
|
const { registerProjectTools } = require('./tools/projects');
|
|
72
71
|
const { registerAgentTools } = require('./tools/agents');
|
|
@@ -74,7 +73,6 @@ const { registerDocTools } = require('./tools/docs');
|
|
|
74
73
|
const { registerVoiceTools } = require('./tools/voices');
|
|
75
74
|
const { registerMusicLibraryTools } = require('./tools/music_library');
|
|
76
75
|
const { registerStockLibraryTools } = require('./tools/stock_library');
|
|
77
|
-
const { registerShortsCreatorTools } = require('./tools/shorts_creator');
|
|
78
76
|
const { registerApps, attachToolWidgetMeta } = require('./apps');
|
|
79
77
|
|
|
80
78
|
/**
|
|
@@ -113,13 +111,13 @@ function createServer(opts = {}) {
|
|
|
113
111
|
'PROJECT CONTRACT (read this before generating anything):',
|
|
114
112
|
'Everything in Kolbo lives inside a PROJECT ā sessions, generations, and media are all project-scoped.',
|
|
115
113
|
'1. When the user names a project ("in my Acme project", "for the summer campaign"), call `list_projects` ONCE to resolve the name to an id, then pass that id as `project_id` on EVERY subsequent generate_* / chat_send_message / upload_media call in the conversation. The target project is per-call, NOT sticky ā any call that omits `project_id` silently lands in the default "API Generations" bucket (flagged is_default:true), which users experience as their work going to the wrong project.',
|
|
116
|
-
'2. `list_projects` lists the user\'s platform projects (for generations/media/chat).
|
|
114
|
+
'2. `list_projects` lists the user\'s platform projects (for generations/media/chat). Do not confuse a generation `session_id` with any other session type ā they are not interchangeable.',
|
|
117
115
|
'3. Misplaced work is fixable: `move_media` / `bulk_move_media` / `move_folder_contents` move media items between projects; `move_session` moves a whole session (plus its media) to another project. If the user says a generation landed in the wrong project, move it rather than regenerating.',
|
|
118
116
|
'4. If the user has not mentioned any project, omit `project_id` ā the default bucket is correct in that case. Do not ask which project to use unless the user\'s intent is ambiguous.',
|
|
119
117
|
'5. Written deliverables (plans, briefs, scripts, research summaries) can live in Kolbo too: author them as AI Docs with `create_doc` (project-scoped, editable in the app, shareable via `share_doc`).',
|
|
120
118
|
'6. DIRECTOR / BATCH JOBS: generate_creative_director runs its scenes (image OR video) in parallel and only reports state="completed" once EVERY scene is terminal. Video batches can take many minutes. If the tool returns `_timed_out:true`, the batch is STILL RUNNING on the server ā call `get_creative_director_status` with the returned generation_id and keep checking until state="completed" to collect all scene outputs. NEVER conclude a Director run failed and fall back to plain generate_image/generate_video without first checking status ā doing so wastes the user\'s credits by paying twice. If scenes already carry image_urls/video_urls, they are done; do not regenerate.',
|
|
121
119
|
'7. SESSION CONTINUITY: keep one workflow in ONE session. chat_send_message and the generation tools return a `session_id` ā for follow-ups, refinements, retries, or additional steps on the SAME task/theme, pass that same `session_id` back on the next call instead of starting fresh. Only OMIT session_id (start a new session) when the user genuinely switches to an unrelated task. Do not open a new conversation/session for every message of the same workflow ā it fragments the user\'s history and loses context.',
|
|
122
|
-
'8. LOCAL FILES /
|
|
120
|
+
'8. LOCAL FILES / REFERENCE MEDIA ā HOW TO HANDLE EVERY CASE: (A) User has a LOCAL file (audio, video, image, document) on their machine: if you have filesystem access (Claude Desktop / Code / IDE / any stdio MCP client) ā call `upload_media` with the absolute local path OR pass the path directly to tools like `transcribe_audio` which accept local paths natively. If you have NO filesystem access (claude.ai browser/mobile) ā call `media_upload_widget` IMMEDIATELY, an upload card appears, the user uploads, and a `media.kolbo.ai` CDN URL comes back ā use that URL for any follow-up tool call. (B) You already have a public URL (media.kolbo.ai, any CDN, any direct link) ā pass it directly to the tool. All Kolbo tools accept public URLs. NEVER search for DO Spaces keys, DigitalOcean credentials, or server-side upload credentials. NEVER ask the user to put the file on Google Drive, Dropbox, or Loom. NEVER invent or guess a URL. NEVER base64 a large file ā use upload_media instead.',
|
|
123
121
|
'9. MODEL SELECTION: ALWAYS pass a specific `model` on every generation tool ā do NOT omit it. Omitting falls back to "Smart Select" auto-routing, which we deliberately avoid because it hides the model choice from the user and often picks a generic default. Choose the model that best fits the task and the user\'s intent (quality, speed, style, capability). If you are unsure which model to use for a given type, call `list_models` with the matching `type` and pick the recommended/flagship one, then pass its `identifier`. Only use Smart Select (omit `model`) if the user EXPLICITLY asks you to auto-pick.',
|
|
124
122
|
'10. IMAGE EDITING: for ANY prompt-driven / content edit of an existing image ā "make it night", changing scene/lighting/colors, adding/removing/replacing objects, restyling ā use `generate_image_edit` (it runs on strong dedicated editing models, same as image generation). Do NOT use `edit_image` for content edits ā `edit_image` is ONLY for mechanical enhancements (upscale, reframe, remove-background, skin retouch). Its `magic_edit` operation is deprecated in favor of `generate_image_edit`.'
|
|
125
123
|
].join('\n')
|
|
@@ -138,14 +136,12 @@ function createServer(opts = {}) {
|
|
|
138
136
|
registerMoodboardTools(server, client, toolOptions);
|
|
139
137
|
registerMediaTools(server, client, toolOptions);
|
|
140
138
|
registerPresetTools(server, client, toolOptions);
|
|
141
|
-
registerAppBuilderTools(server, client, toolOptions);
|
|
142
139
|
registerArtifactTools(server, client, toolOptions);
|
|
143
140
|
registerProjectTools(server, client, toolOptions);
|
|
144
141
|
registerAgentTools(server, client, toolOptions);
|
|
145
142
|
registerDocTools(server, client, toolOptions);
|
|
146
143
|
registerMusicLibraryTools(server, client, toolOptions);
|
|
147
144
|
registerStockLibraryTools(server, client, toolOptions);
|
|
148
|
-
registerShortsCreatorTools(server, client, toolOptions);
|
|
149
145
|
|
|
150
146
|
// MCP Apps widget resources (ui://kolbo/*). Registering resources is inert
|
|
151
147
|
// for text-only hosts ā they never fetch them.
|
package/src/tools/chat.js
CHANGED
|
@@ -14,7 +14,7 @@ function registerChatTools(server, client) {
|
|
|
14
14
|
'Send a chat message to Kolbo AI. Starts a new conversation (omit session_id) or continues an existing one. Returns the assistant response when complete. Supports image/video/audio analysis via media_urls ā pass public URLs and the model auto-routes to a vision-capable model (e.g. Gemini) when media is detected. Supports web search and deep think modes.',
|
|
15
15
|
{
|
|
16
16
|
message: z.string().describe('The user message to send'),
|
|
17
|
-
model: z.string().optional().describe('Model identifier (e.g. "gpt-4o", "claude-sonnet-4-6").
|
|
17
|
+
model: z.string().optional().describe('Model identifier (e.g. "gpt-4o", "claude-sonnet-4-6"). Prefer passing a SPECIFIC model (list_models type="text") ā omitting falls back to Smart Select auto-routing, which we avoid unless the user explicitly asks for auto-pick. Exception: when media_urls contains video or audio, omitting is fine ā routing goes to a Gemini vision model regardless of this field.'),
|
|
18
18
|
session_id: z.string().optional().describe('Existing chat session ID to continue. Omit to start a new conversation.'),
|
|
19
19
|
system_prompt: z.string().optional().describe('System prompt for the conversation. Only applied when creating a new session.'),
|
|
20
20
|
web_search: z.boolean().optional().describe('Enable web search for this message. Default: false'),
|
package/src/tools/generate.js
CHANGED
|
@@ -61,15 +61,16 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
61
61
|
moodboard_id: z.string().optional().describe('Moodboard ID (from list_moodboards / get_moodboard) whose master_prompt and style_guide should be applied to this generation.'),
|
|
62
62
|
enable_web_search: z.boolean().optional().describe('Enable web-search grounding for the prompt (useful for current events, brand references, real-world accuracy). Default: false'),
|
|
63
63
|
resolution: z.string().optional().describe('Image resolution tier: "1K" (~1024px), "2K" (Full HD), "3K" (QHD), or "4K" (UHD). Model-dependent ā call list_models and read supported_resolutions on the chosen model. Read resolution_multipliers on the same model to predict credit cost. Omit to use the model default.'),
|
|
64
|
+
quality: z.string().optional().describe('Quality tier for models that support it (e.g. "low", "medium", "high", "auto"). Check list_models ā supported_qualities on the chosen model. "auto" is normalised to "medium" on gpt-image-2. Omit to use the model default.'),
|
|
64
65
|
preset_id: z.string().optional().describe('Preset ID from list_presets type="image" to apply a saved style preset to this generation.'),
|
|
65
66
|
cinematic: CINEMATIC_SCHEMA,
|
|
66
67
|
project_id: projectIdField
|
|
67
68
|
},
|
|
68
|
-
async ({ prompt, model, aspect_ratio, enhance_prompt, num_images, reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, cinematic, project_id }) => {
|
|
69
|
+
async ({ prompt, model, aspect_ratio, enhance_prompt, num_images, reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, quality, preset_id, cinematic, project_id }) => {
|
|
69
70
|
model = await canonicalModelId(client, model); // lenient id resolution ("z-image" ā "z-image/turbo")
|
|
70
71
|
const gen = await client.post('/v1/generate/image', {
|
|
71
72
|
prompt, model, aspect_ratio, enhance_prompt, num_images,
|
|
72
|
-
reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, preset_id, cinematic, project_id
|
|
73
|
+
reference_images, visual_dna_ids, moodboard_id, enable_web_search, resolution, quality, preset_id, cinematic, project_id
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
if (ui()) return uiGenerating({
|
|
@@ -1157,38 +1158,109 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1157
1158
|
// āāā edit_image āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1158
1159
|
server.tool(
|
|
1159
1160
|
'edit_image',
|
|
1160
|
-
'Apply a
|
|
1161
|
+
'Apply a targeted AI edit to an existing image. Covers mechanical enhancements (upscale, reframe, remove background, skin retouching) AND creative operations (inpaint, erase, face swap, background replace, camera angle, zoom out, multi-shot grid, split/upscale). ā ļø For open-ended PROMPT-DRIVEN content edits ā "make it night", restyling, adding/removing objects ā use `generate_image_edit` instead; it runs on stronger dedicated editing models and produces better results.',
|
|
1161
1162
|
{
|
|
1162
|
-
image_url: z.string().describe('URL of the source image to edit'),
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1163
|
+
image_url: z.string().describe('URL of the primary source image to edit.'),
|
|
1164
|
+
|
|
1165
|
+
operation: z.enum([
|
|
1166
|
+
'upscale', 'clarity_upscale',
|
|
1167
|
+
'reframe', 'zoom_out',
|
|
1168
|
+
'removebg', 'background_replace',
|
|
1169
|
+
'enhance_skin',
|
|
1170
|
+
'inpaint', 'erase',
|
|
1171
|
+
'face_swap',
|
|
1172
|
+
'camera_angle',
|
|
1173
|
+
'split', 'split_upscale',
|
|
1174
|
+
'multi_shot',
|
|
1175
|
+
'magic_edit'
|
|
1176
|
+
]).describe([
|
|
1177
|
+
'Edit operation:',
|
|
1178
|
+
'"upscale" ā increase resolution by 2Ć, 3Ć, or 4Ć (use `scale`). "clarity_upscale" ā AI-powered clarity upscale with detail enhancement (use `resolution`).',
|
|
1179
|
+
'"reframe" ā change aspect ratio (requires `aspect_ratio`, e.g. "16:9" or "9:16").',
|
|
1180
|
+
'"zoom_out" ā expand the image outward, filling new areas with AI-generated content.',
|
|
1181
|
+
'"removebg" ā remove the image background, output is transparent PNG.',
|
|
1182
|
+
'"background_replace" ā remove background and replace it with AI-generated content from `prompt`.',
|
|
1183
|
+
'"enhance_skin" ā portrait skin retouching (use `skin_strength`: "subtle" | "realistic" | "pimple" | "freckle").',
|
|
1184
|
+
'"inpaint" ā paint over a masked area using `mask_image_url` (B&W mask, white = fill area) and optional `prompt`. Add reference images via `additional_images`.',
|
|
1185
|
+
'"erase" ā erase an object defined by `mask_image_url` (white = erase area).',
|
|
1186
|
+
'"face_swap" ā swap the face in `image_url` with the face from `mask_image_url` (required).',
|
|
1187
|
+
'"camera_angle" ā generate the image from a different camera angle. Set `generate_all_angles=true` for a full set. Use `prompt` to guide the angle.',
|
|
1188
|
+
'"split" ā split the image into a 3Ć3 grid of tiles. "split_upscale" ā split into a grid and upscale each tile.',
|
|
1189
|
+
'"multi_shot" ā generate a 3Ć3 multi-shot grid of scenes (uses `additional_images` as reference shots). Use `resolution` for output quality.',
|
|
1190
|
+
'"magic_edit" (DEPRECATED) ā prompt-driven content edit. Prefer `generate_image_edit` for better results.',
|
|
1191
|
+
].join(' ')),
|
|
1192
|
+
|
|
1193
|
+
model: z.string().optional()
|
|
1194
|
+
.describe('Model identifier override. Omit to use the platform default for the operation.'),
|
|
1195
|
+
|
|
1196
|
+
// āā upscale āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1197
|
+
scale: z.number().optional()
|
|
1198
|
+
.describe('Upscale factor: 2, 3, or 4. Used with operation="upscale". Default: 2.'),
|
|
1199
|
+
|
|
1200
|
+
resolution: z.string().optional()
|
|
1201
|
+
.describe('Target output resolution (e.g. "4k", "2k", "1080p"). Used with "clarity_upscale", "split_upscale", "multi_shot".'),
|
|
1202
|
+
|
|
1203
|
+
// āā reframe āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1204
|
+
aspect_ratio: z.string().optional()
|
|
1205
|
+
.describe('Target aspect ratio (e.g. "16:9", "9:16", "1:1", "4:3"). Required for operation="reframe".'),
|
|
1206
|
+
|
|
1207
|
+
// āā enhance_skin āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1168
1208
|
skin_strength: z.enum(['subtle', 'realistic', 'pimple', 'freckle']).optional()
|
|
1169
|
-
.describe('Skin enhancement
|
|
1170
|
-
|
|
1209
|
+
.describe('Skin enhancement preset. Used with operation="enhance_skin". Default: "realistic".'),
|
|
1210
|
+
|
|
1211
|
+
// āā inpaint / erase / face_swap / background_replace / zoom_out / camera_angle / magic_edit āā
|
|
1212
|
+
prompt: z.string().optional()
|
|
1213
|
+
.describe('Text instruction guiding the edit. Required for "background_replace". Used with "inpaint", "zoom_out", "camera_angle", and the deprecated "magic_edit".'),
|
|
1214
|
+
|
|
1215
|
+
mask_image_url: z.string().optional()
|
|
1216
|
+
.describe('URL of a mask image (black & white; white = affected area). Required for "inpaint" and "erase". For "face_swap", this is the face reference image.'),
|
|
1217
|
+
|
|
1218
|
+
additional_images: z.array(z.string()).optional()
|
|
1219
|
+
.describe('Extra reference image URLs (up to 8). For "inpaint": reference images that guide style/content. For "multi_shot": the set of scene reference shots. For "magic_edit": additional source images for composite edits.'),
|
|
1220
|
+
|
|
1221
|
+
// āā camera_angle āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1222
|
+
generate_all_angles: z.boolean().optional()
|
|
1223
|
+
.describe('When true, generates a full set of camera angles instead of just one. Only used with operation="camera_angle".'),
|
|
1224
|
+
|
|
1225
|
+
// āā quality / prompt enhancement āāāāāāāāāāāāāāāāāāāāāāā
|
|
1226
|
+
quality: z.string().optional()
|
|
1227
|
+
.describe('Output quality preset (e.g. "high", "standard"). Applies where the underlying model supports quality tiers.'),
|
|
1228
|
+
|
|
1229
|
+
ai_optimize: z.boolean().optional()
|
|
1230
|
+
.describe('Whether to let Kolbo AI enhance your prompt before sending to the model. Default: true. Set false to use your prompt exactly as written.'),
|
|
1231
|
+
|
|
1171
1232
|
project_id: projectIdField
|
|
1172
1233
|
},
|
|
1173
|
-
async ({
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1234
|
+
async ({
|
|
1235
|
+
image_url, operation, model, scale, aspect_ratio, skin_strength, prompt,
|
|
1236
|
+
mask_image_url, additional_images, generate_all_angles, resolution, quality, ai_optimize,
|
|
1237
|
+
project_id
|
|
1238
|
+
}) => {
|
|
1239
|
+
model = await canonicalModelId(client, model);
|
|
1240
|
+
|
|
1241
|
+
// Basic validation
|
|
1242
|
+
if (operation === 'reframe' && !aspect_ratio) throw new Error('aspect_ratio is required for reframe');
|
|
1243
|
+
if (operation === 'background_replace' && !prompt) throw new Error('prompt is required for background_replace');
|
|
1244
|
+
if (operation === 'face_swap' && !mask_image_url && !(additional_images && additional_images.length > 0)) {
|
|
1245
|
+
throw new Error('mask_image_url (face reference) is required for face_swap');
|
|
1246
|
+
}
|
|
1177
1247
|
|
|
1178
1248
|
const gen = await client.post('/v1/edit/image', {
|
|
1179
|
-
image_url, operation, model, scale, aspect_ratio, skin_strength, prompt,
|
|
1249
|
+
image_url, operation, model, scale, aspect_ratio, skin_strength, prompt,
|
|
1250
|
+
mask_image_url, additional_images, generate_all_angles, resolution, quality, ai_optimize,
|
|
1251
|
+
project_id
|
|
1180
1252
|
});
|
|
1181
1253
|
|
|
1182
1254
|
if (ui()) return uiGenerating({
|
|
1183
1255
|
tool: 'edit_image', kind: 'image', gen, client, model,
|
|
1184
1256
|
prompt: prompt || operation,
|
|
1185
|
-
settings: { mode: operation, aspect_ratio },
|
|
1257
|
+
settings: { mode: operation, aspect_ratio, scale, resolution },
|
|
1186
1258
|
reference_image: image_url
|
|
1187
1259
|
});
|
|
1188
1260
|
|
|
1189
1261
|
const result = await pollUntilDone(client, gen.generation_id, {
|
|
1190
1262
|
interval: (gen.poll_interval_hint || 5) * 1000,
|
|
1191
|
-
timeout:
|
|
1263
|
+
timeout: 300000 // 5 min ā split_upscale and multi_shot can take longer
|
|
1192
1264
|
});
|
|
1193
1265
|
|
|
1194
1266
|
return {
|
|
@@ -1208,39 +1280,139 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1208
1280
|
// āāā edit_video āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1209
1281
|
server.tool(
|
|
1210
1282
|
'edit_video',
|
|
1211
|
-
'Apply a targeted AI edit to an existing video.
|
|
1283
|
+
'Apply a targeted AI edit to an existing video. Covers both mechanical edits (upscale, reframe, remove watermark, remove background) and creative operations (generate audio, face swap, extend, lipsync, inpaint, retake, magic edit). Returns the edited video URL when complete.',
|
|
1212
1284
|
{
|
|
1213
|
-
video_url: z.string().describe('URL of the source video to edit'),
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1285
|
+
video_url: z.string().describe('URL of the source video to edit.'),
|
|
1286
|
+
|
|
1287
|
+
operation: z.enum([
|
|
1288
|
+
'upscale', 'reframe',
|
|
1289
|
+
'generate_audio', 'remove_watermark',
|
|
1290
|
+
'face_swap', 'extend', 'magic_edit',
|
|
1291
|
+
'lipsync', 'remove_background',
|
|
1292
|
+
'inpaint', 'retake'
|
|
1293
|
+
]).describe([
|
|
1294
|
+
'Edit operation:',
|
|
1295
|
+
'"upscale" ā boost to 4K/2K resolution (use `scale` for factor, `resolution` for target, `target_fps` for frame rate).',
|
|
1296
|
+
'"reframe" ā change aspect ratio (requires `aspect_ratio`; use `grid_position_x`/`grid_position_y` to control where the original sits).',
|
|
1297
|
+
'"generate_audio" ā add AI-generated audio from `prompt`. Optionally split into `sound_effect_prompt` and `background_music_prompt`. Set `original_sound=true` to keep original audio alongside.',
|
|
1298
|
+
'"remove_watermark" ā AI-powered watermark removal.',
|
|
1299
|
+
'"face_swap" ā replace the face in the video with the face from `image_url` (required).',
|
|
1300
|
+
'"extend" ā lengthen the video at `mode` ("start" or "end") by `duration` seconds. Optional `prompt` and `context` guide the generated content.',
|
|
1301
|
+
'"magic_edit" ā restyle or transform the video with `prompt` (required).',
|
|
1302
|
+
'"lipsync" ā sync a face to audio. Provide `audio_url` (audio file) OR `text_prompt` (will synthesize speech).',
|
|
1303
|
+
'"remove_background" ā remove or greenscreen the video background. Use `refine_edges=true` for cleaner cutouts, `subject_is_person=true` for portrait-optimized mode.',
|
|
1304
|
+
'"inpaint" ā replace a region of the video. Provide `mask_video_url` (B&W mask, white=fill area), `prompt` for what to generate, and optionally `object_prompt` (what to replace) and `video_strength` (0ā1, how strongly to follow the original).',
|
|
1305
|
+
'"retake" ā regenerate a segment of the video. Use `start_time` (seconds) + `duration` + optional `prompt`.',
|
|
1306
|
+
].join(' ')),
|
|
1307
|
+
|
|
1308
|
+
model: z.string().optional()
|
|
1309
|
+
.describe('Model identifier override. Omit to use the platform default for the operation.'),
|
|
1310
|
+
|
|
1311
|
+
// āā upscale āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1312
|
+
scale: z.number().optional()
|
|
1313
|
+
.describe('Upscale factor (e.g. 2, 4). Used with operation="upscale".'),
|
|
1314
|
+
resolution: z.string().optional()
|
|
1315
|
+
.describe('Target resolution (e.g. "4k", "2k", "1080p"). Used with "upscale" and "reframe".'),
|
|
1316
|
+
target_fps: z.number().optional()
|
|
1317
|
+
.describe('Target frame rate (e.g. 24, 30, 60). Used with operation="upscale".'),
|
|
1318
|
+
|
|
1319
|
+
// āā reframe āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1320
|
+
aspect_ratio: z.string().optional()
|
|
1321
|
+
.describe('Target aspect ratio (e.g. "16:9", "9:16", "1:1"). Required for operation="reframe".'),
|
|
1322
|
+
grid_position_x: z.number().optional()
|
|
1323
|
+
.describe('Horizontal position (0.0ā1.0) of the original content within the reframed canvas. Used with "reframe". Default: 0.5 (center).'),
|
|
1324
|
+
grid_position_y: z.number().optional()
|
|
1325
|
+
.describe('Vertical position (0.0ā1.0) of the original content within the reframed canvas. Used with "reframe". Default: 0.5 (center).'),
|
|
1326
|
+
|
|
1327
|
+
// āā generate_audio āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1328
|
+
prompt: z.string().optional()
|
|
1329
|
+
.describe('Text prompt. Required for "magic_edit". Used with "generate_audio", "extend", "inpaint", "retake", and optionally "lipsync" (for text-to-speech instead of audio_url).'),
|
|
1330
|
+
sound_effect_prompt: z.string().optional()
|
|
1331
|
+
.describe('Separate prompt for sound effects layer. Used with operation="generate_audio" when you want to specify SFX and music independently.'),
|
|
1332
|
+
background_music_prompt: z.string().optional()
|
|
1333
|
+
.describe('Separate prompt for background music layer. Used with operation="generate_audio" alongside `sound_effect_prompt`.'),
|
|
1334
|
+
original_sound: z.boolean().optional()
|
|
1335
|
+
.describe('When true, keeps the original video audio and mixes in the generated audio. Used with "generate_audio". Default: false.'),
|
|
1336
|
+
cfg_strength: z.number().optional()
|
|
1337
|
+
.describe('Guidance strength for audio generation (higher = follows prompt more strictly). Used with "generate_audio".'),
|
|
1338
|
+
|
|
1339
|
+
// āā face_swap āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1340
|
+
image_url: z.string().optional()
|
|
1341
|
+
.describe('URL of the reference face image. Required for operation="face_swap".'),
|
|
1342
|
+
|
|
1343
|
+
// āā extend āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1344
|
+
duration: z.number().optional()
|
|
1345
|
+
.describe('Seconds of content to generate. Used with "extend" (required) and "retake" (optional). Typical range: 1ā20.'),
|
|
1346
|
+
mode: z.string().optional()
|
|
1347
|
+
.describe('Extension direction: "start" or "end". Used with "extend". For "retake": replacement mode (default: "replace_audio_and_video"). Default: "end".'),
|
|
1348
|
+
context: z.string().optional()
|
|
1349
|
+
.describe('Additional context to guide what gets generated in the extended segment. Used with "extend".'),
|
|
1350
|
+
|
|
1351
|
+
// āā lipsync āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1352
|
+
audio_url: z.string().optional()
|
|
1353
|
+
.describe('URL of the audio track to sync to the face. Required for "lipsync" unless `text_prompt` is provided.'),
|
|
1354
|
+
text_prompt: z.string().optional()
|
|
1355
|
+
.describe('Text to synthesize as speech and sync to the face. Used with "lipsync" as an alternative to `audio_url`.'),
|
|
1356
|
+
|
|
1357
|
+
// āā remove_background āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1358
|
+
refine_edges: z.boolean().optional()
|
|
1359
|
+
.describe('Apply edge refinement for cleaner background removal. Used with "remove_background". Default: false.'),
|
|
1360
|
+
subject_is_person: z.boolean().optional()
|
|
1361
|
+
.describe('Optimize background removal for a human subject (portrait mode). Used with "remove_background". Default: false.'),
|
|
1362
|
+
|
|
1363
|
+
// āā inpaint āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1364
|
+
mask_video_url: z.string().optional()
|
|
1365
|
+
.describe('URL of a mask video (B&W; white = area to fill). Used with operation="inpaint".'),
|
|
1366
|
+
object_prompt: z.string().optional()
|
|
1367
|
+
.describe('Description of the object being replaced (helps the model understand what to remove). Used with "inpaint".'),
|
|
1368
|
+
video_strength: z.number().optional()
|
|
1369
|
+
.describe('How closely to follow the original video (0.0ā1.0; higher = closer to original). Used with "inpaint".'),
|
|
1370
|
+
|
|
1371
|
+
// āā retake āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1372
|
+
start_time: z.number().optional()
|
|
1373
|
+
.describe('Start time in seconds of the segment to retake. Used with operation="retake".'),
|
|
1374
|
+
|
|
1224
1375
|
project_id: projectIdField
|
|
1225
1376
|
},
|
|
1226
|
-
async ({
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1377
|
+
async ({
|
|
1378
|
+
video_url, operation, model, aspect_ratio, scale, prompt,
|
|
1379
|
+
image_url, audio_url, duration, mode,
|
|
1380
|
+
target_fps, resolution,
|
|
1381
|
+
grid_position_x, grid_position_y,
|
|
1382
|
+
sound_effect_prompt, background_music_prompt, original_sound, cfg_strength,
|
|
1383
|
+
refine_edges, subject_is_person,
|
|
1384
|
+
text_prompt, context,
|
|
1385
|
+
mask_video_url, object_prompt, video_strength,
|
|
1386
|
+
start_time,
|
|
1387
|
+
project_id
|
|
1388
|
+
}) => {
|
|
1389
|
+
model = await canonicalModelId(client, model);
|
|
1390
|
+
|
|
1391
|
+
// Validation
|
|
1392
|
+
if (operation === 'magic_edit' && !prompt) throw new Error('prompt is required for magic_edit');
|
|
1393
|
+
if (operation === 'generate_audio'&& !prompt) throw new Error('prompt is required for generate_audio');
|
|
1394
|
+
if (operation === 'reframe' && !aspect_ratio)throw new Error('aspect_ratio is required for reframe');
|
|
1395
|
+
if (operation === 'face_swap' && !image_url) throw new Error('image_url (reference face) is required for face_swap');
|
|
1396
|
+
if (operation === 'lipsync' && !audio_url && !text_prompt) throw new Error('audio_url or text_prompt is required for lipsync');
|
|
1397
|
+
if (operation === 'extend' && !duration) throw new Error('duration is required for extend');
|
|
1234
1398
|
|
|
1235
1399
|
const gen = await client.post('/v1/edit/video', {
|
|
1236
1400
|
video_url, operation, model, aspect_ratio, scale, prompt,
|
|
1237
|
-
image_url, audio_url, duration, mode,
|
|
1401
|
+
image_url, audio_url, duration, mode,
|
|
1402
|
+
target_fps, resolution,
|
|
1403
|
+
grid_position_x, grid_position_y,
|
|
1404
|
+
sound_effect_prompt, background_music_prompt, original_sound, cfg_strength,
|
|
1405
|
+
refine_edges, subject_is_person,
|
|
1406
|
+
text_prompt, context,
|
|
1407
|
+
mask_video_url, object_prompt, video_strength,
|
|
1408
|
+
start_time,
|
|
1409
|
+
project_id
|
|
1238
1410
|
});
|
|
1239
1411
|
|
|
1240
1412
|
if (ui()) return uiGenerating({
|
|
1241
1413
|
tool: 'edit_video', kind: 'video', gen, client, model,
|
|
1242
1414
|
prompt: prompt || operation,
|
|
1243
|
-
settings: { mode: operation, duration, aspect_ratio },
|
|
1415
|
+
settings: { mode: operation, duration, aspect_ratio, resolution },
|
|
1244
1416
|
reference_image: image_url
|
|
1245
1417
|
});
|
|
1246
1418
|
|
package/src/tools/models.js
CHANGED
|
@@ -44,22 +44,16 @@ function modelChips(m) {
|
|
|
44
44
|
|
|
45
45
|
// structuredContent for ui://kolbo/catalog.html ā see src/apps/widgets/catalog.js
|
|
46
46
|
// Deliberately CURATED, not exhaustive: the widget is a picker, not a database.
|
|
47
|
-
//
|
|
48
|
-
//
|
|
47
|
+
// Each group shows the recommended/new models (max 6), and the total count
|
|
48
|
+
// chip tells the user how many exist overall. Smart Select / "Auto" rows are
|
|
49
|
+
// deliberately EXCLUDED ā we always want a specific model chosen (server
|
|
50
|
+
// instruction #9): auto-routing hides the model choice and the generation
|
|
51
|
+
// metadata used to read just "Auto".
|
|
49
52
|
function buildCatalogStructured(models, type) {
|
|
50
53
|
const groups = [];
|
|
51
54
|
const byName = new Map();
|
|
52
55
|
const isAuto = (m) => /^auto$|smart.select/i.test(String(m.name || '')) || /smart-select|k_auto/i.test(String(m.identifier || ''));
|
|
53
56
|
|
|
54
|
-
// Pinned Smart Select entry (replaces the confusing "Other / Auto" row).
|
|
55
|
-
const smartSelect = {
|
|
56
|
-
name: 'Smart Select',
|
|
57
|
-
icon: null,
|
|
58
|
-
description: 'Recommended ā automatically routes to the best model for your prompt, quality and cost.',
|
|
59
|
-
chips: ['AUTO'],
|
|
60
|
-
use_hint: 'Generate with Smart Select (omit the model) ā ask me what I want to create first.',
|
|
61
|
-
};
|
|
62
|
-
|
|
63
57
|
// Recommended + new models float to the top of each group.
|
|
64
58
|
const ranked = [...models].filter((m) => !isAuto(m)).sort((a, b) => {
|
|
65
59
|
const score = (m) => (m.recommended ? 2 : 0) + (m.new_model || m.newModel ? 1 : 0);
|
|
@@ -84,7 +78,7 @@ function buildCatalogStructured(models, type) {
|
|
|
84
78
|
widget: 'catalog',
|
|
85
79
|
title: 'Kolbo AI Models' + (type ? ' ā ' + type : ''),
|
|
86
80
|
total_available: models.length,
|
|
87
|
-
groups
|
|
81
|
+
groups,
|
|
88
82
|
};
|
|
89
83
|
}
|
|
90
84
|
|