runapi-suno 0.1.3__tar.gz → 0.2.0__tar.gz

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.
Files changed (33) hide show
  1. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/PKG-INFO +11 -5
  2. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/README.md +7 -1
  3. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/pyproject.toml +4 -4
  4. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/client.py +2 -0
  5. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/contract_gen.py +18 -1
  6. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/__init__.py +2 -0
  7. runapi_suno-0.2.0/src/runapi/suno/resources/blend_lyrics.py +30 -0
  8. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/types.py +12 -0
  9. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/tests/test_client.py +20 -1
  10. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/__init__.py +0 -0
  11. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/_validators.py +0 -0
  12. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/py.typed +0 -0
  13. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/add_instrumental.py +0 -0
  14. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/add_vocals.py +0 -0
  15. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/boost_style.py +0 -0
  16. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/check_voice.py +0 -0
  17. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/convert_audio.py +0 -0
  18. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/cover_audio.py +0 -0
  19. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/create_mashup.py +0 -0
  20. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/extend_music.py +0 -0
  21. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_artwork.py +0 -0
  22. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_lyrics.py +0 -0
  23. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_midi.py +0 -0
  24. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_persona.py +0 -0
  25. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_voice.py +0 -0
  26. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/get_timestamped_lyrics.py +0 -0
  27. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/regenerate_validation_phrase.py +0 -0
  28. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/replace_section.py +0 -0
  29. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/separate_audio_stems.py +0 -0
  30. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/text_to_music.py +0 -0
  31. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/text_to_sound.py +0 -0
  32. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/visualize_music.py +0 -0
  33. {runapi_suno-0.1.3 → runapi_suno-0.2.0}/src/runapi/suno/resources/voice_to_validation_phrase.py +0 -0
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runapi-suno
3
- Version: 0.1.3
4
- Summary: RunAPI Suno SDK for text-to-music, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP
3
+ Version: 0.2.0
4
+ Summary: RunAPI Suno SDK for text-to-music, lyric generation and blending, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP
5
5
  Project-URL: Homepage, https://runapi.ai/models/suno
6
6
  Project-URL: Documentation, https://runapi.ai/docs#sdk-suno
7
7
  Project-URL: Source, https://github.com/runapi-ai/suno-sdk
8
8
  Project-URL: Issues, https://github.com/runapi-ai/suno-sdk/issues
9
9
  Project-URL: Changelog, https://github.com/runapi-ai/suno-sdk/blob/main/CHANGELOG.md
10
- Project-URL: Release Notes, https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.1.3
10
+ Project-URL: Release Notes, https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.2.0
11
11
  Author-email: RunAPI <contact@runapi.ai>
12
12
  License-Expression: Apache-2.0
13
13
  Keywords: api,audio-generation,golang,gradle,java,maven,music-api,music-generation,python,ruby,runapi,runapi-ai,sdk,suno,suno-ai-api,typescript
14
14
  Requires-Python: >=3.9
15
- Requires-Dist: runapi-core>=0.2.1
15
+ Requires-Dist: runapi-core>=0.2.2
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  # Suno API Python SDK for RunAPI
@@ -45,6 +45,12 @@ print(result.audios[0].audio_url)
45
45
  # Generate lyrics from a prompt.
46
46
  lyrics = client.generate_lyrics.run(prompt="a song about the ocean at night")
47
47
  print(lyrics.lyrics[0].text)
48
+
49
+ blended = client.blend_lyrics.run(
50
+ lyrics_a="[Verse]\nCity lights fade into dawn",
51
+ lyrics_b="[Chorus]\nWe rise with the morning sun",
52
+ )
53
+ print(blended.lyrics[0].text)
48
54
  ```
49
55
 
50
56
  Use `create` to submit a task and return quickly, `get` to fetch the latest task state, and `run` when a script should create and poll until completion:
@@ -65,7 +71,7 @@ RunAPI-generated file URLs are temporary. Download and store generated images, v
65
71
 
66
72
  ## Language notes
67
73
 
68
- Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
74
+ Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `blend_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
69
75
 
70
76
  ## Links
71
77
 
@@ -28,6 +28,12 @@ print(result.audios[0].audio_url)
28
28
  # Generate lyrics from a prompt.
29
29
  lyrics = client.generate_lyrics.run(prompt="a song about the ocean at night")
30
30
  print(lyrics.lyrics[0].text)
