Music Interface (Generation and Lyrics)
Music generation capability is unified under IMusicService, currently supporting:
SUNO(via the ChatFire native Suno gateway)
As with video generation, music generation follows an async submit → poll model: submitMusic / submitLyrics only submit the task and immediately return a task id. To actually obtain the track you must fetch the task result.
1. Unified Entry Point
AiService aiService = new AiService(configuration);
IMusicService musicService = aiService.getMusicService(PlatformType.SUNO);
IMusicService exposes three categories of capability. Each provides an overload that takes baseUrl / apiKey explicitly, plus an overload that falls back to the default configuration:
submitMusic(...)— submit a music generation tasksubmitLyrics(...)— submit a lyrics generation taskfetch(...)— poll the task result
2. Submit Music Generation (submitMusic)
SunoMusicRequest request = SunoMusicRequest.builder()
.prompt("[Verse] city lights")
.tags("emotional punk")
.mv("chirp-v4")
.title("City Lights")
.makeInstrumental(Boolean.FALSE)
.gptDescriptionPrompt("write a song")
.build();
SunoSubmitResponse response = musicService.submitMusic(request);
// response.isSuccess() indicates whether submission succeeded
// response.getData() is the taskId used by subsequent fetch calls
submitMusic POSTs to the Suno gateway's music submission endpoint (default suno/submit/music) and returns a SunoSubmitResponse:
code—successindicates successmessage— informational messagedata— task id (string), the input tofetchraw— the provider's original JSON
3. Key Fields of SunoMusicRequest
SunoMusicRequest uses @JsonAnyGetter / @JsonAnySetter to reserve an extraFields pass-through. Its core fields follow Suno's native semantics:
prompt— lyrics / description bodytags— style tagsmv— model version (e.g.chirp-v4)title— track titlegptDescriptionPrompt— GPT description promptmakeInstrumental— whether to generate instrumental onlygenerationType— generation typenegativeTags— negative style tagscontinueAt/continueClipId/continuedAlignedPrompt— continuation-relatedinfillStartS/infillEndS— partial infill (seconds)task— task typecoverClipId— cover clip id
4. Submit Lyrics Generation (submitLyrics)
submitLyrics targets a separate lyrics endpoint (default suno/submit/lyrics) and only accepts prompt:
Map<String, Object> extra = new LinkedHashMap<String, Object>();
extra.put("language", "zh");
SunoSubmitResponse response = musicService.submitLyrics(SunoLyricsRequest.builder()
.prompt("chat fire")
.extraFields(extra)
.build());
The response structure matches submitMusic; data is still a task id.
5. Poll the Task Result (fetch)
SunoFetchResponse result = musicService.fetch(response.getData());
// result.isSuccess() : whether the call succeeded
// result.getData() : SunoTask
// result.getData().getStatus() : task status (e.g. SUCCESS / IN_PROGRESS)
// result.getData().getData() : track array (JsonNode)
fetch URL-encodes the task id, so ids containing /, :, spaces, and similar characters are still requested correctly. fetchUrl also supports the {task_id} placeholder form (e.g. suno/fetch/{task_id}), configured via SunoConfig.
6. Task Objects (SunoTask / SunoSong)
SunoFetchResponse.data is a SunoTask:
taskIdaction— task action (e.g.MUSIC)status— status (IN_PROGRESS/SUCCESS/ failure)failReasonsubmitTime/startTime/finishTimeprogressdata— result payload (JsonNode). Under theMUSICaction this is typically a set ofSunoSong
SunoTask.data is a loosely typed JsonNode, because the result structure differs across Suno actions. Array elements of a MUSIC result can be deserialized into SunoSong:
JsonNode data = result.getData().getData();
if (data != null && data.isArray()) {
for (JsonNode node : data) {
SunoSong song = new ObjectMapper().treeToValue(node, SunoSong.class);
// song.getAudioUrl() / song.getImageUrl() / song.getVideoUrl() ...
}
}
Modeled fields on SunoSong:
id/clipId/title/handletags/prompt/state/statusduration/metadataaudioUrl— direct audio URLimageUrl/imageLargeUrl— cover imagevideoUrl— visualization video direct URLmodelName
7. A Complete Submit → Poll → Retrieve Track Flow
IMusicService musicService = aiService.getMusicService(PlatformType.SUNO);
// 1. Submit
SunoSubmitResponse submitted = musicService.submitMusic(SunoMusicRequest.builder()
.prompt("[Verse] city lights")
.tags("emotional punk")
.mv("chirp-v4")
.title("City Lights")
.makeInstrumental(Boolean.FALSE)
.build());
String taskId = submitted.getData();
// 2. Poll (interval and retry limit controlled by the business layer)
SunoFetchResponse latest = musicService.fetch(taskId);
while (!"SUCCESS".equalsIgnoreCase(latest.getData().getStatus())
&& latest.getData().getFailReason() == null) {
Thread.sleep(5000L);
latest = musicService.fetch(taskId);
}
// 3. Retrieve tracks
if (latest.isSuccess() && latest.getData().getData() != null
&& latest.getData().getData().isArray()) {
for (JsonNode node : latest.getData().getData()) {
SunoSong song = new ObjectMapper().treeToValue(node, SunoSong.class);
System.out.println(song.getAudioUrl());
}
}
The SDK does not build in an automatic polling loop. When to poll, the polling interval, timeout, and retries are all decided by the caller. fetch is a single synchronous GET.
8. Common Issues
8.1 What isSuccess() Actually Checks
The isSuccess() on SunoSubmitResponse and SunoFetchResponse only checks code == "success", meaning the HTTP call itself succeeded — it does not mean the music has finished generating. For generation progress, check SunoTask.status.
8.2 URL Lifespan
audioUrl / videoUrl / imageUrl are typically temporary direct links. Download them promptly or transfer them to your own object storage to avoid link expiration.
8.3 Endpoints and Placeholders
SunoConfig defaults to apiHost=https://api.chatfire.cn/, musicUrl=suno/submit/music, lyricsUrl=suno/submit/lyrics, fetchUrl=suno/fetch. If the gateway's fetch endpoint uses a path-parameter form, configure fetchUrl as a template with the {task_id} placeholder (e.g. suno/fetch/{task_id}).
9. Takeaway
IMusicServiceis currently an async music generation service surface implemented by the Suno gateway:submitMusic/submitLyricssubmit a task to obtain a task id, andfetchpolls the task result. The task result is a loosely typedJsonNode; under theMUSICaction it can be deserialized intoSunoSong. The SDK does not build in a polling loop — the submit → poll → retrieve lifecycle orchestration is the caller's responsibility.