Skip to content

Commit

Permalink
feat(new-plugin): suno Eliza plugin (elizaOS#2656)
Browse files Browse the repository at this point in the history
* Plugin Suno

Plugin Suno Initial Commit

* add initialization

* fix imports

---------

Co-authored-by: Sayo <[email protected]>
  • Loading branch information
Freytes and wtfsayo authored Jan 22, 2025
1 parent 5347b1f commit 78df869
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -777,3 +777,6 @@ EMAIL_INCOMING_HOST=imap.example.com
EMAIL_INCOMING_PORT=993 # Default port for secure IMAP
EMAIL_INCOMING_USER=
EMAIL_INCOMING_PASS=

# Suno AI Music Generation
SUNO_API_KEY=
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"@elizaos/plugin-0x": "workspace:*",
"@elizaos/plugin-dkg": "workspace:*",
"@elizaos/plugin-email": "workspace:*",
"@elizaos/plugin-suno": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
4 changes: 3 additions & 1 deletion agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ import path from "path";
import { fileURLToPath } from "url";
import yargs from "yargs";
import { emailPlugin } from "@elizaos/plugin-email";
import { sunoPlugin } from "@elizaos/plugin-suno";

const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
const __dirname = path.dirname(__filename); // get the name of the directory
Expand Down Expand Up @@ -1127,7 +1128,8 @@ export async function createAgent(
: null,
getSecret(character, "EMAIL_INCOMING_USER") && getSecret(character, "EMAIL_INCOMING_PASS") ||
getSecret(character, "EMAIL_OUTGOING_USER") && getSecret(character, "EMAIL_OUTGOING_PASS") ?
emailPlugin : null
emailPlugin : null,
getSecret(character, "SUNO_API_KEY") ? sunoPlugin : null
].filter(Boolean),
providers: [],
actions: [],
Expand Down
134 changes: 134 additions & 0 deletions packages/plugin-suno/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
@elizaos/plugin-suno

A Suno AI music generation plugin for ElizaOS that enables AI-powered music creation and audio manipulation.

OVERVIEW

The Suno plugin integrates Suno AI's powerful music generation capabilities into ElizaOS, providing a seamless way to:
- Generate music from text prompts
- Create custom music with fine-tuned parameters
- Extend existing audio tracks

Original Plugin: https://github.com/gcui-art/suno-api?tab=readme-ov-file

INSTALLATION

npm install @elizaos/plugin-suno

QUICK START

1. Register the plugin with ElizaOS:

import { sunoPlugin } from '@elizaos/plugin-suno';
import { Eliza } from '@elizaos/eliza';

const eliza = new Eliza();
eliza.registerPlugin(sunoPlugin);

2. Configure the Suno provider with your API credentials:

import { sunoProvider } from '@elizaos/plugin-suno';

sunoProvider.configure({
apiKey: 'your-suno-api-key'
});

FEATURES

1. Generate Music
Generate music using predefined settings and a text prompt:

await eliza.execute('suno.generate', {
prompt: "An upbeat electronic dance track with energetic beats"
});

2. Custom Music Generation
Create music with detailed control over generation parameters:

await eliza.execute('suno.customGenerate', {
prompt: "A melodic piano piece with soft strings",
temperature: 0.8,
duration: 30
});

3. Extend Audio
Extend existing audio tracks to create longer compositions:

await eliza.execute('suno.extend', {
audioFile: "path/to/audio.mp3",
duration: 60
});

API REFERENCE

SunoProvider Configuration
The Suno provider accepts the following configuration options:

interface SunoConfig {
apiKey: string;
}

Action Parameters:

1. Generate Music
interface GenerateMusicParams {
prompt: string;
}

2. Custom Generate Music
interface CustomGenerateMusicParams {
prompt: string;
temperature?: number;
duration?: number;
}

3. Extend Audio
interface ExtendAudioParams {
audioFile: string;
duration: number;
}

ERROR HANDLING

The plugin includes built-in error handling for common scenarios:

try {
await eliza.execute('suno.generate', params);
} catch (error) {
if (error.code === 'SUNO_API_ERROR') {
// Handle API-specific errors
}
// Handle other errors
}

EXAMPLES

Creating a Complete Song:

const result = await eliza.execute('suno.generate', {
prompt: "Create a pop song with vocals, drums, and guitar",
duration: 180 // 3 minutes
});

console.log(`Generated music file: ${result.outputPath}`);

Extending an Existing Track:

const extended = await eliza.execute('suno.extend', {
audioFile: "original-track.mp3",
duration: 60 // Add 60 seconds
});

console.log(`Extended audio file: ${extended.outputPath}`);


LICENSE

MIT

SUPPORT

For issues and feature requests, please open an issue on our GitHub repository.

---
Built with ❤️ for ElizaOS
3 changes: 3 additions & 0 deletions packages/plugin-suno/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.config.mjs";

export default [...eslintGlobalConfig];
23 changes: 23 additions & 0 deletions packages/plugin-suno/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@elizaos/plugin-suno",
"version": "0.1.9-alpha.1",
"description": "Suno AI Music Generation Plugin for Eliza",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "jest"
},
"dependencies": {
"@elizaos/core": "^0.1.0"
},
"devDependencies": {
"typescript": "^4.9.0",
"@types/node": "^16.0.0",
"jest": "^27.0.0",
"@types/jest": "^27.0.0"
},
"peerDependencies": {
"@elizaos/eliza": "^0.1.0"
}
}
32 changes: 32 additions & 0 deletions packages/plugin-suno/src/actions/customGenerate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { type Action } from "@elizaos/core";
import { SunoProvider } from "../providers/suno";
import { CustomGenerateParams, GenerationResponse } from "../types";

