media-agent-mcp 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,7 @@
1
+ """Media Agent MCP Server - A Model Context Protocol server for media processing."""
2
+
3
+ from . import ai_models, media_selectors, storage, video
4
+ from .server import main
5
+
6
+ __version__ = "0.1.0"
7
+ __all__ = ['ai_models', 'selectors', 'storage', 'video', 'main']
@@ -0,0 +1,17 @@
1
+ """AI models module for Media Agent MCP.
2
+
3
+ This module provides AI model functionality including image generation,
4
+ video generation, character maintenance, and vision-language tasks.
5
+ """
6
+
7
+ from .seedream import generate_image
8
+ from .seedance import generate_video
9
+ from .seededit import seededit
10
+ from .seed16 import process_vlm_task
11
+
12
+ __all__ = [
13
+ 'generate_image',
14
+ 'generate_video',
15
+ 'seededit',
16
+ 'process_vlm_task'
17
+ ]
@@ -0,0 +1,151 @@
1
+ """Seed1.6 VLM module.
2
+
3
+ This module provides functionality for vision-language tasks using the Seed1.6 model.
4
+ Based on ModelArk Chat API documentation with blocking requests.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import os
10
+ from typing import Dict, Any, List
11
+
12
+ import requests
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def process_vlm_task(messages: List[Dict[str, Any]], max_tokens: int = 4096,
18
+ temperature: float = 0.7, top_p: float = 0.9, is_json: bool = False, **kwargs) -> Dict[str, Any]:
19
+ """Process a vision-language task using Seed1.6 model with blocking request.
20
+
21
+ Args:
22
+ messages: List of messages in OpenAI format
23
+ max_tokens: Maximum tokens to generate
24
+ temperature: Sampling temperature (0-2)
25
+ top_p: Nucleus sampling parameter (0-1)
26
+ **kwargs: Additional parameters
27
+
28
+ Returns:
29
+ JSON response with status, data (response text), and message
30
+ """
31
+ try:
32
+ # Get API configuration from environment
33
+ api_key = os.getenv('ARK_API_KEY')
34
+ vlm_ep = os.getenv('VLM_EP', 'seed-1-6-250615')
35
+ if not api_key and not vlm_ep:
36
+ raise ValueError("ARK_API_KEY environment variable must be set")
37
+
38
+ # API endpoint for chat completions
39
+ endpoint = "https://ark.ap-southeast.bytepluses.com/api/v3/chat/completions"
40
+
41
+ headers = {
42
+ 'Authorization': f'Bearer {api_key}',
43
+ 'Content-Type': 'application/json'
44
+ }
45
+
46
+ # Prepare request payload according to API documentation
47
+ payload = {
48
+ 'model': vlm_ep,
49
+ 'messages': messages,
50
+ 'max_tokens': max_tokens,
51
+ 'temperature': temperature,
52
+ 'top_p': top_p,
53
+ 'stream': False # Blocking request
54
+ }
55
+
56
+ if is_json:
57
+ payload['response_format'] = {'type': 'json_object'}
58
+
59
+ logger.info(f"Processing VLM task with Seed1.6: {len(messages)} messages")
60
+
61
+ # Make blocking API request
62
+ response = requests.post(
63
+ endpoint,
64
+ headers=headers,
65
+ json=payload,
66
+ timeout=120 # Longer timeout for VLM tasks
67
+ )
68
+
69
+ if response.status_code == 200:
70
+ result = response.json()
71
+
72
+ # Extract response text
73
+ if 'choices' in result and len(result['choices']) > 0:
74
+ choice = result['choices'][0]
75
+ if 'message' in choice and 'content' in choice['message']:
76
+ response_text = choice['message']['content']
77
+ logger.info(f"VLM task completed successfully")
78
+ return {
79
+ "status": "success",
80
+ "data": {"response": response_text},
81
+ "message": "VLM task completed successfully"
82
+ }
83
+ else:
84
+ return {
85
+ "status": "error",
86
+ "data": None,
87
+ "message": "No content in response message"
88
+ }
89
+ else:
90
+ return {
91
+ "status": "error",
92
+ "data": None,
93
+ "message": "No choices in response"
94
+ }
95
+ else:
96
+ error_msg = f"API request failed with status {response.status_code}: {response.text}"
97
+ logger.error(error_msg)
98
+ return {
99
+ "status": "error",
100
+ "data": None,
101
+ "message": error_msg
102
+ }
103
+
104
+ except requests.exceptions.Timeout:
105
+ error_msg = "Request timeout - VLM task took too long"
106
+ logger.error(error_msg)
107
+ return {
108
+ "status": "error",
109
+ "data": None,
110
+ "message": error_msg
111
+ }
112
+ except requests.exceptions.RequestException as e:
113
+ error_msg = f"Network error: {str(e)}"
114
+ logger.error(error_msg)
115
+ return {
116
+ "status": "error",
117
+ "data": None,
118
+ "message": error_msg
119
+ }
120
+ except Exception as e:
121
+ error_msg = f"Unexpected error in Seed1.6 VLM task: {str(e)}"
122
+ logger.error(error_msg)
123
+ return {
124
+ "status": "error",
125
+ "data": None,
126
+ "message": error_msg
127
+ }
128
+
129
+
130
+ if __name__ == '__main__':
131
+ # Example usage
132
+ messages = [
133
+ {
134
+ "role": "user",
135
+ "content": [
136
+ {
137
+ "type": "text",
138
+ "text": "Describe the image in detail."
139
+ },
140
+ {
141
+ "type": "image_url",
142
+ "image_url": {
143
+ "url": "https://carey.tos-ap-southeast-1.bytepluses.com/Art%20Portrait/Art%20Portrait/Art%20Portrait/Art%20Portrait%20(2).jpg"
144
+ }
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+
150
+ response = process_vlm_task(messages)
151
+ print(response)
@@ -0,0 +1,258 @@
1
+ """Seedance video generation module.
2
+
3
+ This module provides functionality for generating videos using the Seedance AI model.
4
+ Based on ModelArk Video Generation API documentation with async polling.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import os
10
+ import time
11
+ from typing import Dict, Any
12
+
13
+ import requests
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def generate_video(prompt: str = "", first_frame_image: str = None, last_frame_image: str = None,
19
+ duration: int = 5, resolution: str = "720p",
20
+ fps: int = 24, seed: int = -1, **kwargs) -> Dict[str, Any]:
21
+ """Generate a video using Seedance model with first/last frame images.
22
+
23
+ Args:
24
+ prompt: Text prompt for video generation (optional for image-to-video)
25
+ first_frame_image: URL or base64 of the first frame image
26
+ last_frame_image: URL or base64 of the last frame image (optional)
27
+ duration: Video duration in seconds (5 or 10)
28
+ resolution: Video resolution (480p, 720p, 1080p)
29
+ ratio: Aspect ratio (21:9, 16:9, 4:3, 1:1, 3:4, 9:16, 9:21, adaptive)
30
+ fps: Frames per second (24)
31
+ seed: Random seed for reproducibility (-1 for random)
32
+ **kwargs: Additional parameters
33
+
34
+ Returns:
35
+ JSON response with status, data (video URL), and message
36
+ """
37
+ try:
38
+ # Get API configuration from environment
39
+ api_key = os.getenv('ARK_API_KEY')
40
+ seedance_ep = os.getenv('SEEDANCE_EP', 'seedance-1-0-lite-i2v-250428')
41
+ if not api_key or not seedance_ep:
42
+ raise ValueError("ARK_API_KEY environment variable must be set")
43
+
44
+ # API endpoint for video generapyttion tasks
45
+ endpoint = "https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks"
46
+
47
+ headers = {
48
+ 'Authorization': f'Bearer {api_key}',
49
+ 'Content-Type': 'application/json'
50
+ }
51
+
52
+ # Determine model based on input type
53
+ model = os.getenv('SEEDANCE_EP', 'seedance-1-0-lite-i2v-250428')
54
+
55
+ # Prepare content array for the request
56
+ content = []
57
+
58
+ # Add text content if prompt is provided
59
+ if prompt:
60
+ text_content = prompt
61
+ # Add parameters to the prompt
62
+ text_content += f" --rs {resolution} --dur {duration} --fps {fps}"
63
+ if seed != -1:
64
+ text_content += f" --seed {seed}"
65
+
66
+ content.append({
67
+ "type": "text",
68
+ "text": text_content
69
+ })
70
+
71
+ # Add first frame image if provided
72
+ if first_frame_image:
73
+ content.append({
74
+ "role": "first_frame",
75
+ "type": "image_url",
76
+ "image_url": {
77
+ "url": first_frame_image
78
+ }
79
+ })
80
+
81
+ if last_frame_image and first_frame_image:
82
+ content.append({
83
+ "role": "last_frame",
84
+ "type": "image_url",
85
+ "image_url": {
86
+ "url": last_frame_image
87
+ }
88
+ })
89
+
90
+ # Prepare request payload according to API documentation
91
+ payload = {
92
+ 'model': model,
93
+ 'content': content
94
+ }
95
+
96
+ logger.info(f"Starting video generation with Seedance: {prompt[:100]}...")
97
+
98
+ # Make initial API request to start video generation
99
+ response = requests.post(
100
+ endpoint,
101
+ headers=headers,
102
+ json=payload,
103
+ timeout=60
104
+ )
105
+
106
+ if response.status_code != 200:
107
+ error_msg = f"API request failed with status {response.status_code}: {response.text}"
108
+ logger.error(error_msg)
109
+ return {
110
+ "status": "error",
111
+ "data": None,
112
+ "message": error_msg
113
+ }
114
+
115
+ result = response.json()
116
+
117
+ # Extract task ID for polling
118
+ if 'id' not in result:
119
+ return {
120
+ "status": "error",
121
+ "data": None,
122
+ "message": "No task ID in response"
123
+ }
124
+
125
+ task_id = result['id']
126
+ logger.info(f"Video generation task started with ID: {task_id}")
127
+
128
+ # Poll for completion
129
+ return _poll_video_status(task_id, api_key)
130
+
131
+ except requests.exceptions.Timeout:
132
+ error_msg = "Request timeout - video generation request took too long"
133
+ logger.error(error_msg)
134
+ return {
135
+ "status": "error",
136
+ "data": None,
137
+ "message": error_msg
138
+ }
139
+ except requests.exceptions.RequestException as e:
140
+ error_msg = f"Network error: {str(e)}"
141
+ logger.error(error_msg)
142
+ return {
143
+ "status": "error",
144
+ "data": None,
145
+ "message": error_msg
146
+ }
147
+ except Exception as e:
148
+ error_msg = f"Unexpected error in Seedance generation: {str(e)}"
149
+ logger.error(error_msg)
150
+ return {
151
+ "status": "error",
152
+ "data": None,
153
+ "message": error_msg
154
+ }
155
+
156
+
157
+ def _poll_video_status(task_id: str, api_key: str, max_wait_time: int = 90) -> Dict[str, Any]:
158
+ """Poll video generation status until completion.
159
+
160
+ Args:
161
+ task_id: The task ID to poll
162
+ api_key: API key for authentication
163
+ max_wait_time: Maximum time to wait in seconds
164
+
165
+ Returns:
166
+ JSON response with status, data (video URL), and message
167
+ """
168
+ query_endpoint = f"https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks/{task_id}"
169
+
170
+ headers = {
171
+ 'Authorization': f'Bearer {api_key}',
172
+ 'Content-Type': 'application/json'
173
+ }
174
+
175
+ start_time = time.time()
176
+
177
+ while time.time() - start_time < max_wait_time:
178
+ try:
179
+ response = requests.get(query_endpoint, headers=headers, timeout=30)
180
+
181
+ if response.status_code != 200:
182
+ logger.error(f"Status query failed: {response.status_code} - {response.text}")
183
+ time.sleep(5)
184
+ continue
185
+
186
+ result = response.json()
187
+ status = result.get('status')
188
+
189
+ logger.info(f"Video generation status: {status}")
190
+
191
+ if status == 'succeeded':
192
+ # Extract video URL from response
193
+ if 'content' in result and 'video_url' in result['content']:
194
+ video_url = result['content']['video_url']
195
+ if video_url:
196
+ logger.info(f"Video generated successfully: {video_url}")
197
+ return {
198
+ "status": "success",
199
+ "data": {"video_url": video_url},
200
+ "message": "Video generated successfully"
201
+ }
202
+ else:
203
+ return {
204
+ "status": "error",
205
+ "data": None,
206
+ "message": "No video URL in completed response"
207
+ }
208
+ else:
209
+ return {
210
+ "status": "error",
211
+ "data": None,
212
+ "message": "No video content in completed response"
213
+ }
214
+
215
+ elif status == 'failed':
216
+ error_detail = result.get('error', {}).get('message', 'Unknown error')
217
+ return {
218
+ "status": "error",
219
+ "data": None,
220
+ "message": f"Video generation failed - {error_detail}"
221
+ }
222
+
223
+ elif status in ['queued', 'running']:
224
+ # Continue polling
225
+ time.sleep(5)
226
+ continue
227
+
228
+ else:
229
+ logger.warning(f"Unknown status: {status}")
230
+ time.sleep(5)
231
+ continue
232
+
233
+ except requests.exceptions.RequestException as e:
234
+ logger.error(f"Error polling status: {str(e)}")
235
+ time.sleep(5)
236
+ continue
237
+ except Exception as e:
238
+ logger.error(f"Unexpected error polling status: {str(e)}")
239
+ time.sleep(5)
240
+ continue
241
+
242
+ return f"Error: Video generation timed out after {max_wait_time} seconds"
243
+
244
+
245
+ if __name__ == '__main__':
246
+ # Example usage
247
+ video_url = generate_video(
248
+ prompt="the woman slowly moves",
249
+ first_frame_image="https://carey.tos-ap-southeast-1.bytepluses.com/Art%20Portrait/Art%20Portrait/Art%20Portrait/Art%20Portrait%20(1).jpg",
250
+ # last_frame_image="https://carey.tos-ap-southeast-1.bytepluses.com/Art%20Portrait/Art%20Portrait/Art%20Portrait/Art%20Portrait%20(2).jpg",
251
+ duration=5,
252
+ resolution="720p",
253
+ ratio="16:9",
254
+ fps=24,
255
+ seed=42
256
+ )
257
+
258
+ print(f"Generated video URL: {video_url}")
@@ -0,0 +1,94 @@
1
+ import os
2
+ from typing import Dict, Any
3
+ from volcengine.visual.VisualService import VisualService
4
+
5
+
6
+ def parse_seededit_response(response) -> Dict[str, Any]:
7
+ """
8
+ Parse the seededit API response and extract the image URL.
9
+
10
+ :param response: The API response dictionary
11
+ :return: JSON response with status, data (image URL), and message
12
+ """
13
+ try:
14
+ # Handle both possible response formats
15
+ if 'data' in response and 'image_urls' in response['data']:
16
+ # Format 1: {'data': {'image_urls': [...]}}
17
+ image_urls = response['data']['image_urls']
18
+ elif 'image_urls' in response:
19
+ # Format 2: {'image_urls': [...]}
20
+ image_urls = response['image_urls']
21
+ else:
22
+ raise ValueError("No image_urls found in response")
23
+
24
+ if not image_urls or len(image_urls) == 0:
25
+ raise ValueError("Empty image_urls list")
26
+
27
+ # Get the first URL and clean it
28
+ url = image_urls[0]
29
+ # Remove leading/trailing whitespace and backticks
30
+ url = url.strip().strip('`').strip()
31
+
32
+ return {
33
+ "status": "success",
34
+ "data": {"image_url": url},
35
+ "message": "Image URL parsed successfully"
36
+ }
37
+ except Exception as e:
38
+ return {
39
+ "status": "error",
40
+ "data": None,
41
+ "message": f"Failed to parse response: {e}"
42
+ }
43
+
44
+
45
+ def seededit(image_url, prompt, charactor_keep=False, return_url=True, scale=1, seed=-1) -> Dict[str, Any]:
46
+ """
47
+ Perform image editing using the VisualService.
48
+
49
+ :param image_url: URL of the input image.
50
+ :param prompt: The editing prompt.
51
+ :param charactor_keep: Whether to keep the main character in the image.
52
+ :param return_url: Whether to return image URL or base64 string.
53
+ :param scale: Text influence scale (0.1-1.0).
54
+ :param seed: Random seed for reproducibility.
55
+ :return: JSON response with status, data (image URL), and message.
56
+ """
57
+ try:
58
+ visual_service = VisualService()
59
+
60
+ # call below method if you don't set ak and sk in $HOME/.volc/config
61
+ ak = os.getenv('VOLC_AK')
62
+ sk = os.getenv('VOLC_SK')
63
+
64
+ if not ak or not sk:
65
+ return {
66
+ "status": "error",
67
+ "data": None,
68
+ "message": "VOLC_AK and VOLC_SK environment variables must be set"
69
+ }
70
+
71
+ visual_service.set_ak(ak)
72
+ visual_service.set_sk(sk)
73
+
74
+ form = {
75
+ "req_key": "seed3l_single_ip" if charactor_keep else "seededit_v3.0",
76
+ "image_urls": [image_url],
77
+ 'prompt': prompt,
78
+ 'return_url': return_url,
79
+ 'scale': scale,
80
+ 'seed': seed,
81
+ }
82
+
83
+ response = visual_service.cv_process(form)
84
+ return parse_seededit_response(response)
85
+ except Exception as e:
86
+ return {
87
+ "status": "error",
88
+ "data": None,
89
+ "message": f"Error in seededit: {str(e)}"
90
+ }
91
+
92
+
93
+ if __name__ == '__main__':
94
+ print(seededit('https://carey.tos-ap-southeast-1.bytepluses.com/Art%20Portrait/Art%20Portrait/Art%20Portrait/Art%20Portrait%20(1).jpg', prompt='在和边钓鱼', charactor_keep=False))
@@ -0,0 +1,136 @@
1
+ """Seedream image generation module.
2
+
3
+ This module provides functionality for generating images using the Seedream AI model.
4
+ Based on ModelArk Image Generation API documentation.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import os
10
+ from typing import Dict, Any
11
+
12
+ import requests
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def generate_image(prompt: str, size: str = "1024x1024", guidance_scale: float = 2.5,
18
+ watermark: bool = True, seed: int = -1, **kwargs) -> Dict[str, Any]:
19
+ """Generate an image using Seedream model.
20
+
21
+ Args:
22
+ prompt: Text prompt for image generation
23
+ size: Image size (e.g., "1024x1024", "864x1152", etc.), Must be between [512x512, 2048x2048]
24
+ guidance_scale: Controls prompt alignment (1-10)
25
+ watermark: Whether to add watermark
26
+ seed: Random seed for reproducibility (-1 for random)
27
+ **kwargs: Additional parameters
28
+
29
+ Returns:
30
+ JSON response with status, data (image URL), and message
31
+ """
32
+ try:
33
+ # Get API configuration from environment
34
+ api_key = os.getenv('ARK_API_KEY')
35
+ seedream_ep = os.getenv('SEEDREAM_EP', 'seedream-3-0-t2i-250415')
36
+
37
+ if not api_key or not seedream_ep:
38
+ raise ValueError("ARK_API_KEY environment variable must be set")
39
+
40
+ # API endpoint for image generation
41
+ endpoint = "https://ark.ap-southeast.bytepluses.com/api/v3/images/generations"
42
+
43
+ headers = {
44
+ 'Authorization': f'Bearer {api_key}',
45
+ 'Content-Type': 'application/json'
46
+ }
47
+
48
+ # Prepare request payload according to API documentation
49
+ payload = {
50
+ 'model': seedream_ep,
51
+ 'prompt': prompt,
52
+ 'response_format': 'url',
53
+ 'size': size,
54
+ 'guidance_scale': guidance_scale,
55
+ 'watermark': watermark
56
+ }
57
+
58
+ # Add seed if specified
59
+ if seed != -1:
60
+ payload['seed'] = seed
61
+
62
+ logger.info(f"Generating image with Seedream: {prompt[:100]}...")
63
+
64
+ # Make API request
65
+ response = requests.post(
66
+ endpoint,
67
+ headers=headers,
68
+ json=payload,
69
+ timeout=60
70
+ )
71
+
72
+ if response.status_code == 200:
73
+ result = response.json()
74
+
75
+ # Extract image URL from response
76
+ if 'data' in result and len(result['data']) > 0:
77
+ image_url = result['data'][0].get('url')
78
+ if image_url:
79
+ logger.info(f"Image generated successfully: {image_url}")
80
+ return {
81
+ "status": "success",
82
+ "data": {"image_url": image_url},
83
+ "message": "Image generated successfully"
84
+ }
85
+ else:
86
+ return {
87
+ "status": "error",
88
+ "data": None,
89
+ "message": "No image URL in response"
90
+ }
91
+ else:
92
+ return {
93
+ "status": "error",
94
+ "data": None,
95
+ "message": "No image data in response"
96
+ }
97
+ else:
98
+ error_msg = f"API request failed with status {response.status_code}: {response.text}"
99
+ logger.error(error_msg)
100
+ return {
101
+ "status": "error",
102
+ "data": None,
103
+ "message": error_msg
104
+ }
105
+
106
+ except requests.exceptions.Timeout:
107
+ error_msg = "Request timeout - image generation took too long"
108
+ logger.error(error_msg)
109
+ return {
110
+ "status": "error",
111
+ "data": None,
112
+ "message": error_msg
113
+ }
114
+ except requests.exceptions.RequestException as e:
115
+ error_msg = f"Network error: {str(e)}"
116
+ logger.error(error_msg)
117
+ return {
118
+ "status": "error",
119
+ "data": None,
120
+ "message": error_msg
121
+ }
122
+ except Exception as e:
123
+ error_msg = f"Unexpected error in Seedream generation: {str(e)}"
124
+ logger.error(error_msg)
125
+ return {
126
+ "status": "error",
127
+ "data": None,
128
+ "message": error_msg
129
+ }
130
+
131
+
132
+ if __name__ == "__main__":
133
+ # Test the function
134
+ prompt = "A serene landscape with mountains and a lake"
135
+ image_url = generate_image(prompt)
136
+ print(image_url)
@@ -0,0 +1,9 @@
1
+ """Selectors module for Media Agent MCP.
2
+
3
+ This module provides selection functionality for images and videos.
4
+ """
5
+
6
+ from .image_selector import select_best_image
7
+ from .video_selector import select_best_video
8
+
9
+ __all__ = ['select_best_image', 'select_best_video']