Skip to content

Commit 78df869

Browse files
Freyteswtfsayo
andauthored
feat(new-plugin): suno Eliza plugin (elizaOS#2656)
* Plugin Suno Plugin Suno Initial Commit * add initialization * fix imports --------- Co-authored-by: Sayo <hi@sayo.wtf>
1 parent 5347b1f commit 78df869

14 files changed

+411
-1
lines changed

.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -777,3 +777,6 @@ EMAIL_INCOMING_HOST=imap.example.com
777777
EMAIL_INCOMING_PORT=993 # Default port for secure IMAP
778778
EMAIL_INCOMING_USER=
779779
EMAIL_INCOMING_PASS=
780+
781+
# Suno AI Music Generation
782+
SUNO_API_KEY=

agent/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
"@elizaos/plugin-0x": "workspace:*",
117117
"@elizaos/plugin-dkg": "workspace:*",
118118
"@elizaos/plugin-email": "workspace:*",
119+
"@elizaos/plugin-suno": "workspace:*",
119120
"readline": "1.3.0",
120121
"ws": "8.18.0",
121122
"yargs": "17.7.2"

agent/src/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ import path from "path";
127127
import { fileURLToPath } from "url";
128128
import yargs from "yargs";
129129
import { emailPlugin } from "@elizaos/plugin-email";
130+
import { sunoPlugin } from "@elizaos/plugin-suno";
130131

131132
const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
132133
const __dirname = path.dirname(__filename); // get the name of the directory
@@ -1127,7 +1128,8 @@ export async function createAgent(
11271128
: null,
11281129
getSecret(character, "EMAIL_INCOMING_USER") && getSecret(character, "EMAIL_INCOMING_PASS") ||
11291130
getSecret(character, "EMAIL_OUTGOING_USER") && getSecret(character, "EMAIL_OUTGOING_PASS") ?
1130-
emailPlugin : null
1131+
emailPlugin : null,
1132+
getSecret(character, "SUNO_API_KEY") ? sunoPlugin : null
11311133
].filter(Boolean),
11321134
providers: [],
11331135
actions: [],

packages/plugin-suno/README.md

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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
9+
- Create custom music with fine-tuned parameters
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/eliza';
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
39+
Generate music using predefined settings and a text prompt:
40+
41+
await eliza.execute('suno.generate', {
42+
prompt: "An upbeat electronic dance track with energetic beats"
43+
});
44+
45+
2. Custom Music Generation
46+
Create music with detailed control over generation parameters:
47+
48+
await eliza.execute('suno.customGenerate', {
49+
prompt: "A melodic piano piece with soft strings",
50+
temperature: 0.8,
51+
duration: 30
52+
});
53+
54+
3. Extend Audio
55+
Extend existing audio tracks to create longer compositions:
56+
57+
await eliza.execute('suno.extend', {
58+
audioFile: "path/to/audio.mp3",
59+
duration: 60
60+
});
61+
62+
API REFERENCE
63+
64+
SunoProvider Configuration
65+
The Suno provider accepts the following configuration options:
66+
67+
interface SunoConfig {
68+
apiKey: string;
69+
}
70+
71+
Action Parameters:
72+
73+
1. Generate Music
74+
interface GenerateMusicParams {
75+
prompt: string;
76+
}
77+
78+
2. Custom Generate Music
79+
interface CustomGenerateMusicParams {
80+
prompt: string;
81+
temperature?: number;
82+
duration?: number;
83+
}
84+
85+
3. Extend Audio
86+
interface ExtendAudioParams {
87+
audioFile: string;
88+
duration: number;
89+
}
90+
91+
ERROR HANDLING
92+
93+
The plugin includes built-in error handling for common scenarios:
94+
95+
try {
96+
await eliza.execute('suno.generate', params);
97+
} catch (error) {
98+
if (error.code === 'SUNO_API_ERROR') {
99+
// Handle API-specific errors
100+
}
101+
// Handle other errors
102+
}
103+
104+
EXAMPLES
105+
106+
Creating a Complete Song:
107+
108+
const result = await eliza.execute('suno.generate', {
109+
prompt: "Create a pop song with vocals, drums, and guitar",
110+
duration: 180 // 3 minutes
111+
});
112+
113+
console.log(`Generated music file: ${result.outputPath}`);
114+
115+
Extending an Existing Track:
116+
117+
const extended = await eliza.execute('suno.extend', {
118+
audioFile: "original-track.mp3",
119+
duration: 60 // Add 60 seconds
120+
});
121+
122+
console.log(`Extended audio file: ${extended.outputPath}`);
123+
124+
125+
LICENSE
126+
127+
MIT
128+
129+
SUPPORT
130+
131+
For issues and feature requests, please open an issue on our GitHub repository.
132+
133+
---
134+
Built with ❤️ for ElizaOS
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import eslintGlobalConfig from "../../eslint.config.mjs";
2+
3+
export default [...eslintGlobalConfig];

packages/plugin-suno/package.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "@elizaos/plugin-suno",
3+
"version": "0.1.9-alpha.1",
4+
"description": "Suno AI Music Generation Plugin for Eliza",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"scripts": {
8+
"build": "tsc",
9+
"test": "jest"
10+
},
11+
"dependencies": {
12+
"@elizaos/core": "^0.1.0"
13+
},
14+
"devDependencies": {
15+
"typescript": "^4.9.0",
16+
"@types/node": "^16.0.0",
17+
"jest": "^27.0.0",
18+
"@types/jest": "^27.0.0"
19+
},
20+
"peerDependencies": {
21+
"@elizaos/eliza": "^0.1.0"
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { type Action } from "@elizaos/core";
2+
import { SunoProvider } from "../providers/suno";
3+
import { CustomGenerateParams, GenerationResponse } from "../types";
4+
5+
const customGenerateMusic: Action<CustomGenerateParams, GenerationResponse> = {
6+
name: "custom-generate-music",
7+
description: "Generate music with custom parameters using Suno AI",
8+
provider: "suno",
9+
10+
async execute(params: CustomGenerateParams, provider: SunoProvider): Promise<GenerationResponse> {
11+
const response = await provider.request('/custom-generate', {
12+
method: 'POST',
13+
body: JSON.stringify({
14+
prompt: params.prompt,
15+
duration: params.duration || 30,
16+
temperature: params.temperature || 1.0,
17+
top_k: params.topK || 250,
18+
top_p: params.topP || 0.95,
19+
classifier_free_guidance: params.classifier_free_guidance || 3.0,
20+
reference_audio: params.reference_audio,
21+
style: params.style,
22+
bpm: params.bpm,
23+
key: params.key,
24+
mode: params.mode
25+
})
26+
});
27+
28+
return response;
29+
}
30+
};
31+
32+
export default customGenerateMusic;
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Action } from "@elizaos/eliza";
2+
import { SunoProvider } from "../providers/suno";
3+
import { ExtendParams, GenerationResponse } from "../types";
4+
5+
const extendAudio: Action<ExtendParams, GenerationResponse> = {
6+
name: "extend-audio",
7+
description: "Extend the duration of an existing audio generation",
8+
provider: "suno",
9+
10+
async execute(params: ExtendParams, provider: SunoProvider): Promise<GenerationResponse> {
11+
const response = await provider.request('/extend', {
12+
method: 'POST',
13+
body: JSON.stringify({
14+
audio_id: params.audio_id,
15+
duration: params.duration
16+
})
17+
});
18+
19+
return response;
20+
}
21+
};
22+
23+
export default extendAudio;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Action } from "@elizaos/eliza";
2+
import { SunoProvider } from "../providers/suno";
3+
import { GenerateParams, GenerationResponse } from "../types";
4+
5+
const generateMusic: Action<GenerateParams, GenerationResponse> = {
6+
name: "generate-music",
7+
description: "Generate music using Suno AI",
8+
provider: "suno",
9+
10+
async execute(params: GenerateParams, provider: SunoProvider): Promise<GenerationResponse> {
11+
const response = await provider.request('/generate', {
12+
method: 'POST',
13+
body: JSON.stringify({
14+
prompt: params.prompt,
15+
duration: params.duration || 30,
16+
temperature: params.temperature || 1.0,
17+
top_k: params.topK || 250,
18+
top_p: params.topP || 0.95,
19+
classifier_free_guidance: params.classifier_free_guidance || 3.0
20+
})
21+
});
22+
23+
return response;
24+
}
25+
};
26+
27+
export default generateMusic;