31
+
32
+ blended = client.blend_lyrics.run(
33
+ lyrics_a="[Verse]\nCity lights fade into dawn",
34
+ lyrics_b="[Chorus]\nWe rise with the morning sun",
35
+ )
36
+ print(blended.lyrics[0].text)
31
37
  ```
32
38
 
33
39
  Use `create` to submit a task and return quickly, `get` to fetch the latest task state, and `run` when a script should create and poll until completion:
@@ -48,7 +54,7 @@ RunAPI-generated file URLs are temporary. Download and store generated images, v
48
54
 
49
55
  ## Language notes
50
56
 
51
- Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
57
+ Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `blend_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
52
58
 
53
59
  ## Links
54
60
 
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "runapi-suno"
7
- version = "0.1.3"
8
- description = "RunAPI Suno SDK for text-to-music, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP"
7
+ version = "0.2.0"
8
+ description = "RunAPI Suno SDK for text-to-music, lyric generation and blending, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  license = "Apache-2.0"
12
12
  authors = [{ name = "RunAPI", email = "contact@runapi.ai" }]
13
13
  keywords = ["runapi", "runapi-ai", "suno", "api", "sdk", "typescript", "python", "ruby", "golang", "java", "maven", "gradle", "music-generation", "audio-generation", "music-api", "suno-ai-api"]
14
- dependencies = ["runapi-core>=0.2.1"]
14
+ dependencies = ["runapi-core>=0.2.2"]
15
15
 
16
16
  [project.urls]
17
17
  Homepage = "https://runapi.ai/models/suno"
@@ -19,7 +19,7 @@ Documentation = "https://runapi.ai/docs#sdk-suno"
19
19
  Source = "https://github.com/runapi-ai/suno-sdk"
20
20
  Issues = "https://github.com/runapi-ai/suno-sdk/issues"
21
21
  Changelog = "https://github.com/runapi-ai/suno-sdk/blob/main/CHANGELOG.md"
22
- "Release Notes" = "https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.1.3"
22
+ "Release Notes" = "https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.2.0"
23
23
 
24
24
  [tool.hatch.build.targets.wheel]
25
25
  packages = ["src/runapi"]
@@ -9,6 +9,7 @@ from runapi.core import ClientOptions, HttpClient, resolve_api_key
9
9
  from .resources.add_instrumental import AddInstrumental
10
10
  from .resources.add_vocals import AddVocals
11
11
  from .resources.boost_style import BoostStyle
12
+ from .resources.blend_lyrics import BlendLyrics
12
13
  from .resources.check_voice import CheckVoice
13
14
  from .resources.convert_audio import ConvertAudio
14
15
  from .resources.cover_audio import CoverAudio
@@ -58,6 +59,7 @@ class SunoClient:
58
59
  self.convert_audio = ConvertAudio(http)
59
60
  self.visualize_music = VisualizeMusic(http)
60
61
  self.generate_lyrics = GenerateLyrics(http)
62
+ self.blend_lyrics = BlendLyrics(http)
61
63
  self.get_timestamped_lyrics = GetTimestampedLyrics(http)
62
64
  self.replace_section = ReplaceSection(http)
63
65
  self.create_mashup = CreateMashup(http)
@@ -70,6 +70,19 @@ CONTRACT = {
70
70
  }
71
71
  }
72
72
  },