const customGenerateMusic: Action<CustomGenerateParams, GenerationResponse> = {
name: "custom-generate-music",
description: "Generate music with custom parameters using Suno AI",
provider: "suno",

async execute(params: CustomGenerateParams, provider: SunoProvider): Promise<GenerationResponse> {
const response = await provider.request('/custom-generate', {
method: 'POST',
body: JSON.stringify({
prompt: params.prompt,
duration: params.duration || 30,
temperature: params.temperature || 1.0,
top_k: params.topK || 250,
top_p: params.topP || 0.95,
classifier_free_guidance: params.classifier_free_guidance || 3.0,
reference_audio: params.reference_audio,
style: params.style,
bpm: params.bpm,
key: params.key,
mode: params.mode
})
});

return response;
}
};

export default customGenerateMusic;
23 changes: 23 additions & 0 deletions packages/plugin-suno/src/actions/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Action } from "@elizaos/eliza";
import { SunoProvider } from "../providers/suno";
import { ExtendParams, GenerationResponse } from "../types";

const extendAudio: Action<ExtendParams, GenerationResponse> = {
name: "extend-audio",
description: "Extend the duration of an existing audio generation",
provider: "suno",

async execute(params: ExtendParams, provider: SunoProvider): Promise<GenerationResponse> {
const response = await provider.request('/extend', {
method: 'POST',
body: JSON.stringify({
audio_id: params.audio_id,
duration: params.duration
})
});

return response;
}
};

export default extendAudio;
27 changes: 27 additions & 0 deletions packages/plugin-suno/src/actions/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Action } from "@elizaos/eliza";
import { SunoProvider } from "../providers/suno";
import { GenerateParams, GenerationResponse } from "../types";

const generateMusic: Action<GenerateParams, GenerationResponse> = {
name: "generate-music",
description: "Generate music using Suno AI",
provider: "suno",

async execute(params: GenerateParams, provider: SunoProvider): Promise<GenerationResponse> {
const response = await provider.request('/generate', {
method: 'POST',
body: JSON.stringify({
prompt: params.prompt,
duration: params.duration || 30,
temperature: params.temperature || 1.0,
top_k: params.topK || 250,
top_p: params.topP || 0.95,
classifier_free_guidance: params.classifier_free_guidance || 3.0
})
});

return response;
}
};

export default generateMusic;
22 changes: 22 additions & 0 deletions packages/plugin-suno/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Plugin } from "@elizaos/eliza";
import generateMusic from "./actions/generate";
import customGenerateMusic from "./actions/customGenerate";
import extendAudio from "./actions/extend";
import { SunoProvider, sunoProvider } from "./providers/suno";

export {
SunoProvider,
generateMusic as GenerateMusic,
customGenerateMusic as CustomGenerateMusic,
extendAudio as ExtendAudio
};

export const sunoPlugin: Plugin = {
name: "suno",
description: "Suno AI Music Generation Plugin for Eliza",
actions: [generateMusic, customGenerateMusic, extendAudio],
evaluators: [],
providers: [sunoProvider],
};

export default sunoPlugin;
67 changes: 67 additions & 0 deletions packages/plugin-suno/src/providers/suno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Provider } from "@elizaos/eliza";

export interface SunoConfig {
apiKey: string;
baseUrl?: string;
}

export class SunoProvider implements Provider {
private apiKey: string;
private baseUrl: string;

constructor(config: SunoConfig) {
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl || 'https://api.suno.ai/v1';
}

async request(endpoint: string, options: RequestInit = {}) {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers,
};

const response = await fetch(url, {
...options,
headers,
});

if (!response.ok) {
throw new Error(`Suno API error: ${response.statusText}`);
}

return response.json();
}
}

export const sunoProvider = new SunoProvider({ apiKey: process.env.SUNO_API_KEY || '' });

export interface GenerateParams {
prompt: string;
duration?: number;
temperature?: number;
topK?: number;
topP?: number;
classifier_free_guidance?: number;
}

export interface CustomGenerateParams extends GenerateParams {
reference_audio?: string;
style?: string;
bpm?: number;
key?: string;
mode?: string;
}

export interface ExtendParams {
audio_id: string;
duration: number;
}

export interface GenerationResponse {
id: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
audio_url?: string;
error?: string;
}
Loading

0 comments on commit 78df869

Please sign in to comment.