packages/plugin-suno/src/index.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Plugin } from "@elizaos/eliza";
2+
import generateMusic from "./actions/generate";
3+
import customGenerateMusic from "./actions/customGenerate";
4+
import extendAudio from "./actions/extend";
5+
import { SunoProvider, sunoProvider } from "./providers/suno";
6+
7+
export {
8+
SunoProvider,
9+
generateMusic as GenerateMusic,
10+
customGenerateMusic as CustomGenerateMusic,
11+
extendAudio as ExtendAudio
12+
};
13+
14+
export const sunoPlugin: Plugin = {
15+
name: "suno",
16+
description: "Suno AI Music Generation Plugin for Eliza",
17+
actions: [generateMusic, customGenerateMusic, extendAudio],
18+
evaluators: [],
19+
providers: [sunoProvider],
20+
};
21+
22+
export default sunoPlugin;
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Provider } from "@elizaos/eliza";
2+
3+
export interface SunoConfig {
4+
apiKey: string;
5+
baseUrl?: string;
6+
}
7+
8+
export class SunoProvider implements Provider {
9+
private apiKey: string;
10+
private baseUrl: string;
11+
12+
constructor(config: SunoConfig) {
13+
this.apiKey = config.apiKey;
14+
this.baseUrl = config.baseUrl || 'https://api.suno.ai/v1';
15+
}
16+
17+
async request(endpoint: string, options: RequestInit = {}) {
18+
const url = `${this.baseUrl}${endpoint}`;
19+
const headers = {
20+
'Authorization': `Bearer ${this.apiKey}`,
21+
'Content-Type': 'application/json',
22+
...options.headers,
23+
};
24+
25+
const response = await fetch(url, {
26+
...options,
27+
headers,
28+
});
29+
30+
if (!response.ok) {
31+
throw new Error(`Suno API error: ${response.statusText}`);
32+
}
33+
34+
return response.json();
35+
}
36+
}
37+
38+
export const sunoProvider = new SunoProvider({ apiKey: process.env.SUNO_API_KEY || '' });
39+
40+
export interface GenerateParams {
41+
prompt: string;
42+
duration?: number;
43+
temperature?: number;
44+
topK?: number;
45+
topP?: number;
46+
classifier_free_guidance?: number;
47+
}
48+
49+
export interface CustomGenerateParams extends GenerateParams {
50+
reference_audio?: string;
51+
style?: string;
52+
bpm?: number;
53+
key?: string;
54+
mode?: string;
55+
}
56+
57+
export interface ExtendParams {
58+
audio_id: string;
59+
duration: number;
60+
}
61+
62+
export interface GenerationResponse {
63+
id: string;
64+
status: 'pending' | 'processing' | 'completed' | 'failed';
65+
audio_url?: string;
66+
error?: string;
67+
}

0 commit comments

Comments
 (0)