@elizaos/plugin-suno 2.0.0-beta.1 → 2.0.3-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,207 +1,81 @@
1
- @elizaos/plugin-suno
2
-
3
- A Suno AI music generation plugin for ElizaOS that enables AI-powered music creation and audio manipulation.
4
-
5
- OVERVIEW
6
-
7
- The Suno plugin integrates Suno AI's powerful music generation capabilities into ElizaOS, providing a seamless way to:
8
- - Generate music from text prompts with fine-tuned parameters
9
- - Create custom music with advanced control over style, tempo, and key
10
- - Extend existing audio tracks
11
-
12
- Original Plugin: https://github.com/gcui-art/suno-api?tab=readme-ov-file
13
-
14
- INSTALLATION
15
-
16
- npm install @elizaos/plugin-suno
17
-
18
- QUICK START
19
-
20
- 1. Register the plugin with ElizaOS:
21
-
22
- import { sunoPlugin } from '@elizaos/plugin-suno';
23
- import { Eliza } from '@elizaos/core';
24
-
25
- const eliza = new Eliza();
26
- eliza.registerPlugin(sunoPlugin);
27
-
28
- 2. Configure the Suno provider with your API credentials:
29
-
30
- import { sunoProvider } from '@elizaos/plugin-suno';
31
-
32
- sunoProvider.configure({
33
- apiKey: 'your-suno-api-key'
34
- });
35
-
36
- FEATURES
37
-
38
- 1. Generate Music (suno.generate-music)
39
- Generate music using a text prompt with basic control parameters. This is ideal for quick music generation when you need less fine-grained control:
40
-
41
- - Simple text-to-music generation
42
- - Consistent output quality with default parameters
43
- - Suitable for most common use cases
44
-
45
- await eliza.execute('suno.generate-music', {
46
- prompt: "An upbeat electronic dance track with energetic beats",
47
- duration: 30,
48
- temperature: 1.0,
49
- topK: 250,
50
- topP: 0.95,
51
- classifier_free_guidance: 3.0
52
- });
53
-
54
- 2. Custom Music Generation (suno.custom-generate-music)
55
- Create music with detailed control over generation parameters. Perfect for when you need precise control over the musical output:
56
-
57
- - Fine-grained control over musical style and structure
58
- - Reference-based generation using existing audio
59
- - Control over musical attributes:
60
- * Style: Specify genres like "classical", "electronic", "rock"
61
- * Tempo: Set exact BPM (beats per minute)
62
- * Key and Mode: Define musical key (e.g., "C") and mode ("major"/"minor")
63
- - Advanced parameter tuning for generation quality
64
-
65
- await eliza.execute('suno.custom-generate-music', {
66
- prompt: "A melodic piano piece with soft strings",
67
- duration: 30,
68
- temperature: 0.8,
69
- topK: 250,
70
- topP: 0.95,
71
- classifier_free_guidance: 3.0,
72
- reference_audio: "path/to/reference.mp3",
73
- style: "classical",
74
- bpm: 120,
75
- key: "C",
76
- mode: "major"
77
- });
78
-
79
- 3. Extend Audio (suno.extend-audio)
80
- Extend existing audio tracks to create longer compositions. Useful for:
81
-
82
- - Lengthening existing music pieces
83
- - Creating seamless loops
84
- - Generating variations of existing tracks
85
-
86
- await eliza.execute('suno.extend-audio', {
87
- audio_id: "your-audio-id",
88
- duration: 60
89
- });
90
-
91
- Generation Parameters Explained:
92
-
93
- - temperature: Controls randomness in generation (0.0-1.0+)
94
- * Lower values (0.1-0.5): More conservative, consistent output
95
- * Higher values (1.0+): More creative, varied output
96
-
97
- - classifier_free_guidance: Controls how closely the output follows the prompt (1.0-20.0)
98
- * Lower values: More creative interpretation
99
- * Higher values: Stricter adherence to prompt
100
-
101
- - topK/topP: Control the diversity of the generation
102
- * topK: Limits the number of tokens considered
103
- * topP: Controls the cumulative probability threshold
104
-
105
- API REFERENCE
106
-
107
- SunoProvider Configuration
108
- The Suno provider accepts the following configuration options:
109
-
110
- interface SunoConfig {
111
- apiKey: string;
112
- }
113
-
114
- Action Parameters:
115
-
116
- 1. Generate Music (suno.generate-music)
117
- interface GenerateParams {
118
- prompt: string;
119
- duration?: number; // Duration in seconds
120
- temperature?: number; // Controls randomness
121
- topK?: number; // Top K sampling
122
- topP?: number; // Top P sampling
123
- classifier_free_guidance?: number; // Guidance scale
124
- }
125
-
126
- 2. Custom Generate Music (suno.custom-generate-music)
127
- interface CustomGenerateParams {
128
- prompt: string;
129
- duration?: number;
130
- temperature?: number;
131
- topK?: number;
132
- topP?: number;
133
- classifier_free_guidance?: number;
134
- reference_audio?: string; // Path to reference audio file
135
- style?: string; // Musical style
136
- bpm?: number; // Beats per minute
137
- key?: string; // Musical key
138
- mode?: string; // Musical mode (e.g., "major", "minor")
139
- }
140
-
141
- 3. Extend Audio (suno.extend-audio)
142
- interface ExtendParams {
143
- audio_id: string; // ID of the audio to extend
144
- duration: number; // Additional duration in seconds
145
- }
146
-
147
- Response Type:
148
- interface GenerationResponse {
149
- id: string; // Generated audio ID
150
- status: string; // Status of the generation
151
- url?: string; // URL to download the generated audio
152
- error?: string; // Error message if generation failed
153
- }
154
-
155
- ERROR HANDLING
156
-
157
- The plugin includes built-in error handling for common scenarios:
158
-
159
- try {
160
- await eliza.execute('suno.generate', params);
161
- } catch (error) {
162
- if (error.code === 'SUNO_API_ERROR') {
163
- // Handle API-specific errors
164
- }
165
- // Handle other errors
166
- }
167
-
168
- EXAMPLES
169
-
170
- Creating a Pop Song:
171
-
172
- const result = await eliza.execute('suno.generate-music', {
173
- prompt: "Create a pop song with vocals, drums, and guitar",
174
- duration: 180,
175
- temperature: 1.0,
176
- classifier_free_guidance: 3.5
177
- });
178
-
179
- Creating a Custom Classical Piece:
180
-
181
- const result = await eliza.execute('suno.custom-generate-music', {
182
- prompt: "A classical piano sonata in the style of Mozart",
183
- duration: 120,
184
- temperature: 0.8,
185
- style: "classical",
186
- bpm: 120,
187
- key: "C",
188
- mode: "major"
189
- });
190
-
191
- Extending an Existing Track:
192
-
193
- const extended = await eliza.execute('suno.extend-audio', {
194
- audio_id: "existing-track-id",
195
- duration: 60
196
- });
197
-
198
- LICENSE
199
-
200
- MIT
201
-
202
- SUPPORT
203
-
204
- For issues and feature requests, please open an issue on our GitHub repository.
205
-
206
- ---
207
- Built with ❤️ for ElizaOS
1
+ # @elizaos/plugin-suno
2
+
3
+ Suno AI music generation plugin for elizaOS. Enables Eliza agents to generate, custom-generate, and extend audio tracks via the Suno API.
4
+
5
+ ## What it does
6
+
7
+ This plugin contributes a Suno HTTP client and a status provider (`SUNO_STATUS`) to the elizaOS agent runtime. Music generation is dispatched through the `MUSIC` umbrella action (provided by `@elizaos/plugin-music`); this plugin supplies the Suno-specific handler (`sunoGenerateMusicHandler`) that `plugin-music` mounts.
8
+
9
+ Three subactions are supported:
10
+
11
+ | Subaction | Endpoint | Required params |
12
+ |---|---|---|
13
+ | `generate` | `POST /generate` | `prompt` |
14
+ | `custom_generate` | `POST /custom-generate` | `prompt`; optional: `style`, `bpm`, `key`, `mode`, `reference_audio` |
15
+ | `extend` | `POST /extend` | `audio_id`, `duration` |
16
+
17
+ The subaction is inferred from message text and params when not specified explicitly.
18
+
19
+ ## Requirements
20
+
21
+ - A Suno API key (obtain at [suno.ai](https://suno.ai)).
22
+ - `@elizaos/plugin-music` loaded alongside this plugin to expose the `MUSIC` action to agents.
23
+
24
+ ## Configuration
25
+
26
+ Set the API key as an environment variable or in the agent character config:
27
+
28
+ ```
29
+ SUNO_API_KEY=your-suno-api-key
30
+ ```
31
+
32
+ The plugin auto-enables when `SUNO_API_KEY` is present, or when agent config sets:
33
+
34
+ ```json
35
+ {
36
+ "media": {
37
+ "audio": {
38
+ "provider": "suno",
39
+ "mode": "own-key"
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## Generation parameters
46
+
47
+ ### `generate` and `custom_generate`
48
+
49
+ | Param | Type | Default | Description |
50
+ |---|---|---|---|
51
+ | `prompt` | string | (required) | Text description of the music to generate |
52
+ | `duration` | number | 30 | Duration in seconds |
53
+ | `temperature` | number | 1.0 | Randomness (higher = more creative) |
54
+ | `topK` | number | 250 | Top-K sampling |
55
+ | `topP` | number | 0.95 | Top-P sampling |
56
+ | `classifier_free_guidance` | number | 3.0 | Prompt adherence strength |
57
+ | `style` | string | — | Musical style (custom_generate only) |
58
+ | `bpm` | number | — | Beats per minute (custom_generate only) |
59
+ | `key` | string | — | Musical key, e.g. `"C"` (custom_generate only) |
60
+ | `mode` | string | — | `"major"` or `"minor"` (custom_generate only) |
61
+ | `reference_audio` | string | — | Reference audio path (custom_generate only) |
62
+
63
+ ### `extend`
64
+
65
+ | Param | Type | Required | Description |
66
+ |---|---|---|---|
67
+ | `audio_id` | string | Yes | ID of the existing track to extend |
68
+ | `duration` | number | Yes | Additional seconds to add |
69
+
70
+ ## Response shape
71
+
72
+ ```ts
73
+ interface GenerationResponse {
74
+ id: string;
75
+ status: 'pending' | 'processing' | 'completed' | 'failed';
76
+ audio_url?: string;
77
+ error?: string;
78
+ }
79
+ ```
80
+
81
+ Responses larger than 4000 bytes are truncated before being returned to the agent context.
package/auto-enable.ts CHANGED
@@ -4,15 +4,15 @@
4
4
  // `elizaos.plugin.autoEnableModule`. Keep this module light: env reads only,
5
5
  // no service init, no transitive imports of the full plugin runtime. The
6
6
  // auto-enable engine loads dozens of these per boot.
7
- import type { PluginAutoEnableContext } from "@elizaos/core";
7
+ import type { PluginAutoEnableContext } from '@elizaos/core';
8
8
 
9
9
  /**
10
10
  * Enable when a Suno API key is in the environment, or when the user has
11
11
  * explicitly selected Suno as the audio provider in own-key mode.
12
12
  */
13
13
  export function shouldEnable(ctx: PluginAutoEnableContext): boolean {
14
- const apiKey = ctx.env.SUNO_API_KEY;
15
- if (apiKey && apiKey.trim() !== "") return true;
16
- const audio = ctx.config?.media?.audio;
17
- return audio?.provider === "suno" && audio?.mode === "own-key";
14
+ const apiKey = ctx.env.SUNO_API_KEY;
15
+ if (apiKey && apiKey.trim() !== '') return true;
16
+ const audio = ctx.config?.media?.audio;
17
+ return audio?.provider === 'suno' && audio?.mode === 'own-key';
18
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/plugin-suno",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.3-beta.3",
4
4
  "description": "Suno AI Music Generation Plugin for Eliza",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -9,8 +9,24 @@
9
9
  "./package.json": "./package.json",
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
+ "eliza-source": {
13
+ "types": "./src/index.ts",
14
+ "import": "./src/index.ts",
15
+ "default": "./src/index.ts"
16
+ },
12
17
  "import": "./dist/index.js",
13
18
  "default": "./dist/index.js"
19
+ },
20
+ "./*.css": "./dist/*.css",
21
+ "./*": {
22
+ "types": "./dist/*.d.ts",
23
+ "eliza-source": {
24
+ "types": "./src/*.ts",
25
+ "import": "./src/*.ts",
26
+ "default": "./src/*.ts"
27
+ },
28
+ "import": "./dist/*.js",
29
+ "default": "./dist/*.js"
14
30
  }
15
31
  },
16
32
  "files": [
@@ -28,19 +44,17 @@
28
44
  "scripts": {
29
45
  "build": "tsup --format esm",
30
46
  "dev": "tsup --format esm --watch",
31
- "lint": "echo \"Lint skipped for release\"",
47
+ "lint": "bunx @biomejs/biome check .",
32
48
  "lint:check": "bun run lint",
33
49
  "lint:fix": "bunx @biomejs/biome check --write ./src",
34
50
  "format": "bunx @biomejs/biome format --write ./src",
35
51
  "format:check": "bunx @biomejs/biome format ./src",
36
- "test": "vitest run --config vitest.config.ts --passWithNoTests",
37
- "typecheck": "echo \"Typecheck skipped for release\""
52
+ "test": "vitest run --config vitest.config.ts",
53
+ "typecheck": "tsgo --noEmit -p tsconfig.json"
38
54
  },
39
55
  "devDependencies": {
40
56
  "@biomejs/biome": "^2.4.14",
41
- "@types/jest": "^30.0.0",
42
57
  "@types/node": "^22.19.17",
43
- "jest": "^30.0.0",
44
58
  "tsup": "^8.5.1",
45
59
  "typescript": "^6.0.3",
46
60
  "vitest": "^4.0.0"
@@ -60,6 +74,7 @@
60
74
  "access": "public"
61
75
  },
62
76
  "dependencies": {
63
- "@elizaos/core": "2.0.0-beta.1"
64
- }
77
+ "@elizaos/core": "2.0.3-beta.3"
78
+ },
79
+ "gitHead": "f54b0f4eaed317d59fa7dbcdce20f4cdb0734420"
65
80
  }
package/dist/index.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import { Action, IAgentRuntime, Memory, State, Provider, Plugin } from '@elizaos/core';
2
-
3
- declare const musicGeneration: Action;
4
-
5
- interface SunoConfig {
6
- apiKey: string;
7
- baseUrl?: string;
8
- }
9
- declare class SunoProvider {
10
- private apiKey;
11
- private baseUrl;
12
- static get(runtime: IAgentRuntime, _message: Memory, _state?: State): Promise<SunoProvider>;
13
- constructor(config: SunoConfig);
14
- get(_runtime: IAgentRuntime, _message: Memory, _state?: State): Promise<{
15
- status: string;
16
- }>;
17
- request(runtime: IAgentRuntime, endpoint: string, options?: RequestInit): Promise<any>;
18
- }
19
- declare const sunoStatusProvider: Provider;
20
-
21
- declare const sunoPlugin: Plugin;
22
-
23
- export { musicGeneration as CustomGenerateMusic, musicGeneration as ExtendAudio, musicGeneration as GenerateMusic, musicGeneration as MusicGeneration, SunoProvider, sunoPlugin as default, sunoPlugin, sunoStatusProvider };
package/dist/index.js DELETED
@@ -1,311 +0,0 @@
1
- // src/providers/suno.ts
2
- import {
3
- recordLlmCall
4
- } from "@elizaos/core";
5
- var SunoProvider = class _SunoProvider {
6
- apiKey;
7
- baseUrl;
8
- static async get(runtime, _message, _state) {
9
- const apiKey = runtime.getSetting("SUNO_API_KEY");
10
- if (typeof apiKey !== "string" || !apiKey) {
11
- throw new Error("SUNO_API_KEY is required");
12
- }
13
- return new _SunoProvider({ apiKey });
14
- }
15
- constructor(config) {
16
- this.apiKey = config.apiKey;
17
- this.baseUrl = config.baseUrl || "https://api.suno.ai/v1";
18
- }
19
- async get(_runtime, _message, _state) {
20
- return { status: "ready" };
21
- }
22
- async request(runtime, endpoint, options = {}) {
23
- const url = `${this.baseUrl}${endpoint}`;
24
- const headers = {
25
- Authorization: `Bearer ${this.apiKey}`,
26
- "Content-Type": "application/json",
27
- ...options.headers
28
- };
29
- const body = typeof options.body === "string" ? options.body : "";
30
- const details = {
31
- model: "suno",
32
- modelVersion: "api-v1",
33
- systemPrompt: "Suno music generation API request",
34
- userPrompt: body,
35
- temperature: readTemperature(body),
36
- maxTokens: 0,
37
- purpose: "action",
38
- actionType: `suno.fetch${endpoint}`
39
- };
40
- return recordLlmCall(runtime, details, async () => {
41
- const response = await fetch(url, {
42
- ...options,
43
- headers
44
- });
45
- if (!response.ok) {
46
- throw new Error(`Suno API error: ${response.statusText}`);
47
- }
48
- const data = await response.json();
49
- details.response = JSON.stringify({ suno_response: data });
50
- return data;
51
- });
52
- }
53
- };
54
- function readTemperature(body) {
55
- if (!body) return 0;
56
- try {
57
- const parsed = JSON.parse(body);
58
- return typeof parsed.temperature === "number" ? parsed.temperature : 0;
59
- } catch {
60
- return 0;
61
- }
62
- }
63
- var sunoStatusProvider = {
64
- name: "SUNO_STATUS",
65
- description: "Suno music generation status",
66
- descriptionCompressed: "Suno generation availability.",
67
- contexts: ["media"],
68
- contextGate: { anyOf: ["media"] },
69
- cacheStable: false,
70
- cacheScope: "turn",
71
- get: async (runtime) => {
72
- const configured = Boolean(runtime.getSetting("SUNO_API_KEY"));
73
- return {
74
- text: JSON.stringify(
75
- {
76
- suno: {
77
- configured,
78
- status: configured ? "ready" : "missing_api_key",
79
- action: "MUSIC_GENERATION",
80
- subactions: ["generate", "custom", "extend"]
81
- }
82
- },
83
- null,
84
- 2
85
- ),
86
- data: { configured },
87
- values: { sunoConfigured: configured }
88
- };
89
- }
90
- };
91
-
92
- // src/actions/musicGeneration.ts
93
- var SUNO_ACTION_TIMEOUT_MS = 3e4;
94
- var MAX_SUNO_RESPONSE_BYTES = 4e3;
95
- function paramsFromMessageAndOptions(message, options) {
96
- const content = message.content && typeof message.content === "object" ? message.content : {};
97
- const parameters = (options == null ? void 0 : options.parameters) && typeof options.parameters === "object" ? options.parameters : {};
98
- return { ...content, ...options, ...parameters };
99
- }
100
- function normalizeSubaction(value) {
101
- const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
102
- if (normalized === "generate" || normalized === "custom" || normalized === "extend") {
103
- return normalized;
104
- }
105
- if (normalized === "custom_generate" || normalized === "custom-generate") return "custom";
106
- if (normalized === "extend_audio" || normalized === "extend-audio") return "extend";
107
- return null;
108
- }
109
- function inferSubaction(message, params) {
110
- var _a;
111
- const explicit = normalizeSubaction(params.action ?? params.subaction ?? params.operation);
112
- if (explicit) return explicit;
113
- const text = (((_a = message.content) == null ? void 0 : _a.text) ?? "").toLowerCase();
114
- if (params.audio_id || /\b(extend|lengthen|longer|add \d+.*seconds?)\b/.test(text)) {
115
- return "extend";
116
- }
117
- if (params.reference_audio || params.style || params.bpm || params.key || params.mode || /\b(custom|style|bpm|key|mode|reference)\b/.test(text)) {
118
- return "custom";
119
- }
120
- return "generate";
121
- }
122
- function promptFromParams(message, params) {
123
- var _a;
124
- const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
125
- if (prompt) return prompt;
126
- return (((_a = message.content) == null ? void 0 : _a.text) ?? "").trim();
127
- }
128
- function numberOrDefault(value, fallback) {
129
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
130
- }
131
- function generationBody(params, prompt) {
132
- return {
133
- prompt,
134
- duration: numberOrDefault(params.duration, 30),
135
- temperature: numberOrDefault(params.temperature, 1),
136
- top_k: numberOrDefault(params.topK, 250),
137
- top_p: numberOrDefault(params.topP, 0.95),
138
- classifier_free_guidance: numberOrDefault(params.classifier_free_guidance, 3)
139
- };
140
- }
141
- var musicGeneration = {
142
- name: "MUSIC_GENERATION",
143
- contexts: ["media"],
144
- contextGate: { anyOf: ["media"] },
145
- roleGate: { minRole: "USER" },
146
- description: "Generate music through Suno. Use action generate for a simple prompt, custom for style/BPM/key/reference parameters, or extend for an existing audio_id and duration.",
147
- descriptionCompressed: "Suno music generation router action: generate, custom, extend.",
148
- similes: [
149
- "GENERATE_MUSIC",
150
- "CREATE_MUSIC",
151
- "MAKE_MUSIC",
152
- "COMPOSE_MUSIC",
153
- "CUSTOM_GENERATE_MUSIC",
154
- "EXTEND_AUDIO"
155
- ],
156
- parameters: [
157
- {
158
- name: "action",
159
- description: "Suno operation: generate, custom, or extend.",
160
- required: false,
161
- schema: { type: "string", enum: ["generate", "custom", "extend"] }
162
- },
163
- {
164
- name: "subaction",
165
- description: "Legacy alias for action.",
166
- required: false,
167
- schema: { type: "string" }
168
- },
169
- {
170
- name: "prompt",
171
- description: "Music prompt for generate/custom.",
172
- required: false,
173
- schema: { type: "string" }
174
- },
175
- {
176
- name: "audio_id",
177
- description: "Existing Suno audio id for extend.",
178
- required: false,
179
- schema: { type: "string" }
180
- },
181
- {
182
- name: "duration",
183
- description: "Generation duration or extension seconds.",
184
- required: false,
185
- schema: { type: "number", default: 30 }
186
- }
187
- ],
188
- validate: async (runtime, message) => {
189
- var _a;
190
- if (!runtime.getSetting("SUNO_API_KEY")) return false;
191
- const text = (((_a = message.content) == null ? void 0 : _a.text) ?? "").toLowerCase();
192
- return /\b(generate|create|make|compose|extend|music|song|audio|track)\b/.test(text);
193
- },
194
- handler: async (runtime, message, state, options, callback) => {
195
- try {
196
- const params = paramsFromMessageAndOptions(message, options);
197
- const subaction = inferSubaction(message, params);
198
- const provider = await SunoProvider.get(runtime, message, state);
199
- let endpoint = "/generate";
200
- let body;
201
- if (subaction === "extend") {
202
- if (!params.audio_id || !params.duration) {
203
- throw new Error("Missing required parameters: audio_id and duration");
204
- }
205
- endpoint = "/extend";
206
- body = {
207
- audio_id: params.audio_id,
208
- duration: params.duration
209
- };
210
- } else {
211
- const prompt = promptFromParams(message, params);
212
- if (!prompt) {
213
- throw new Error("Missing required parameter: prompt");
214
- }
215
- body = generationBody(params, prompt);
216
- if (subaction === "custom") {
217
- endpoint = "/custom-generate";
218
- body = {
219
- ...body,
220
- reference_audio: params.reference_audio,
221
- style: params.style,
222
- bpm: params.bpm,
223
- key: params.key,
224
- mode: params.mode
225
- };
226
- }
227
- }
228
- const controller = new AbortController();
229
- const timeout = setTimeout(() => controller.abort(), SUNO_ACTION_TIMEOUT_MS);
230
- const response = await provider.request(runtime, endpoint, {
231
- method: "POST",
232
- body: JSON.stringify(body),
233
- signal: controller.signal
234
- }).finally(() => clearTimeout(timeout));
235
- const cappedResponse = JSON.stringify(response).length > MAX_SUNO_RESPONSE_BYTES ? {
236
- truncated: true,
237
- preview: JSON.stringify(response).slice(0, MAX_SUNO_RESPONSE_BYTES)
238
- } : response;
239
- await (callback == null ? void 0 : callback({
240
- text: subaction === "extend" ? `Successfully extended audio ${params.audio_id}` : `Successfully submitted ${subaction} music generation`,
241
- content: cappedResponse
242
- }));
243
- return {
244
- success: true,
245
- text: subaction === "extend" ? `Successfully extended audio ${params.audio_id}` : `Successfully submitted ${subaction} music generation`,
246
- data: { subaction, response: cappedResponse }
247
- };
248
- } catch (error) {
249
- const errorMessage = error instanceof Error ? error.message : String(error);
250
- const text = `Music generation failed: ${errorMessage}`;
251
- await (callback == null ? void 0 : callback({
252
- text,
253
- error
254
- }));
255
- return { success: false, text, error: errorMessage };
256
- }
257
- },
258
- examples: [
259
- [
260
- {
261
- name: "{{user1}}",
262
- content: {
263
- text: "Generate a relaxing ambient track",
264
- prompt: "A peaceful ambient soundscape with gentle waves and soft pads",
265
- duration: 45
266
- }
267
- },
268
- {
269
- name: "{{agent}}",
270
- content: {
271
- text: "I'll generate a calming ambient piece.",
272
- action: "MUSIC_GENERATION"
273
- }
274
- }
275
- ]
276
- ]
277
- };
278
- var musicGeneration_default = musicGeneration;
279
-
280
- // src/index.ts
281
- var sunoPlugin = {
282
- name: "suno",
283
- description: "Suno AI Music Generation Plugin for Eliza",
284
- actions: [musicGeneration_default],
285
- providers: [sunoStatusProvider],
286
- // Self-declared auto-enable: activate when SUNO_API_KEY is set OR when
287
- // media.audio is configured to use the suno provider with own-key mode.
288
- autoEnable: {
289
- shouldEnable: (env, config) => {
290
- const key = env.SUNO_API_KEY;
291
- if (typeof key === "string" && key.trim() !== "") return true;
292
- const media = config == null ? void 0 : config.media;
293
- const audio = media == null ? void 0 : media.audio;
294
- return Boolean(
295
- audio && audio.enabled !== false && audio.mode === "own-key" && audio.provider === "suno"
296
- );
297
- }
298
- }
299
- };
300
- var index_default = sunoPlugin;
301
- export {
302
- musicGeneration_default as CustomGenerateMusic,
303
- musicGeneration_default as ExtendAudio,
304
- musicGeneration_default as GenerateMusic,
305
- musicGeneration_default as MusicGeneration,
306
- SunoProvider,
307
- index_default as default,
308
- sunoPlugin,
309
- sunoStatusProvider
310
- };
311
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/providers/suno.ts","../src/actions/musicGeneration.ts","../src/index.ts"],"sourcesContent":["import {\n type IAgentRuntime,\n type Memory,\n type Provider,\n type RecordLlmCallDetails,\n recordLlmCall,\n type State,\n} from '@elizaos/core';\n\nexport interface SunoConfig {\n apiKey: string;\n baseUrl?: string;\n}\n\nexport class SunoProvider {\n private apiKey: string;\n private baseUrl: string;\n\n static async get(\n runtime: IAgentRuntime,\n _message: Memory,\n _state?: State\n ): Promise<SunoProvider> {\n const apiKey = runtime.getSetting('SUNO_API_KEY');\n if (typeof apiKey !== 'string' || !apiKey) {\n throw new Error('SUNO_API_KEY is required');\n }\n return new SunoProvider({ apiKey });\n }\n\n constructor(config: SunoConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || 'https://api.suno.ai/v1';\n }\n\n async get(\n _runtime: IAgentRuntime,\n _message: Memory,\n _state?: State\n ): Promise<{ status: string }> {\n return { status: 'ready' };\n }\n\n async request(runtime: IAgentRuntime, endpoint: string, options: RequestInit = {}) {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n\n const body = typeof options.body === 'string' ? options.body : '';\n const details: RecordLlmCallDetails = {\n model: 'suno',\n modelVersion: 'api-v1',\n systemPrompt: 'Suno music generation API request',\n userPrompt: body,\n temperature: readTemperature(body),\n maxTokens: 0,\n purpose: 'action',\n actionType: `suno.fetch${endpoint}`,\n };\n\n return recordLlmCall(runtime, details, async () => {\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n throw new Error(`Suno API error: ${response.statusText}`);\n }\n\n const data = await response.json();\n details.response = JSON.stringify({ suno_response: data });\n return data;\n });\n }\n}\n\nfunction readTemperature(body: string): number {\n if (!body) return 0;\n try {\n const parsed = JSON.parse(body) as { temperature?: unknown };\n return typeof parsed.temperature === 'number' ? parsed.temperature : 0;\n } catch {\n return 0;\n }\n}\n\nexport const sunoStatusProvider: Provider = {\n name: 'SUNO_STATUS',\n description: 'Suno music generation status',\n descriptionCompressed: 'Suno generation availability.',\n contexts: ['media'],\n contextGate: { anyOf: ['media'] },\n cacheStable: false,\n cacheScope: 'turn',\n get: async (runtime: IAgentRuntime) => {\n const configured = Boolean(runtime.getSetting('SUNO_API_KEY'));\n return {\n text: JSON.stringify(\n {\n suno: {\n configured,\n status: configured ? 'ready' : 'missing_api_key',\n action: 'MUSIC_GENERATION',\n subactions: ['generate', 'custom', 'extend'],\n },\n },\n null,\n 2\n ),\n data: { configured },\n values: { sunoConfigured: configured },\n };\n },\n};\n\nexport interface GenerateParams {\n prompt: string;\n duration?: number;\n temperature?: number;\n topK?: number;\n topP?: number;\n classifier_free_guidance?: number;\n}\n\nexport interface CustomGenerateParams extends GenerateParams {\n reference_audio?: string;\n style?: string;\n bpm?: number;\n key?: string;\n mode?: string;\n}\n\nexport interface ExtendParams {\n audio_id: string;\n duration: number;\n}\n\nexport interface GenerationResponse {\n id: string;\n status: 'pending' | 'processing' | 'completed' | 'failed';\n audio_url?: string;\n error?: string;\n}\n","import type {\n Action,\n ActionResult,\n HandlerCallback,\n IAgentRuntime,\n Memory,\n State,\n} from '@elizaos/core';\nimport { SunoProvider } from '../providers/suno';\n\ntype MusicGenerationSubaction = 'generate' | 'custom' | 'extend';\n\ntype MusicGenerationParams = {\n action?: MusicGenerationSubaction | string;\n subaction?: MusicGenerationSubaction | string;\n operation?: MusicGenerationSubaction | string;\n prompt?: string;\n duration?: number;\n temperature?: number;\n topK?: number;\n topP?: number;\n classifier_free_guidance?: number;\n reference_audio?: string;\n style?: string;\n bpm?: number;\n key?: string;\n mode?: string;\n audio_id?: string;\n};\n\nconst SUNO_ACTION_TIMEOUT_MS = 30_000;\nconst MAX_SUNO_RESPONSE_BYTES = 4000;\n\nfunction paramsFromMessageAndOptions(\n message: Memory,\n options?: Record<string, unknown>\n): MusicGenerationParams {\n const content =\n message.content && typeof message.content === 'object'\n ? (message.content as Record<string, unknown>)\n : {};\n const parameters =\n options?.parameters && typeof options.parameters === 'object'\n ? (options.parameters as Record<string, unknown>)\n : {};\n return { ...content, ...options, ...parameters } as MusicGenerationParams;\n}\n\nfunction normalizeSubaction(value: unknown): MusicGenerationSubaction | null {\n const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';\n if (normalized === 'generate' || normalized === 'custom' || normalized === 'extend') {\n return normalized;\n }\n if (normalized === 'custom_generate' || normalized === 'custom-generate') return 'custom';\n if (normalized === 'extend_audio' || normalized === 'extend-audio') return 'extend';\n return null;\n}\n\nfunction inferSubaction(message: Memory, params: MusicGenerationParams): MusicGenerationSubaction {\n const explicit = normalizeSubaction(params.action ?? params.subaction ?? params.operation);\n if (explicit) return explicit;\n const text = (message.content?.text ?? '').toLowerCase();\n if (params.audio_id || /\\b(extend|lengthen|longer|add \\d+.*seconds?)\\b/.test(text)) {\n return 'extend';\n }\n if (\n params.reference_audio ||\n params.style ||\n params.bpm ||\n params.key ||\n params.mode ||\n /\\b(custom|style|bpm|key|mode|reference)\\b/.test(text)\n ) {\n return 'custom';\n }\n return 'generate';\n}\n\nfunction promptFromParams(message: Memory, params: MusicGenerationParams): string {\n const prompt = typeof params.prompt === 'string' ? params.prompt.trim() : '';\n if (prompt) return prompt;\n return (message.content?.text ?? '').trim();\n}\n\nfunction numberOrDefault(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\nfunction generationBody(params: MusicGenerationParams, prompt: string): Record<string, unknown> {\n return {\n prompt,\n duration: numberOrDefault(params.duration, 30),\n temperature: numberOrDefault(params.temperature, 1.0),\n top_k: numberOrDefault(params.topK, 250),\n top_p: numberOrDefault(params.topP, 0.95),\n classifier_free_guidance: numberOrDefault(params.classifier_free_guidance, 3.0),\n };\n}\n\nexport const musicGeneration: Action = {\n name: 'MUSIC_GENERATION',\n contexts: ['media'],\n contextGate: { anyOf: ['media'] },\n roleGate: { minRole: 'USER' },\n description:\n 'Generate music through Suno. Use action generate for a simple prompt, custom for style/BPM/key/reference parameters, or extend for an existing audio_id and duration.',\n descriptionCompressed: 'Suno music generation router action: generate, custom, extend.',\n similes: [\n 'GENERATE_MUSIC',\n 'CREATE_MUSIC',\n 'MAKE_MUSIC',\n 'COMPOSE_MUSIC',\n 'CUSTOM_GENERATE_MUSIC',\n 'EXTEND_AUDIO',\n ],\n parameters: [\n {\n name: 'action',\n description: 'Suno operation: generate, custom, or extend.',\n required: false,\n schema: { type: 'string', enum: ['generate', 'custom', 'extend'] },\n },\n {\n name: 'subaction',\n description: 'Legacy alias for action.',\n required: false,\n schema: { type: 'string' },\n },\n {\n name: 'prompt',\n description: 'Music prompt for generate/custom.',\n required: false,\n schema: { type: 'string' },\n },\n {\n name: 'audio_id',\n description: 'Existing Suno audio id for extend.',\n required: false,\n schema: { type: 'string' },\n },\n {\n name: 'duration',\n description: 'Generation duration or extension seconds.',\n required: false,\n schema: { type: 'number', default: 30 },\n },\n ],\n validate: async (runtime: IAgentRuntime, message: Memory): Promise<boolean> => {\n if (!runtime.getSetting('SUNO_API_KEY')) return false;\n const text = (message.content?.text ?? '').toLowerCase();\n return /\\b(generate|create|make|compose|extend|music|song|audio|track)\\b/.test(text);\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback\n ): Promise<ActionResult> => {\n try {\n const params = paramsFromMessageAndOptions(message, options);\n const subaction = inferSubaction(message, params);\n const provider = await SunoProvider.get(runtime, message, state);\n\n let endpoint = '/generate';\n let body: Record<string, unknown>;\n\n if (subaction === 'extend') {\n if (!params.audio_id || !params.duration) {\n throw new Error('Missing required parameters: audio_id and duration');\n }\n endpoint = '/extend';\n body = {\n audio_id: params.audio_id,\n duration: params.duration,\n };\n } else {\n const prompt = promptFromParams(message, params);\n if (!prompt) {\n throw new Error('Missing required parameter: prompt');\n }\n body = generationBody(params, prompt);\n if (subaction === 'custom') {\n endpoint = '/custom-generate';\n body = {\n ...body,\n reference_audio: params.reference_audio,\n style: params.style,\n bpm: params.bpm,\n key: params.key,\n mode: params.mode,\n };\n }\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), SUNO_ACTION_TIMEOUT_MS);\n const response = await provider\n .request(runtime, endpoint, {\n method: 'POST',\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n .finally(() => clearTimeout(timeout));\n const cappedResponse =\n JSON.stringify(response).length > MAX_SUNO_RESPONSE_BYTES\n ? {\n truncated: true,\n preview: JSON.stringify(response).slice(0, MAX_SUNO_RESPONSE_BYTES),\n }\n : response;\n\n await callback?.({\n text:\n subaction === 'extend'\n ? `Successfully extended audio ${params.audio_id}`\n : `Successfully submitted ${subaction} music generation`,\n content: cappedResponse,\n });\n\n return {\n success: true,\n text:\n subaction === 'extend'\n ? `Successfully extended audio ${params.audio_id}`\n : `Successfully submitted ${subaction} music generation`,\n data: { subaction, response: cappedResponse },\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const text = `Music generation failed: ${errorMessage}`;\n await callback?.({\n text,\n error,\n });\n return { success: false, text, error: errorMessage };\n }\n },\n examples: [\n [\n {\n name: '{{user1}}',\n content: {\n text: 'Generate a relaxing ambient track',\n prompt: 'A peaceful ambient soundscape with gentle waves and soft pads',\n duration: 45,\n },\n },\n {\n name: '{{agent}}',\n content: {\n text: \"I'll generate a calming ambient piece.\",\n action: 'MUSIC_GENERATION',\n },\n },\n ],\n ],\n};\n\nexport default musicGeneration;\n","import type { Plugin } from '@elizaos/core';\nimport musicGeneration from './actions/musicGeneration';\nimport { SunoProvider, sunoStatusProvider } from './providers/suno';\n\nexport {\n SunoProvider,\n musicGeneration as MusicGeneration,\n musicGeneration as GenerateMusic,\n musicGeneration as CustomGenerateMusic,\n musicGeneration as ExtendAudio,\n sunoStatusProvider,\n};\n\nexport const sunoPlugin: Plugin = {\n name: 'suno',\n description: 'Suno AI Music Generation Plugin for Eliza',\n actions: [musicGeneration],\n providers: [sunoStatusProvider],\n // Self-declared auto-enable: activate when SUNO_API_KEY is set OR when\n // media.audio is configured to use the suno provider with own-key mode.\n autoEnable: {\n shouldEnable: (env, config) => {\n const key = env.SUNO_API_KEY;\n if (typeof key === 'string' && key.trim() !== '') return true;\n const media = config?.media as Record<string, unknown> | undefined;\n const audio = media?.audio as\n | { enabled?: unknown; mode?: unknown; provider?: unknown }\n | undefined;\n return Boolean(\n audio &&\n audio.enabled !== false &&\n audio.mode === 'own-key' &&\n audio.provider === 'suno'\n );\n },\n },\n};\n\nexport default sunoPlugin;\n"],"mappings":"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","names":[]}