Fix review issues: lyrics polling, validation, undici fetch, retry, timeout, throttle, payload helper
This commit is contained in:
Vendored
+10
-2
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Suno API HTTP client with rate limiting and polling helpers.
|
* Suno API HTTP client with rate limiting, proxy support, retries and polling helpers.
|
||||||
*
|
*
|
||||||
* Rate limit: 20 requests per 10 seconds (sunoapi.org).
|
* Rate limit: 20 requests per 10 seconds (sunoapi.org).
|
||||||
* Proxy support via SUNO_HTTP_PROXY / HTTPS_PROXY / HTTP_PROXY env vars.
|
* Proxy support via SUNO_HTTP_PROXY / HTTPS_PROXY / HTTP_PROXY env vars.
|
||||||
@@ -28,13 +28,19 @@ export interface SunoTrack {
|
|||||||
createTime?: string;
|
createTime?: string;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
}
|
}
|
||||||
|
export interface SunoLyricsVariant {
|
||||||
|
text?: string;
|
||||||
|
title?: string;
|
||||||
|
status?: string;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
export interface SunoTaskData {
|
export interface SunoTaskData {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
parentMusicId?: string;
|
parentMusicId?: string;
|
||||||
param?: string;
|
param?: string;
|
||||||
response?: {
|
response?: {
|
||||||
taskId?: string;
|
taskId?: string;
|
||||||
data?: SunoTrack[];
|
data?: SunoTrack[] | SunoLyricsVariant[];
|
||||||
sunoData?: SunoTrack[];
|
sunoData?: SunoTrack[];
|
||||||
};
|
};
|
||||||
status: string;
|
status: string;
|
||||||
@@ -49,6 +55,8 @@ export declare class SunoClient {
|
|||||||
private readonly maxRequests;
|
private readonly maxRequests;
|
||||||
private readonly windowMs;
|
private readonly windowMs;
|
||||||
private readonly proxyAgent?;
|
private readonly proxyAgent?;
|
||||||
|
private readonly requestTimeoutMs;
|
||||||
|
private readonly maxRetries;
|
||||||
private requestTimestamps;
|
private requestTimestamps;
|
||||||
constructor(apiKey: string);
|
constructor(apiKey: string);
|
||||||
/**
|
/**
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,CAAC,CAAC;CACT;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;QACnB,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;KACxB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,cAAc,GAAG,eAAe,CAAC;IACzE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAYD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,iBAAiB,CAAgB;gBAE7B,MAAM,EAAE,MAAM;IAQ1B;;OAEG;YACW,QAAQ;IAkBhB,OAAO,CAAC,CAAC,GAAG,OAAO,EACvB,MAAM,EAAE,KAAK,GAAG,MAAM,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAmC9B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAI3D,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAI3F;;OAEG;IACG,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;KAChB,GACL,OAAO,CAAC,YAAY,CAAC;CA2BzB"}
|
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
||||||
Vendored
+49
-11
@@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
/**
|
/**
|
||||||
* Suno API HTTP client with rate limiting and polling helpers.
|
* Suno API HTTP client with rate limiting, proxy support, retries and polling helpers.
|
||||||
*
|
*
|
||||||
* Rate limit: 20 requests per 10 seconds (sunoapi.org).
|
* Rate limit: 20 requests per 10 seconds (sunoapi.org).
|
||||||
* Proxy support via SUNO_HTTP_PROXY / HTTPS_PROXY / HTTP_PROXY env vars.
|
* Proxy support via SUNO_HTTP_PROXY / HTTPS_PROXY / HTTP_PROXY env vars.
|
||||||
@@ -15,12 +15,24 @@ function getProxyUrl() {
|
|||||||
process.env.HTTP_PROXY ||
|
process.env.HTTP_PROXY ||
|
||||||
process.env.http_proxy);
|
process.env.http_proxy);
|
||||||
}
|
}
|
||||||
|
function isRetryableError(error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
const msg = error.message.toLowerCase();
|
||||||
|
return msg.includes('timeout') || msg.includes('econnreset') || msg.includes('socket') || msg.includes('fetch failed');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
class SunoClient {
|
class SunoClient {
|
||||||
baseUrl = 'https://api.sunoapi.org/api/v1';
|
baseUrl = 'https://api.sunoapi.org/api/v1';
|
||||||
apiKey;
|
apiKey;
|
||||||
maxRequests = 20;
|
maxRequests = 20;
|
||||||
windowMs = 10_000;
|
windowMs = 10_000;
|
||||||
proxyAgent;
|
proxyAgent;
|
||||||
|
requestTimeoutMs = 30_000;
|
||||||
|
maxRetries = 2;
|
||||||
requestTimestamps = [];
|
requestTimestamps = [];
|
||||||
constructor(apiKey) {
|
constructor(apiKey) {
|
||||||
this.apiKey = apiKey;
|
this.apiKey = apiKey;
|
||||||
@@ -33,17 +45,17 @@ class SunoClient {
|
|||||||
* Enforce rate limit: max 20 requests per 10 seconds.
|
* Enforce rate limit: max 20 requests per 10 seconds.
|
||||||
*/
|
*/
|
||||||
async throttle() {
|
async throttle() {
|
||||||
|
while (true) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
this.requestTimestamps = this.requestTimestamps.filter((ts) => now - ts < this.windowMs);
|
this.requestTimestamps = this.requestTimestamps.filter((ts) => now - ts < this.windowMs);
|
||||||
if (this.requestTimestamps.length >= this.maxRequests) {
|
if (this.requestTimestamps.length < this.maxRequests) {
|
||||||
const oldest = this.requestTimestamps[0];
|
|
||||||
const wait = this.windowMs - (now - oldest);
|
|
||||||
if (wait > 0) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
||||||
return this.throttle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.requestTimestamps.push(now);
|
this.requestTimestamps.push(now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const oldest = this.requestTimestamps[0];
|
||||||
|
const wait = Math.max(0, this.windowMs - (now - oldest));
|
||||||
|
await sleep(wait);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async request(method, path, body) {
|
async request(method, path, body) {
|
||||||
await this.throttle();
|
await this.throttle();
|
||||||
@@ -52,15 +64,19 @@ class SunoClient {
|
|||||||
Authorization: `Bearer ${this.apiKey}`,
|
Authorization: `Bearer ${this.apiKey}`,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
|
let lastError;
|
||||||
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
const fetchOptions = {
|
const fetchOptions = {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
body: body ? JSON.stringify(body) : undefined,
|
body: body ? JSON.stringify(body) : undefined,
|
||||||
|
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
||||||
};
|
};
|
||||||
if (this.proxyAgent) {
|
if (this.proxyAgent) {
|
||||||
fetchOptions.dispatcher = this.proxyAgent;
|
fetchOptions.dispatcher = this.proxyAgent;
|
||||||
}
|
}
|
||||||
const response = await fetch(url, fetchOptions);
|
const response = await (0, undici_1.fetch)(url, fetchOptions);
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
let json;
|
let json;
|
||||||
try {
|
try {
|
||||||
@@ -69,11 +85,32 @@ class SunoClient {
|
|||||||
catch {
|
catch {
|
||||||
throw new Error(`Invalid JSON response from Suno API: ${text}`);
|
throw new Error(`Invalid JSON response from Suno API: ${text}`);
|
||||||
}
|
}
|
||||||
|
// Retry on transient server errors / rate-limit before throwing.
|
||||||
|
if (json.code === 429 || json.code === 503 || json.code === 504) {
|
||||||
|
if (attempt < this.maxRetries) {
|
||||||
|
const backoff = Math.min(1000 * 2 ** attempt, 8000);
|
||||||
|
await sleep(backoff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (json.code !== 200) {
|
if (json.code !== 200) {
|
||||||
throw new Error(`Suno API error ${json.code}: ${json.msg || 'unknown error'}`);
|
throw new Error(`Suno API error ${json.code}: ${json.msg || 'unknown error'}`);
|
||||||
}
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
catch (error) {
|
||||||
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
|
const retryable = isRetryableError(lastError);
|
||||||
|
if (retryable && attempt < this.maxRetries) {
|
||||||
|
const backoff = Math.min(1000 * 2 ** attempt, 8000);
|
||||||
|
await sleep(backoff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw lastError ?? new Error('Suno API request failed');
|
||||||
|
}
|
||||||
get(path) {
|
get(path) {
|
||||||
return this.request('GET', path);
|
return this.request('GET', path);
|
||||||
}
|
}
|
||||||
@@ -94,12 +131,13 @@ class SunoClient {
|
|||||||
status === 'FAILED' ||
|
status === 'FAILED' ||
|
||||||
status === 'CREATE_TASK_FAILED' ||
|
status === 'CREATE_TASK_FAILED' ||
|
||||||
status === 'GENERATE_AUDIO_FAILED' ||
|
status === 'GENERATE_AUDIO_FAILED' ||
|
||||||
|
status === 'GENERATE_LYRICS_FAILED' ||
|
||||||
status === 'CALLBACK_EXCEPTION' ||
|
status === 'CALLBACK_EXCEPTION' ||
|
||||||
status === 'SENSITIVE_WORD_ERROR') {
|
status === 'SENSITIVE_WORD_ERROR') {
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
if (attempt < maxAttempts) {
|
if (attempt < maxAttempts) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
await sleep(intervalMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error(`Polling timeout for task ${taskId} after ${maxAttempts} attempts`);
|
throw new Error(`Polling timeout for task ${taskId} after ${maxAttempts} attempts`);
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgB,MAAM,UAAU,CAAC;AA2EpD,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,UAAU;IAEzC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;CAsUlG"}
|
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA8C,MAAM,UAAU,CAAC;AAuIlF,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,UAAU;IAEzC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;CA4TlG"}
|
||||||
Vendored
+97
-127
@@ -37,6 +37,8 @@ exports.ToolHandlers = void 0;
|
|||||||
const schemas = __importStar(require("./schemas"));
|
const schemas = __importStar(require("./schemas"));
|
||||||
const ASYNC_POLLING_PATHS = {
|
const ASYNC_POLLING_PATHS = {
|
||||||
'suno_generate_music': '/generate/record-info?taskId=',
|
'suno_generate_music': '/generate/record-info?taskId=',
|
||||||
|
'suno_generate_lyrics': '/lyrics/record-info?taskId=',
|
||||||
|
'suno_generate_sounds': '/generate/record-info?taskId=',
|
||||||
'suno_extend_music': '/generate/record-info?taskId=',
|
'suno_extend_music': '/generate/record-info?taskId=',
|
||||||
'suno_replace_section': '/generate/record-info?taskId=',
|
'suno_replace_section': '/generate/record-info?taskId=',
|
||||||
'suno_upload_and_cover': '/generate/record-info?taskId=',
|
'suno_upload_and_cover': '/generate/record-info?taskId=',
|
||||||
@@ -49,9 +51,46 @@ const ASYNC_POLLING_PATHS = {
|
|||||||
'suno_create_video': '/generate/video-info?taskId=',
|
'suno_create_video': '/generate/video-info?taskId=',
|
||||||
'suno_create_cover': '/generate/cover-info?taskId=',
|
'suno_create_cover': '/generate/cover-info?taskId=',
|
||||||
'suno_convert_to_wav': '/generate/wav-info?taskId=',
|
'suno_convert_to_wav': '/generate/wav-info?taskId=',
|
||||||
'suno_generate_sounds': '/generate/record-info?taskId=',
|
|
||||||
};
|
};
|
||||||
|
function copyDefined(source, target, keys) {
|
||||||
|
for (const key of keys) {
|
||||||
|
const value = source[key];
|
||||||
|
if (value !== undefined) {
|
||||||
|
target[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isLyricsData(data) {
|
||||||
|
return data.type === 'LYRICS';
|
||||||
|
}
|
||||||
|
function formatLyricsResult(data) {
|
||||||
|
const status = data.status;
|
||||||
|
if (status !== 'SUCCESS') {
|
||||||
|
return JSON.stringify({
|
||||||
|
taskId: data.taskId,
|
||||||
|
status,
|
||||||
|
errorCode: data.errorCode,
|
||||||
|
errorMessage: data.errorMessage,
|
||||||
|
}, null, 2);
|
||||||
|
}
|
||||||
|
const variants = data.response?.data ?? [];
|
||||||
|
return JSON.stringify({
|
||||||
|
taskId: data.taskId,
|
||||||
|
status,
|
||||||
|
type: data.type,
|
||||||
|
variants: variants.map((v, index) => ({
|
||||||
|
index: index + 1,
|
||||||
|
title: v.title,
|
||||||
|
text: v.text,
|
||||||
|
status: v.status,
|
||||||
|
errorMessage: v.errorMessage,
|
||||||
|
})),
|
||||||
|
}, null, 2);
|
||||||
|
}
|
||||||
function formatTaskResult(data) {
|
function formatTaskResult(data) {
|
||||||
|
if (isLyricsData(data)) {
|
||||||
|
return formatLyricsResult(data);
|
||||||
|
}
|
||||||
const status = data.status;
|
const status = data.status;
|
||||||
if (status !== 'SUCCESS') {
|
if (status !== 'SUCCESS') {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
@@ -62,7 +101,7 @@ function formatTaskResult(data) {
|
|||||||
}, null, 2);
|
}, null, 2);
|
||||||
}
|
}
|
||||||
const response = data.response;
|
const response = data.response;
|
||||||
const tracks = response?.data ?? response?.sunoData ?? [];
|
const tracks = (response?.data ?? response?.sunoData ?? []);
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
taskId: data.taskId,
|
taskId: data.taskId,
|
||||||
status,
|
status,
|
||||||
@@ -102,40 +141,32 @@ class ToolHandlers {
|
|||||||
case 'suno_generate_music': {
|
case 'suno_generate_music': {
|
||||||
const input = schemas.GenerateMusicSchema.parse(args);
|
const input = schemas.GenerateMusicSchema.parse(args);
|
||||||
const payload = {
|
const payload = {
|
||||||
prompt: input.prompt,
|
|
||||||
customMode: input.customMode,
|
customMode: input.customMode,
|
||||||
instrumental: input.instrumental,
|
instrumental: input.instrumental,
|
||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.style !== undefined)
|
copyDefined(input, payload, [
|
||||||
payload.style = input.style;
|
'prompt',
|
||||||
if (input.title !== undefined)
|
'style',
|
||||||
payload.title = input.title;
|
'title',
|
||||||
if (input.negativeTags !== undefined)
|
'negativeTags',
|
||||||
payload.negativeTags = input.negativeTags;
|
'vocalGender',
|
||||||
if (input.vocalGender !== undefined)
|
'styleWeight',
|
||||||
payload.vocalGender = input.vocalGender;
|
'weirdnessConstraint',
|
||||||
if (input.styleWeight !== undefined)
|
'audioWeight',
|
||||||
payload.styleWeight = input.styleWeight;
|
'personaId',
|
||||||
if (input.weirdnessConstraint !== undefined)
|
'personaModel',
|
||||||
payload.weirdnessConstraint = input.weirdnessConstraint;
|
]);
|
||||||
if (input.audioWeight !== undefined)
|
|
||||||
payload.audioWeight = input.audioWeight;
|
|
||||||
if (input.personaId !== undefined)
|
|
||||||
payload.personaId = input.personaId;
|
|
||||||
if (input.personaModel !== undefined)
|
|
||||||
payload.personaModel = input.personaModel;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'suno_generate_lyrics': {
|
case 'suno_generate_lyrics': {
|
||||||
const input = schemas.GenerateLyricsSchema.parse(args);
|
const input = schemas.GenerateLyricsSchema.parse(args);
|
||||||
const result = await this.client.post('/lyrics', {
|
resultText = await runAsyncTask(this.client, name, '/lyrics', {
|
||||||
prompt: input.prompt,
|
prompt: input.prompt,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
});
|
});
|
||||||
resultText = JSON.stringify({ taskId: result.data.taskId, status: 'PENDING' }, null, 2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'suno_generate_sounds': {
|
case 'suno_generate_sounds': {
|
||||||
@@ -145,14 +176,7 @@ class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.soundLoop !== undefined)
|
copyDefined(input, payload, ['soundLoop', 'soundTempo', 'soundKey', 'grabLyrics']);
|
||||||
payload.soundLoop = input.soundLoop;
|
|
||||||
if (input.soundTempo !== undefined)
|
|
||||||
payload.soundTempo = input.soundTempo;
|
|
||||||
if (input.soundKey !== undefined)
|
|
||||||
payload.soundKey = input.soundKey;
|
|
||||||
if (input.grabLyrics !== undefined)
|
|
||||||
payload.grabLyrics = input.grabLyrics;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/sounds', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/sounds', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -164,28 +188,19 @@ class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.prompt !== undefined)
|
copyDefined(input, payload, [
|
||||||
payload.prompt = input.prompt;
|
'prompt',
|
||||||
if (input.style !== undefined)
|
'style',
|
||||||
payload.style = input.style;
|
'title',
|
||||||
if (input.title !== undefined)
|
'continueAt',
|
||||||
payload.title = input.title;
|
'negativeTags',
|
||||||
if (input.continueAt !== undefined)
|
'vocalGender',
|
||||||
payload.continueAt = input.continueAt;
|
'styleWeight',
|
||||||
if (input.negativeTags !== undefined)
|
'weirdnessConstraint',
|
||||||
payload.negativeTags = input.negativeTags;
|
'audioWeight',
|
||||||
if (input.vocalGender !== undefined)
|
'personaId',
|
||||||
payload.vocalGender = input.vocalGender;
|
'personaModel',
|
||||||
if (input.styleWeight !== undefined)
|
]);
|
||||||
payload.styleWeight = input.styleWeight;
|
|
||||||
if (input.weirdnessConstraint !== undefined)
|
|
||||||
payload.weirdnessConstraint = input.weirdnessConstraint;
|
|
||||||
if (input.audioWeight !== undefined)
|
|
||||||
payload.audioWeight = input.audioWeight;
|
|
||||||
if (input.personaId !== undefined)
|
|
||||||
payload.personaId = input.personaId;
|
|
||||||
if (input.personaModel !== undefined)
|
|
||||||
payload.personaModel = input.personaModel;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/extend', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/extend', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -199,16 +214,7 @@ class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.replaceAudioUrl !== undefined)
|
copyDefined(input, payload, ['replaceAudioUrl', 'customMode', 'style', 'title', 'instrumental']);
|
||||||
payload.replaceAudioUrl = input.replaceAudioUrl;
|
|
||||||
if (input.customMode !== undefined)
|
|
||||||
payload.customMode = input.customMode;
|
|
||||||
if (input.style !== undefined)
|
|
||||||
payload.style = input.style;
|
|
||||||
if (input.title !== undefined)
|
|
||||||
payload.title = input.title;
|
|
||||||
if (input.instrumental !== undefined)
|
|
||||||
payload.instrumental = input.instrumental;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/replace-section', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/replace-section', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -216,16 +222,10 @@ class ToolHandlers {
|
|||||||
const input = schemas.AddVocalsSchema.parse(args);
|
const input = schemas.AddVocalsSchema.parse(args);
|
||||||
const payload = {
|
const payload = {
|
||||||
audioId: input.audioId,
|
audioId: input.audioId,
|
||||||
prompt: input.prompt,
|
|
||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.customMode !== undefined)
|
copyDefined(input, payload, ['prompt', 'customMode', 'style', 'title']);
|
||||||
payload.customMode = input.customMode;
|
|
||||||
if (input.style !== undefined)
|
|
||||||
payload.style = input.style;
|
|
||||||
if (input.title !== undefined)
|
|
||||||
payload.title = input.title;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/add-vocals', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/add-vocals', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -233,16 +233,10 @@ class ToolHandlers {
|
|||||||
const input = schemas.AddInstrumentalSchema.parse(args);
|
const input = schemas.AddInstrumentalSchema.parse(args);
|
||||||
const payload = {
|
const payload = {
|
||||||
audioId: input.audioId,
|
audioId: input.audioId,
|
||||||
prompt: input.prompt,
|
|
||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.customMode !== undefined)
|
copyDefined(input, payload, ['prompt', 'customMode', 'style', 'title']);
|
||||||
payload.customMode = input.customMode;
|
|
||||||
if (input.style !== undefined)
|
|
||||||
payload.style = input.style;
|
|
||||||
if (input.title !== undefined)
|
|
||||||
payload.title = input.title;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/add-instrumental', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/add-instrumental', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -255,26 +249,18 @@ class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.prompt !== undefined)
|
copyDefined(input, payload, [
|
||||||
payload.prompt = input.prompt;
|
'prompt',
|
||||||
if (input.style !== undefined)
|
'style',
|
||||||
payload.style = input.style;
|
'title',
|
||||||
if (input.title !== undefined)
|
'negativeTags',
|
||||||
payload.title = input.title;
|
'vocalGender',
|
||||||
if (input.negativeTags !== undefined)
|
'styleWeight',
|
||||||
payload.negativeTags = input.negativeTags;
|
'weirdnessConstraint',
|
||||||
if (input.vocalGender !== undefined)
|
'audioWeight',
|
||||||
payload.vocalGender = input.vocalGender;
|
'personaId',
|
||||||
if (input.styleWeight !== undefined)
|
'personaModel',
|
||||||
payload.styleWeight = input.styleWeight;
|
]);
|
||||||
if (input.weirdnessConstraint !== undefined)
|
|
||||||
payload.weirdnessConstraint = input.weirdnessConstraint;
|
|
||||||
if (input.audioWeight !== undefined)
|
|
||||||
payload.audioWeight = input.audioWeight;
|
|
||||||
if (input.personaId !== undefined)
|
|
||||||
payload.personaId = input.personaId;
|
|
||||||
if (input.personaModel !== undefined)
|
|
||||||
payload.personaModel = input.personaModel;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/upload-cover', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/upload-cover', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -286,14 +272,7 @@ class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.prompt !== undefined)
|
copyDefined(input, payload, ['prompt', 'style', 'title', 'continueAt']);
|
||||||
payload.prompt = input.prompt;
|
|
||||||
if (input.style !== undefined)
|
|
||||||
payload.style = input.style;
|
|
||||||
if (input.title !== undefined)
|
|
||||||
payload.title = input.title;
|
|
||||||
if (input.continueAt !== undefined)
|
|
||||||
payload.continueAt = input.continueAt;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/upload-extend', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/upload-extend', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -306,22 +285,16 @@ class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.instrumental !== undefined)
|
copyDefined(input, payload, [
|
||||||
payload.instrumental = input.instrumental;
|
'instrumental',
|
||||||
if (input.style !== undefined)
|
'style',
|
||||||
payload.style = input.style;
|
'title',
|
||||||
if (input.title !== undefined)
|
'negativeTags',
|
||||||
payload.title = input.title;
|
'vocalGender',
|
||||||
if (input.negativeTags !== undefined)
|
'styleWeight',
|
||||||
payload.negativeTags = input.negativeTags;
|
'weirdnessConstraint',
|
||||||
if (input.vocalGender !== undefined)
|
'audioWeight',
|
||||||
payload.vocalGender = input.vocalGender;
|
]);
|
||||||
if (input.styleWeight !== undefined)
|
|
||||||
payload.styleWeight = input.styleWeight;
|
|
||||||
if (input.weirdnessConstraint !== undefined)
|
|
||||||
payload.weirdnessConstraint = input.weirdnessConstraint;
|
|
||||||
if (input.audioWeight !== undefined)
|
|
||||||
payload.audioWeight = input.audioWeight;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/mashup', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/mashup', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -351,10 +324,7 @@ class ToolHandlers {
|
|||||||
audioId: input.audioId,
|
audioId: input.audioId,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.author !== undefined)
|
copyDefined(input, payload, ['author', 'domainName']);
|
||||||
payload.author = input.author;
|
|
||||||
if (input.domainName !== undefined)
|
|
||||||
payload.domainName = input.domainName;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/video', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/video', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -407,8 +377,8 @@ class ToolHandlers {
|
|||||||
}
|
}
|
||||||
case 'suno_get_lyrics_details': {
|
case 'suno_get_lyrics_details': {
|
||||||
const input = schemas.GetLyricsDetailsSchema.parse(args);
|
const input = schemas.GetLyricsDetailsSchema.parse(args);
|
||||||
const result = await this.client.get(`/generate/lyrics-info?taskId=${encodeURIComponent(input.taskId)}`);
|
const result = await this.client.get(`/lyrics/record-info?taskId=${encodeURIComponent(input.taskId)}`);
|
||||||
resultText = JSON.stringify(result.data, null, 2);
|
resultText = formatLyricsResult(result.data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'suno_get_wav_details': {
|
case 'suno_get_wav_details': {
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+128
-4
@@ -1,5 +1,5 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
export declare const GenerateMusicSchema: z.ZodObject<{
|
export declare const GenerateMusicSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
||||||
model: z.ZodEnum<[string, ...string[]]>;
|
model: z.ZodEnum<[string, ...string[]]>;
|
||||||
negativeTags: z.ZodOptional<z.ZodString>;
|
negativeTags: z.ZodOptional<z.ZodString>;
|
||||||
vocalGender: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
vocalGender: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
|
||||||
@@ -44,6 +44,66 @@ export declare const GenerateMusicSchema: z.ZodObject<{
|
|||||||
personaId?: string | undefined;
|
personaId?: string | undefined;
|
||||||
personaModel?: "style_persona" | "voice_persona" | undefined;
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
callBackUrl?: string | undefined;
|
callBackUrl?: string | undefined;
|
||||||
|
}>, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
|
}, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
|
}>, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
|
}, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
}>;
|
}>;
|
||||||
export declare const GenerateLyricsSchema: z.ZodObject<{
|
export declare const GenerateLyricsSchema: z.ZodObject<{
|
||||||
prompt: z.ZodString;
|
prompt: z.ZodString;
|
||||||
@@ -142,11 +202,11 @@ export declare const ReplaceSectionSchema: z.ZodObject<{
|
|||||||
model: z.ZodEnum<[string, ...string[]]>;
|
model: z.ZodEnum<[string, ...string[]]>;
|
||||||
callBackUrl: z.ZodOptional<z.ZodString>;
|
callBackUrl: z.ZodOptional<z.ZodString>;
|
||||||
}, "strict", z.ZodTypeAny, {
|
}, "strict", z.ZodTypeAny, {
|
||||||
|
end: number;
|
||||||
prompt: string;
|
prompt: string;
|
||||||
model: string;
|
model: string;
|
||||||
audioId: string;
|
audioId: string;
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
|
||||||
customMode?: boolean | undefined;
|
customMode?: boolean | undefined;
|
||||||
instrumental?: boolean | undefined;
|
instrumental?: boolean | undefined;
|
||||||
style?: string | undefined;
|
style?: string | undefined;
|
||||||
@@ -154,11 +214,11 @@ export declare const ReplaceSectionSchema: z.ZodObject<{
|
|||||||
callBackUrl?: string | undefined;
|
callBackUrl?: string | undefined;
|
||||||
replaceAudioUrl?: string | undefined;
|
replaceAudioUrl?: string | undefined;
|
||||||
}, {
|
}, {
|
||||||
|
end: number;
|
||||||
prompt: string;
|
prompt: string;
|
||||||
model: string;
|
model: string;
|
||||||
audioId: string;
|
audioId: string;
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
|
||||||
customMode?: boolean | undefined;
|
customMode?: boolean | undefined;
|
||||||
instrumental?: boolean | undefined;
|
instrumental?: boolean | undefined;
|
||||||
style?: string | undefined;
|
style?: string | undefined;
|
||||||
@@ -216,7 +276,7 @@ export declare const AddInstrumentalSchema: z.ZodObject<{
|
|||||||
title?: string | undefined;
|
title?: string | undefined;
|
||||||
callBackUrl?: string | undefined;
|
callBackUrl?: string | undefined;
|
||||||
}>;
|
}>;
|
||||||
export declare const UploadAndCoverSchema: z.ZodObject<{
|
export declare const UploadAndCoverSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
||||||
uploadUrl: z.ZodString;
|
uploadUrl: z.ZodString;
|
||||||
prompt: z.ZodOptional<z.ZodString>;
|
prompt: z.ZodOptional<z.ZodString>;
|
||||||
customMode: z.ZodBoolean;
|
customMode: z.ZodBoolean;
|
||||||
@@ -264,6 +324,70 @@ export declare const UploadAndCoverSchema: z.ZodObject<{
|
|||||||
personaId?: string | undefined;
|
personaId?: string | undefined;
|
||||||
personaModel?: "style_persona" | "voice_persona" | undefined;
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
callBackUrl?: string | undefined;
|
callBackUrl?: string | undefined;
|
||||||
|
}>, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
|
}, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
|
}>, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
|
}, {
|
||||||
|
customMode: boolean;
|
||||||
|
instrumental: boolean;
|
||||||
|
model: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
prompt?: string | undefined;
|
||||||
|
style?: string | undefined;
|
||||||
|
title?: string | undefined;
|
||||||
|
negativeTags?: string | undefined;
|
||||||
|
vocalGender?: "m" | "f" | undefined;
|
||||||
|
styleWeight?: number | undefined;
|
||||||
|
weirdnessConstraint?: number | undefined;
|
||||||
|
audioWeight?: number | undefined;
|
||||||
|
personaId?: string | undefined;
|
||||||
|
personaModel?: "style_persona" | "voice_persona" | undefined;
|
||||||
|
callBackUrl?: string | undefined;
|
||||||
}>;
|
}>;
|
||||||
export declare const UploadAndExtendSchema: z.ZodObject<{
|
export declare const UploadAndExtendSchema: z.ZodObject<{
|
||||||
uploadUrl: z.ZodString;
|
uploadUrl: z.ZodString;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
|
||||||
Vendored
+40
-6
@@ -20,17 +20,43 @@ const CommonGenerateParams = {
|
|||||||
callBackUrl: CallbackUrl,
|
callBackUrl: CallbackUrl,
|
||||||
};
|
};
|
||||||
// 1. Generate Music (12 credits)
|
// 1. Generate Music (12 credits)
|
||||||
exports.GenerateMusicSchema = zod_1.z.object({
|
exports.GenerateMusicSchema = zod_1.z
|
||||||
prompt: zod_1.z.string().optional().describe('Music description / lyrics (max 500 chars non-custom, 3000-5000 custom mode; required when customMode=false or customMode=true with instrumental=false)'),
|
.object({
|
||||||
|
prompt: zod_1.z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe('Music description / lyrics (max 500 chars non-custom, 3000-5000 custom mode; required when customMode=false or customMode=true with instrumental=false)'),
|
||||||
customMode: zod_1.z.boolean().describe('Enable custom mode (requires style and title)'),
|
customMode: zod_1.z.boolean().describe('Enable custom mode (requires style and title)'),
|
||||||
instrumental: zod_1.z.boolean().describe('Generate instrumental without vocals'),
|
instrumental: zod_1.z.boolean().describe('Generate instrumental without vocals'),
|
||||||
style: zod_1.z.string().optional().describe('Music style/genre (required when customMode=true)'),
|
style: zod_1.z.string().optional().describe('Music style/genre (required when customMode=true)'),
|
||||||
title: zod_1.z.string().optional().describe('Song title (required when customMode=true)'),
|
title: zod_1.z.string().optional().describe('Song title (required when customMode=true)'),
|
||||||
...CommonGenerateParams,
|
...CommonGenerateParams,
|
||||||
}).strict();
|
})
|
||||||
|
.strict()
|
||||||
|
.superRefine(requirePromptWhenNeeded)
|
||||||
|
.refine((data) => !data.customMode || (!!data.style && !!data.title), {
|
||||||
|
message: 'style and title are required when customMode is true',
|
||||||
|
path: ['style'],
|
||||||
|
});
|
||||||
|
function requirePromptWhenNeeded(data, ctx) {
|
||||||
|
if (!data.customMode && !data.prompt) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: zod_1.z.ZodIssueCode.custom,
|
||||||
|
message: 'prompt is required when customMode is false',
|
||||||
|
path: ['prompt'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.customMode && !data.instrumental && !data.prompt) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: zod_1.z.ZodIssueCode.custom,
|
||||||
|
message: 'prompt is required when customMode is true and instrumental is false',
|
||||||
|
path: ['prompt'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
// 2. Generate Lyrics (0.4 credits)
|
// 2. Generate Lyrics (0.4 credits)
|
||||||
exports.GenerateLyricsSchema = zod_1.z.object({
|
exports.GenerateLyricsSchema = zod_1.z.object({
|
||||||
prompt: zod_1.z.string().describe('Topic or theme for lyrics'),
|
prompt: zod_1.z.string().max(200).describe('Topic or theme for lyrics (max 200 chars)'),
|
||||||
callBackUrl: CallbackUrl,
|
callBackUrl: CallbackUrl,
|
||||||
}).strict();
|
}).strict();
|
||||||
// 3. Generate Sounds (2.5 credits)
|
// 3. Generate Sounds (2.5 credits)
|
||||||
@@ -99,7 +125,8 @@ exports.AddInstrumentalSchema = zod_1.z.object({
|
|||||||
callBackUrl: CallbackUrl,
|
callBackUrl: CallbackUrl,
|
||||||
}).strict();
|
}).strict();
|
||||||
// 8. Upload and Cover (12 credits)
|
// 8. Upload and Cover (12 credits)
|
||||||
exports.UploadAndCoverSchema = zod_1.z.object({
|
exports.UploadAndCoverSchema = zod_1.z
|
||||||
|
.object({
|
||||||
uploadUrl: zod_1.z.string().url().describe('Public URL of audio file to cover (max 8 minutes)'),
|
uploadUrl: zod_1.z.string().url().describe('Public URL of audio file to cover (max 8 minutes)'),
|
||||||
prompt: zod_1.z.string().optional(),
|
prompt: zod_1.z.string().optional(),
|
||||||
customMode: zod_1.z.boolean(),
|
customMode: zod_1.z.boolean(),
|
||||||
@@ -115,7 +142,13 @@ exports.UploadAndCoverSchema = zod_1.z.object({
|
|||||||
personaId: zod_1.z.string().optional(),
|
personaId: zod_1.z.string().optional(),
|
||||||
personaModel: PersonaModelEnum,
|
personaModel: PersonaModelEnum,
|
||||||
callBackUrl: CallbackUrl,
|
callBackUrl: CallbackUrl,
|
||||||
}).strict();
|
})
|
||||||
|
.strict()
|
||||||
|
.superRefine(requirePromptWhenNeeded)
|
||||||
|
.refine((data) => !data.customMode || (!!data.style && !!data.title), {
|
||||||
|
message: 'style and title are required when customMode is true',
|
||||||
|
path: ['style'],
|
||||||
|
});
|
||||||
// 9. Upload and Extend (12 credits)
|
// 9. Upload and Extend (12 credits)
|
||||||
exports.UploadAndExtendSchema = zod_1.z.object({
|
exports.UploadAndExtendSchema = zod_1.z.object({
|
||||||
uploadUrl: zod_1.z.string().url().describe('Public URL of audio file to extend'),
|
uploadUrl: zod_1.z.string().url().describe('Public URL of audio file to extend'),
|
||||||
@@ -169,6 +202,7 @@ exports.GeneratePersonaSchema = zod_1.z.object({
|
|||||||
audioId: zod_1.z.string().describe('Track ID to derive persona from'),
|
audioId: zod_1.z.string().describe('Track ID to derive persona from'),
|
||||||
}).strict();
|
}).strict();
|
||||||
// 17. Generate Mashup (12 credits)
|
// 17. Generate Mashup (12 credits)
|
||||||
|
// Note: Suno API docs list only V4/V4_5/V4_5PLUS/V4_5ALL/V5 for mashup; V5_5 is not supported.
|
||||||
exports.GenerateMashupSchema = zod_1.z.object({
|
exports.GenerateMashupSchema = zod_1.z.object({
|
||||||
uploadUrlList: zod_1.z.array(zod_1.z.string().url()).length(2).describe('Exactly 2 audio URLs to mash up'),
|
uploadUrlList: zod_1.z.array(zod_1.z.string().url()).length(2).describe('Exactly 2 audio URLs to mash up'),
|
||||||
customMode: zod_1.z.boolean(),
|
customMode: zod_1.z.boolean(),
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+64
-18
@@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Suno API HTTP client with rate limiting and polling helpers.
|
* Suno API HTTP client with rate limiting, proxy support, retries and polling helpers.
|
||||||
*
|
*
|
||||||
* Rate limit: 20 requests per 10 seconds (sunoapi.org).
|
* Rate limit: 20 requests per 10 seconds (sunoapi.org).
|
||||||
* Proxy support via SUNO_HTTP_PROXY / HTTPS_PROXY / HTTP_PROXY env vars.
|
* Proxy support via SUNO_HTTP_PROXY / HTTPS_PROXY / HTTP_PROXY env vars.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ProxyAgent } from 'undici';
|
import { fetch, ProxyAgent } from 'undici';
|
||||||
|
|
||||||
export interface SunoApiResponse<T = unknown> {
|
export interface SunoApiResponse<T = unknown> {
|
||||||
code: number;
|
code: number;
|
||||||
@@ -33,13 +33,20 @@ export interface SunoTrack {
|
|||||||
duration?: number;
|
duration?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SunoLyricsVariant {
|
||||||
|
text?: string;
|
||||||
|
title?: string;
|
||||||
|
status?: string;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SunoTaskData {
|
export interface SunoTaskData {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
parentMusicId?: string;
|
parentMusicId?: string;
|
||||||
param?: string;
|
param?: string;
|
||||||
response?: {
|
response?: {
|
||||||
taskId?: string;
|
taskId?: string;
|
||||||
data?: SunoTrack[];
|
data?: SunoTrack[] | SunoLyricsVariant[];
|
||||||
sunoData?: SunoTrack[];
|
sunoData?: SunoTrack[];
|
||||||
};
|
};
|
||||||
status: string;
|
status: string;
|
||||||
@@ -59,12 +66,26 @@ function getProxyUrl(): string | undefined {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRetryableError(error: unknown): boolean {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
const msg = error.message.toLowerCase();
|
||||||
|
return msg.includes('timeout') || msg.includes('econnreset') || msg.includes('socket') || msg.includes('fetch failed');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
export class SunoClient {
|
export class SunoClient {
|
||||||
private readonly baseUrl = 'https://api.sunoapi.org/api/v1';
|
private readonly baseUrl = 'https://api.sunoapi.org/api/v1';
|
||||||
private readonly apiKey: string;
|
private readonly apiKey: string;
|
||||||
private readonly maxRequests = 20;
|
private readonly maxRequests = 20;
|
||||||
private readonly windowMs = 10_000;
|
private readonly windowMs = 10_000;
|
||||||
private readonly proxyAgent?: ProxyAgent;
|
private readonly proxyAgent?: ProxyAgent;
|
||||||
|
private readonly requestTimeoutMs = 30_000;
|
||||||
|
private readonly maxRetries = 2;
|
||||||
private requestTimestamps: number[] = [];
|
private requestTimestamps: number[] = [];
|
||||||
|
|
||||||
constructor(apiKey: string) {
|
constructor(apiKey: string) {
|
||||||
@@ -79,21 +100,19 @@ export class SunoClient {
|
|||||||
* Enforce rate limit: max 20 requests per 10 seconds.
|
* Enforce rate limit: max 20 requests per 10 seconds.
|
||||||
*/
|
*/
|
||||||
private async throttle(): Promise<void> {
|
private async throttle(): Promise<void> {
|
||||||
|
while (true) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
this.requestTimestamps = this.requestTimestamps.filter(
|
this.requestTimestamps = this.requestTimestamps.filter((ts) => now - ts < this.windowMs);
|
||||||
(ts) => now - ts < this.windowMs
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.requestTimestamps.length >= this.maxRequests) {
|
|
||||||
const oldest = this.requestTimestamps[0];
|
|
||||||
const wait = this.windowMs - (now - oldest);
|
|
||||||
if (wait > 0) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
||||||
return this.throttle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (this.requestTimestamps.length < this.maxRequests) {
|
||||||
this.requestTimestamps.push(now);
|
this.requestTimestamps.push(now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldest = this.requestTimestamps[0];
|
||||||
|
const wait = Math.max(0, this.windowMs - (now - oldest));
|
||||||
|
await sleep(wait);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async request<T = unknown>(
|
async request<T = unknown>(
|
||||||
@@ -109,18 +128,22 @@ export class SunoClient {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchOptions: RequestInit & { dispatcher?: any } = {
|
let lastError: Error | undefined;
|
||||||
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
const fetchOptions: RequestInit & { dispatcher?: ProxyAgent } = {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
body: body ? JSON.stringify(body) : undefined,
|
body: body ? JSON.stringify(body) : undefined,
|
||||||
|
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
||||||
};
|
};
|
||||||
if (this.proxyAgent) {
|
if (this.proxyAgent) {
|
||||||
fetchOptions.dispatcher = this.proxyAgent;
|
fetchOptions.dispatcher = this.proxyAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url, fetchOptions);
|
const response = await fetch(url, fetchOptions);
|
||||||
|
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
|
|
||||||
let json: SunoApiResponse<T>;
|
let json: SunoApiResponse<T>;
|
||||||
try {
|
try {
|
||||||
json = JSON.parse(text) as SunoApiResponse<T>;
|
json = JSON.parse(text) as SunoApiResponse<T>;
|
||||||
@@ -128,11 +151,33 @@ export class SunoClient {
|
|||||||
throw new Error(`Invalid JSON response from Suno API: ${text}`);
|
throw new Error(`Invalid JSON response from Suno API: ${text}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry on transient server errors / rate-limit before throwing.
|
||||||
|
if (json.code === 429 || json.code === 503 || json.code === 504) {
|
||||||
|
if (attempt < this.maxRetries) {
|
||||||
|
const backoff = Math.min(1000 * 2 ** attempt, 8000);
|
||||||
|
await sleep(backoff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (json.code !== 200) {
|
if (json.code !== 200) {
|
||||||
throw new Error(`Suno API error ${json.code}: ${json.msg || 'unknown error'}`);
|
throw new Error(`Suno API error ${json.code}: ${json.msg || 'unknown error'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
|
const retryable = isRetryableError(lastError);
|
||||||
|
if (retryable && attempt < this.maxRetries) {
|
||||||
|
const backoff = Math.min(1000 * 2 ** attempt, 8000);
|
||||||
|
await sleep(backoff);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError ?? new Error('Suno API request failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
get<T = unknown>(path: string): Promise<SunoApiResponse<T>> {
|
get<T = unknown>(path: string): Promise<SunoApiResponse<T>> {
|
||||||
@@ -167,6 +212,7 @@ export class SunoClient {
|
|||||||
status === 'FAILED' ||
|
status === 'FAILED' ||
|
||||||
status === 'CREATE_TASK_FAILED' ||
|
status === 'CREATE_TASK_FAILED' ||
|
||||||
status === 'GENERATE_AUDIO_FAILED' ||
|
status === 'GENERATE_AUDIO_FAILED' ||
|
||||||
|
status === 'GENERATE_LYRICS_FAILED' ||
|
||||||
status === 'CALLBACK_EXCEPTION' ||
|
status === 'CALLBACK_EXCEPTION' ||
|
||||||
status === 'SENSITIVE_WORD_ERROR'
|
status === 'SENSITIVE_WORD_ERROR'
|
||||||
) {
|
) {
|
||||||
@@ -174,7 +220,7 @@ export class SunoClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attempt < maxAttempts) {
|
if (attempt < maxAttempts) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
await sleep(intervalMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+123
-73
@@ -1,8 +1,10 @@
|
|||||||
import { SunoClient, SunoTaskData } from './client';
|
import { SunoClient, SunoLyricsVariant, SunoTaskData, SunoTrack } from './client';
|
||||||
import * as schemas from './schemas';
|
import * as schemas from './schemas';
|
||||||
|
|
||||||
const ASYNC_POLLING_PATHS: Record<string, string> = {
|
const ASYNC_POLLING_PATHS: Record<string, string> = {
|
||||||
'suno_generate_music': '/generate/record-info?taskId=',
|
'suno_generate_music': '/generate/record-info?taskId=',
|
||||||
|
'suno_generate_lyrics': '/lyrics/record-info?taskId=',
|
||||||
|
'suno_generate_sounds': '/generate/record-info?taskId=',
|
||||||
'suno_extend_music': '/generate/record-info?taskId=',
|
'suno_extend_music': '/generate/record-info?taskId=',
|
||||||
'suno_replace_section': '/generate/record-info?taskId=',
|
'suno_replace_section': '/generate/record-info?taskId=',
|
||||||
'suno_upload_and_cover': '/generate/record-info?taskId=',
|
'suno_upload_and_cover': '/generate/record-info?taskId=',
|
||||||
@@ -15,22 +17,80 @@ const ASYNC_POLLING_PATHS: Record<string, string> = {
|
|||||||
'suno_create_video': '/generate/video-info?taskId=',
|
'suno_create_video': '/generate/video-info?taskId=',
|
||||||
'suno_create_cover': '/generate/cover-info?taskId=',
|
'suno_create_cover': '/generate/cover-info?taskId=',
|
||||||
'suno_convert_to_wav': '/generate/wav-info?taskId=',
|
'suno_convert_to_wav': '/generate/wav-info?taskId=',
|
||||||
'suno_generate_sounds': '/generate/record-info?taskId=',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function formatTaskResult(data: SunoTaskData): string {
|
function copyDefined<T extends Record<string, unknown>>(
|
||||||
|
source: T,
|
||||||
|
target: Record<string, unknown>,
|
||||||
|
keys: (keyof T)[]
|
||||||
|
): void {
|
||||||
|
for (const key of keys) {
|
||||||
|
const value = source[key];
|
||||||
|
if (value !== undefined) {
|
||||||
|
target[key as string] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLyricsData(data: SunoTaskData): boolean {
|
||||||
|
return data.type === 'LYRICS';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLyricsResult(data: SunoTaskData): string {
|
||||||
const status = data.status;
|
const status = data.status;
|
||||||
if (status !== 'SUCCESS') {
|
if (status !== 'SUCCESS') {
|
||||||
return JSON.stringify({
|
return JSON.stringify(
|
||||||
|
{
|
||||||
taskId: data.taskId,
|
taskId: data.taskId,
|
||||||
status,
|
status,
|
||||||
errorCode: data.errorCode,
|
errorCode: data.errorCode,
|
||||||
errorMessage: data.errorMessage,
|
errorMessage: data.errorMessage,
|
||||||
}, null, 2);
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const variants = (data.response?.data as SunoLyricsVariant[] | undefined) ?? [];
|
||||||
|
return JSON.stringify(
|
||||||
|
{
|
||||||
|
taskId: data.taskId,
|
||||||
|
status,
|
||||||
|
type: data.type,
|
||||||
|
variants: variants.map((v, index) => ({
|
||||||
|
index: index + 1,
|
||||||
|
title: v.title,
|
||||||
|
text: v.text,
|
||||||
|
status: v.status,
|
||||||
|
errorMessage: v.errorMessage,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTaskResult(data: SunoTaskData): string {
|
||||||
|
if (isLyricsData(data)) {
|
||||||
|
return formatLyricsResult(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = data.status;
|
||||||
|
if (status !== 'SUCCESS') {
|
||||||
|
return JSON.stringify(
|
||||||
|
{
|
||||||
|
taskId: data.taskId,
|
||||||
|
status,
|
||||||
|
errorCode: data.errorCode,
|
||||||
|
errorMessage: data.errorMessage,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = data.response;
|
const response = data.response;
|
||||||
const tracks = response?.data ?? response?.sunoData ?? [];
|
const tracks = (response?.data ?? response?.sunoData ?? []) as SunoTrack[];
|
||||||
return JSON.stringify(
|
return JSON.stringify(
|
||||||
{
|
{
|
||||||
taskId: data.taskId,
|
taskId: data.taskId,
|
||||||
@@ -83,32 +143,33 @@ export class ToolHandlers {
|
|||||||
case 'suno_generate_music': {
|
case 'suno_generate_music': {
|
||||||
const input = schemas.GenerateMusicSchema.parse(args);
|
const input = schemas.GenerateMusicSchema.parse(args);
|
||||||
const payload: Record<string, unknown> = {
|
const payload: Record<string, unknown> = {
|
||||||
prompt: input.prompt,
|
|
||||||
customMode: input.customMode,
|
customMode: input.customMode,
|
||||||
instrumental: input.instrumental,
|
instrumental: input.instrumental,
|
||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
copyDefined(input, payload, [
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
'prompt',
|
||||||
if (input.negativeTags !== undefined) payload.negativeTags = input.negativeTags;
|
'style',
|
||||||
if (input.vocalGender !== undefined) payload.vocalGender = input.vocalGender;
|
'title',
|
||||||
if (input.styleWeight !== undefined) payload.styleWeight = input.styleWeight;
|
'negativeTags',
|
||||||
if (input.weirdnessConstraint !== undefined) payload.weirdnessConstraint = input.weirdnessConstraint;
|
'vocalGender',
|
||||||
if (input.audioWeight !== undefined) payload.audioWeight = input.audioWeight;
|
'styleWeight',
|
||||||
if (input.personaId !== undefined) payload.personaId = input.personaId;
|
'weirdnessConstraint',
|
||||||
if (input.personaModel !== undefined) payload.personaModel = input.personaModel;
|
'audioWeight',
|
||||||
|
'personaId',
|
||||||
|
'personaModel',
|
||||||
|
]);
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'suno_generate_lyrics': {
|
case 'suno_generate_lyrics': {
|
||||||
const input = schemas.GenerateLyricsSchema.parse(args);
|
const input = schemas.GenerateLyricsSchema.parse(args);
|
||||||
const result = await this.client.post<{ taskId: string }>('/lyrics', {
|
resultText = await runAsyncTask(this.client, name, '/lyrics', {
|
||||||
prompt: input.prompt,
|
prompt: input.prompt,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
});
|
});
|
||||||
resultText = JSON.stringify({ taskId: result.data.taskId, status: 'PENDING' }, null, 2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,10 +180,7 @@ export class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.soundLoop !== undefined) payload.soundLoop = input.soundLoop;
|
copyDefined(input, payload, ['soundLoop', 'soundTempo', 'soundKey', 'grabLyrics']);
|
||||||
if (input.soundTempo !== undefined) payload.soundTempo = input.soundTempo;
|
|
||||||
if (input.soundKey !== undefined) payload.soundKey = input.soundKey;
|
|
||||||
if (input.grabLyrics !== undefined) payload.grabLyrics = input.grabLyrics;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/sounds', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/sounds', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -135,17 +193,19 @@ export class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.prompt !== undefined) payload.prompt = input.prompt;
|
copyDefined(input, payload, [
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
'prompt',
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
'style',
|
||||||
if (input.continueAt !== undefined) payload.continueAt = input.continueAt;
|
'title',
|
||||||
if (input.negativeTags !== undefined) payload.negativeTags = input.negativeTags;
|
'continueAt',
|
||||||
if (input.vocalGender !== undefined) payload.vocalGender = input.vocalGender;
|
'negativeTags',
|
||||||
if (input.styleWeight !== undefined) payload.styleWeight = input.styleWeight;
|
'vocalGender',
|
||||||
if (input.weirdnessConstraint !== undefined) payload.weirdnessConstraint = input.weirdnessConstraint;
|
'styleWeight',
|
||||||
if (input.audioWeight !== undefined) payload.audioWeight = input.audioWeight;
|
'weirdnessConstraint',
|
||||||
if (input.personaId !== undefined) payload.personaId = input.personaId;
|
'audioWeight',
|
||||||
if (input.personaModel !== undefined) payload.personaModel = input.personaModel;
|
'personaId',
|
||||||
|
'personaModel',
|
||||||
|
]);
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/extend', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/extend', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -160,11 +220,7 @@ export class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.replaceAudioUrl !== undefined) payload.replaceAudioUrl = input.replaceAudioUrl;
|
copyDefined(input, payload, ['replaceAudioUrl', 'customMode', 'style', 'title', 'instrumental']);
|
||||||
if (input.customMode !== undefined) payload.customMode = input.customMode;
|
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
|
||||||
if (input.instrumental !== undefined) payload.instrumental = input.instrumental;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/replace-section', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/replace-section', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -173,13 +229,10 @@ export class ToolHandlers {
|
|||||||
const input = schemas.AddVocalsSchema.parse(args);
|
const input = schemas.AddVocalsSchema.parse(args);
|
||||||
const payload: Record<string, unknown> = {
|
const payload: Record<string, unknown> = {
|
||||||
audioId: input.audioId,
|
audioId: input.audioId,
|
||||||
prompt: input.prompt,
|
|
||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.customMode !== undefined) payload.customMode = input.customMode;
|
copyDefined(input, payload, ['prompt', 'customMode', 'style', 'title']);
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/add-vocals', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/add-vocals', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -188,13 +241,10 @@ export class ToolHandlers {
|
|||||||
const input = schemas.AddInstrumentalSchema.parse(args);
|
const input = schemas.AddInstrumentalSchema.parse(args);
|
||||||
const payload: Record<string, unknown> = {
|
const payload: Record<string, unknown> = {
|
||||||
audioId: input.audioId,
|
audioId: input.audioId,
|
||||||
prompt: input.prompt,
|
|
||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.customMode !== undefined) payload.customMode = input.customMode;
|
copyDefined(input, payload, ['prompt', 'customMode', 'style', 'title']);
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/add-instrumental', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/add-instrumental', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -208,16 +258,18 @@ export class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.prompt !== undefined) payload.prompt = input.prompt;
|
copyDefined(input, payload, [
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
'prompt',
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
'style',
|
||||||
if (input.negativeTags !== undefined) payload.negativeTags = input.negativeTags;
|
'title',
|
||||||
if (input.vocalGender !== undefined) payload.vocalGender = input.vocalGender;
|
'negativeTags',
|
||||||
if (input.styleWeight !== undefined) payload.styleWeight = input.styleWeight;
|
'vocalGender',
|
||||||
if (input.weirdnessConstraint !== undefined) payload.weirdnessConstraint = input.weirdnessConstraint;
|
'styleWeight',
|
||||||
if (input.audioWeight !== undefined) payload.audioWeight = input.audioWeight;
|
'weirdnessConstraint',
|
||||||
if (input.personaId !== undefined) payload.personaId = input.personaId;
|
'audioWeight',
|
||||||
if (input.personaModel !== undefined) payload.personaModel = input.personaModel;
|
'personaId',
|
||||||
|
'personaModel',
|
||||||
|
]);
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/upload-cover', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/upload-cover', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -230,10 +282,7 @@ export class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.prompt !== undefined) payload.prompt = input.prompt;
|
copyDefined(input, payload, ['prompt', 'style', 'title', 'continueAt']);
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
|
||||||
if (input.continueAt !== undefined) payload.continueAt = input.continueAt;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/upload-extend', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/upload-extend', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -247,14 +296,16 @@ export class ToolHandlers {
|
|||||||
model: input.model,
|
model: input.model,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.instrumental !== undefined) payload.instrumental = input.instrumental;
|
copyDefined(input, payload, [
|
||||||
if (input.style !== undefined) payload.style = input.style;
|
'instrumental',
|
||||||
if (input.title !== undefined) payload.title = input.title;
|
'style',
|
||||||
if (input.negativeTags !== undefined) payload.negativeTags = input.negativeTags;
|
'title',
|
||||||
if (input.vocalGender !== undefined) payload.vocalGender = input.vocalGender;
|
'negativeTags',
|
||||||
if (input.styleWeight !== undefined) payload.styleWeight = input.styleWeight;
|
'vocalGender',
|
||||||
if (input.weirdnessConstraint !== undefined) payload.weirdnessConstraint = input.weirdnessConstraint;
|
'styleWeight',
|
||||||
if (input.audioWeight !== undefined) payload.audioWeight = input.audioWeight;
|
'weirdnessConstraint',
|
||||||
|
'audioWeight',
|
||||||
|
]);
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/mashup', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/mashup', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -287,8 +338,7 @@ export class ToolHandlers {
|
|||||||
audioId: input.audioId,
|
audioId: input.audioId,
|
||||||
callBackUrl: input.callBackUrl ?? '',
|
callBackUrl: input.callBackUrl ?? '',
|
||||||
};
|
};
|
||||||
if (input.author !== undefined) payload.author = input.author;
|
copyDefined(input, payload, ['author', 'domainName']);
|
||||||
if (input.domainName !== undefined) payload.domainName = input.domainName;
|
|
||||||
resultText = await runAsyncTask(this.client, name, '/generate/video', payload);
|
resultText = await runAsyncTask(this.client, name, '/generate/video', payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -353,10 +403,10 @@ export class ToolHandlers {
|
|||||||
|
|
||||||
case 'suno_get_lyrics_details': {
|
case 'suno_get_lyrics_details': {
|
||||||
const input = schemas.GetLyricsDetailsSchema.parse(args);
|
const input = schemas.GetLyricsDetailsSchema.parse(args);
|
||||||
const result = await this.client.get<unknown>(
|
const result = await this.client.get<SunoTaskData>(
|
||||||
`/generate/lyrics-info?taskId=${encodeURIComponent(input.taskId)}`
|
`/lyrics/record-info?taskId=${encodeURIComponent(input.taskId)}`
|
||||||
);
|
);
|
||||||
resultText = JSON.stringify(result.data, null, 2);
|
resultText = formatLyricsResult(result.data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,18 +22,50 @@ const CommonGenerateParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 1. Generate Music (12 credits)
|
// 1. Generate Music (12 credits)
|
||||||
export const GenerateMusicSchema = z.object({
|
export const GenerateMusicSchema = z
|
||||||
prompt: z.string().optional().describe('Music description / lyrics (max 500 chars non-custom, 3000-5000 custom mode; required when customMode=false or customMode=true with instrumental=false)'),
|
.object({
|
||||||
|
prompt: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Music description / lyrics (max 500 chars non-custom, 3000-5000 custom mode; required when customMode=false or customMode=true with instrumental=false)'
|
||||||
|
),
|
||||||
customMode: z.boolean().describe('Enable custom mode (requires style and title)'),
|
customMode: z.boolean().describe('Enable custom mode (requires style and title)'),
|
||||||
instrumental: z.boolean().describe('Generate instrumental without vocals'),
|
instrumental: z.boolean().describe('Generate instrumental without vocals'),
|
||||||
style: z.string().optional().describe('Music style/genre (required when customMode=true)'),
|
style: z.string().optional().describe('Music style/genre (required when customMode=true)'),
|
||||||
title: z.string().optional().describe('Song title (required when customMode=true)'),
|
title: z.string().optional().describe('Song title (required when customMode=true)'),
|
||||||
...CommonGenerateParams,
|
...CommonGenerateParams,
|
||||||
}).strict();
|
})
|
||||||
|
.strict()
|
||||||
|
.superRefine(requirePromptWhenNeeded)
|
||||||
|
.refine(
|
||||||
|
(data) => !data.customMode || (!!data.style && !!data.title),
|
||||||
|
{
|
||||||
|
message: 'style and title are required when customMode is true',
|
||||||
|
path: ['style'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function requirePromptWhenNeeded(data: { customMode: boolean; instrumental: boolean; prompt?: string }, ctx: z.RefinementCtx): void {
|
||||||
|
if (!data.customMode && !data.prompt) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: 'prompt is required when customMode is false',
|
||||||
|
path: ['prompt'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.customMode && !data.instrumental && !data.prompt) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: 'prompt is required when customMode is true and instrumental is false',
|
||||||
|
path: ['prompt'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Generate Lyrics (0.4 credits)
|
// 2. Generate Lyrics (0.4 credits)
|
||||||
export const GenerateLyricsSchema = z.object({
|
export const GenerateLyricsSchema = z.object({
|
||||||
prompt: z.string().describe('Topic or theme for lyrics'),
|
prompt: z.string().max(200).describe('Topic or theme for lyrics (max 200 chars)'),
|
||||||
callBackUrl: CallbackUrl,
|
callBackUrl: CallbackUrl,
|
||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
@@ -108,7 +140,8 @@ export const AddInstrumentalSchema = z.object({
|
|||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
// 8. Upload and Cover (12 credits)
|
// 8. Upload and Cover (12 credits)
|
||||||
export const UploadAndCoverSchema = z.object({
|
export const UploadAndCoverSchema = z
|
||||||
|
.object({
|
||||||
uploadUrl: z.string().url().describe('Public URL of audio file to cover (max 8 minutes)'),
|
uploadUrl: z.string().url().describe('Public URL of audio file to cover (max 8 minutes)'),
|
||||||
prompt: z.string().optional(),
|
prompt: z.string().optional(),
|
||||||
customMode: z.boolean(),
|
customMode: z.boolean(),
|
||||||
@@ -124,7 +157,16 @@ export const UploadAndCoverSchema = z.object({
|
|||||||
personaId: z.string().optional(),
|
personaId: z.string().optional(),
|
||||||
personaModel: PersonaModelEnum,
|
personaModel: PersonaModelEnum,
|
||||||
callBackUrl: CallbackUrl,
|
callBackUrl: CallbackUrl,
|
||||||
}).strict();
|
})
|
||||||
|
.strict()
|
||||||
|
.superRefine(requirePromptWhenNeeded)
|
||||||
|
.refine(
|
||||||
|
(data) => !data.customMode || (!!data.style && !!data.title),
|
||||||
|
{
|
||||||
|
message: 'style and title are required when customMode is true',
|
||||||
|
path: ['style'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 9. Upload and Extend (12 credits)
|
// 9. Upload and Extend (12 credits)
|
||||||
export const UploadAndExtendSchema = z.object({
|
export const UploadAndExtendSchema = z.object({
|
||||||
@@ -187,6 +229,7 @@ export const GeneratePersonaSchema = z.object({
|
|||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
// 17. Generate Mashup (12 credits)
|
// 17. Generate Mashup (12 credits)
|
||||||
|
// Note: Suno API docs list only V4/V4_5/V4_5PLUS/V4_5ALL/V5 for mashup; V5_5 is not supported.
|
||||||
export const GenerateMashupSchema = z.object({
|
export const GenerateMashupSchema = z.object({
|
||||||
uploadUrlList: z.array(z.string().url()).length(2).describe('Exactly 2 audio URLs to mash up'),
|
uploadUrlList: z.array(z.string().url()).length(2).describe('Exactly 2 audio URLs to mash up'),
|
||||||
customMode: z.boolean(),
|
customMode: z.boolean(),
|
||||||
|
|||||||
Reference in New Issue
Block a user