73
+ "blend-lyrics": {
74
+ "models": [],
75
+ "fields_by_model": {
76
+ "_": {
77
+ "lyrics_a": {
78
+ "required": True
79
+ },
80
+ "lyrics_b": {
81
+ "required": True
82
+ }
83
+ }
84
+ }
85
+ },
73
86
  "boost-style": {
74
87
  "models": [],
75
88
  "fields_by_model": {
@@ -470,7 +483,11 @@ CONTRACT = {
470
483
  "generate-lyrics": {
471
484
  "models": [],
472
485
  "fields_by_model": {
473
- "_": {}
486
+ "_": {
487
+ "prompt": {
488
+ "required": True
489
+ }
490
+ }
474
491
  }
475
492
  },
476
493
  "generate-midi": {
@@ -1,6 +1,7 @@
1
1
  from .add_instrumental import AddInstrumental
2
2
  from .add_vocals import AddVocals
3
3
  from .boost_style import BoostStyle
4
+ from .blend_lyrics import BlendLyrics
4
5
  from .check_voice import CheckVoice
5
6
  from .convert_audio import ConvertAudio
6
7
  from .cover_audio import CoverAudio
@@ -32,6 +33,7 @@ __all__ = [
32
33
  "ConvertAudio",
33
34
  "VisualizeMusic",
34
35
  "GenerateLyrics",
36
+ "BlendLyrics",
35
37
  "GetTimestampedLyrics",
36
38
  "ReplaceSection",
37
39
  "CreateMashup",
@@ -0,0 +1,30 @@
1
+ """Suno blend_lyrics resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from runapi.core import RequestOptions, Resource
8
+
9
+ from ..contract_gen import CONTRACT
10
+ from ..types import BlendLyricsResponse, CompletedBlendLyricsResponse
11
+
12
+
13
+ class BlendLyrics(Resource):
14
+ """Blend two caller-authored lyrics texts."""
15
+
16
+ ENDPOINT = "/api/v1/suno/blend_lyrics"
17
+ RESPONSE_CLASS = BlendLyricsResponse
18
+ COMPLETED_RESPONSE_CLASS = CompletedBlendLyricsResponse
19
+
20
+ def run(self, options: Optional[RequestOptions] = None, **params: Any) -> Any:
21
+ task = self.create(options=options, **params)
22
+ return self._poll_until_complete(lambda: self.get(task.id, options=options))
23
+
24
+ def create(self, options: Optional[RequestOptions] = None, **params: Any) -> Any:
25
+ compacted = self._compact_params(params)
26
+ self._validate_contract(CONTRACT["blend-lyrics"], compacted)
27
+ return self._request("post", self.ENDPOINT, body=compacted, options=options)
28
+
29
+ def get(self, id: str, options: Optional[RequestOptions] = None) -> Any:
30
+ return self._request("get", f"{self.ENDPOINT}/{id}", options=options)
@@ -228,6 +228,12 @@ class GenerateLyricsResponse(AsyncTaskResponse):
228
228
  lyrics = optional([lambda: Lyric])
229
229
 
230
230
 
231
+ class BlendLyricsResponse(AsyncTaskResponse):
232
+ """Suno lyrics blending task status response."""
233
+
234
+ lyrics = optional([lambda: Lyric])
235
+
236
+
231
237
  class GetTimestampedLyricsResponse(BaseModel):
232
238
  """Suno timestamped-lyrics result."""
233
239
 
@@ -352,6 +358,12 @@ class CompletedGenerateLyricsResponse(GenerateLyricsResponse):
352
358
  lyrics = required([lambda: Lyric])
353
359
 
354
360
 
361
+ class CompletedBlendLyricsResponse(BlendLyricsResponse):
362
+ """Narrowed lyrics blending response once polling observes completion."""
363
+
364
+ lyrics = required([lambda: Lyric])
365
+
366
+
355
367
  class CompletedReplaceSectionResponse(ReplaceSectionResponse):
356
368
  """Narrowed replace-section response once polling observes completion."""
357
369
 
@@ -80,6 +80,7 @@ def test_exposes_all_resource_accessors():
80
80
  "convert_audio": R.ConvertAudio,
81
81
  "visualize_music": R.VisualizeMusic,
82
82
  "generate_lyrics": R.GenerateLyrics,
83
+ "blend_lyrics": R.BlendLyrics,
83
84
  "get_timestamped_lyrics": R.GetTimestampedLyrics,
84
85
  "replace_section": R.ReplaceSection,
85
86
  "create_mashup": R.CreateMashup,
@@ -91,7 +92,7 @@ def test_exposes_all_resource_accessors():
91
92
  "generate_persona": R.GeneratePersona,
92
93
  "boost_style": R.BoostStyle,
93
94
  }
94
- assert len(expected) == 21
95
+ assert len(expected) == 22
95
96
  for name, cls in expected.items():
96
97
  assert isinstance(getattr(client, name), cls), name
97
98
 
@@ -122,6 +123,24 @@ def test_get_fetches_by_id():
122
123
  assert fake.calls == [("get", "/api/v1/suno/text_to_music/t1", None)]
123
124
 
124
125
 
126
+ def test_blend_lyrics_posts_flat_lyrics_pair():
127
+ fake = FakeHttp({"id": "blend_1", "status": "processing"})
128
+ client = SunoClient(api_key="k", http_client=fake)
129
+
130
+ client.blend_lyrics.create(lyrics_a="First verse", lyrics_b="Second verse")
131
+
132
+ assert fake.calls == [
133
+ ("post", "/api/v1/suno/blend_lyrics", {"lyrics_a": "First verse", "lyrics_b": "Second verse"}),
134
+ ]
135
+
136
+
137
+ def test_blend_lyrics_requires_both_lyrics_texts():
138
+ client = SunoClient(api_key="k", http_client=FakeHttp())
139
+
140
+ with pytest.raises(ValidationError, match="lyrics_b"):
141
+ client.blend_lyrics.create(lyrics_a="First verse")
142
+
143
+
125
144
  def test_run_narrows_completed_type():
126
145
  fake = FakeHttp(
127
146
  {"id": "t1", "status": "pending"},