massgen 0.1.3__py3-none-any.whl → 0.1.4__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.
Potentially problematic release.
This version of massgen might be problematic. Click here for more details.
- massgen/__init__.py +1 -1
- massgen/api_params_handler/_chat_completions_api_params_handler.py +4 -0
- massgen/api_params_handler/_claude_api_params_handler.py +4 -0
- massgen/api_params_handler/_gemini_api_params_handler.py +4 -0
- massgen/api_params_handler/_response_api_params_handler.py +4 -0
- massgen/backend/base_with_custom_tool_and_mcp.py +25 -5
- massgen/backend/docs/permissions_and_context_files.md +2 -2
- massgen/backend/response.py +2 -0
- massgen/configs/README.md +49 -40
- massgen/configs/tools/custom_tools/crawl4ai_example.yaml +55 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_file_generation_multi.yaml +61 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_file_generation_single.yaml +29 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_image_generation_multi.yaml +51 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_image_generation_single.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_speech_generation_multi.yaml +55 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_speech_generation_single.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_video_generation_multi.yaml +47 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_video_generation_single.yaml +29 -0
- massgen/configs/tools/custom_tools/multimodal_tools/understand_audio.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/understand_file.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/understand_image.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/understand_video.yaml +1 -1
- massgen/configs/tools/custom_tools/multimodal_tools/youtube_video_analysis.yaml +1 -1
- massgen/filesystem_manager/_filesystem_manager.py +1 -0
- massgen/filesystem_manager/_path_permission_manager.py +148 -0
- massgen/message_templates.py +160 -12
- massgen/orchestrator.py +16 -0
- massgen/tests/test_binary_file_blocking.py +274 -0
- massgen/tests/test_case_studies.md +12 -12
- massgen/tests/test_multimodal_size_limits.py +407 -0
- massgen/tool/_manager.py +7 -2
- massgen/tool/_multimodal_tools/image_to_image_generation.py +293 -0
- massgen/tool/_multimodal_tools/text_to_file_generation.py +455 -0
- massgen/tool/_multimodal_tools/text_to_image_generation.py +222 -0
- massgen/tool/_multimodal_tools/text_to_speech_continue_generation.py +226 -0
- massgen/tool/_multimodal_tools/text_to_speech_transcription_generation.py +217 -0
- massgen/tool/_multimodal_tools/text_to_video_generation.py +223 -0
- massgen/tool/_multimodal_tools/understand_audio.py +19 -1
- massgen/tool/_multimodal_tools/understand_file.py +6 -1
- massgen/tool/_multimodal_tools/understand_image.py +112 -8
- massgen/tool/_multimodal_tools/understand_video.py +32 -5
- massgen/tool/_web_tools/crawl4ai_tool.py +718 -0
- massgen/tool/docs/multimodal_tools.md +589 -0
- {massgen-0.1.3.dist-info → massgen-0.1.4.dist-info}/METADATA +96 -69
- {massgen-0.1.3.dist-info → massgen-0.1.4.dist-info}/RECORD +49 -40
- massgen/configs/tools/custom_tools/crawl4ai_mcp_example.yaml +0 -67
- massgen/configs/tools/custom_tools/crawl4ai_multi_agent_example.yaml +0 -68
- massgen/configs/tools/custom_tools/multimodal_tools/playwright_with_img_understanding.yaml +0 -98
- massgen/configs/tools/custom_tools/multimodal_tools/understand_video_example.yaml +0 -54
- massgen/configs/tools/memory/README.md +0 -199
- massgen/configs/tools/memory/gpt5mini_gemini_context_window_management.yaml +0 -131
- massgen/configs/tools/memory/gpt5mini_gemini_no_persistent_memory.yaml +0 -133
- massgen/configs/tools/memory/test_context_window_management.py +0 -286
- massgen/configs/tools/multimodal/gpt5mini_gpt5nano_documentation_evolution.yaml +0 -97
- {massgen-0.1.3.dist-info → massgen-0.1.4.dist-info}/WHEEL +0 -0
- {massgen-0.1.3.dist-info → massgen-0.1.4.dist-info}/entry_points.txt +0 -0
- {massgen-0.1.3.dist-info → massgen-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.1.3.dist-info → massgen-0.1.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Tests for size and dimension limits in multimodal tools (image, video, audio).
|
|
5
|
+
|
|
6
|
+
This test suite generates fake media files to test:
|
|
7
|
+
- understand_image: 18MB file size + 768px × 2000px dimension limits
|
|
8
|
+
- understand_video: Frame dimension limits (768px × 2000px per frame)
|
|
9
|
+
- understand_audio: 25MB file size limit
|
|
10
|
+
|
|
11
|
+
All test files are created in temporary directories and cleaned up after tests.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import tempfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestImageSizeLimits:
|
|
21
|
+
"""Test suite for understand_image size and dimension limits."""
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def temp_dir(self):
|
|
25
|
+
"""Create a temporary directory for test files."""
|
|
26
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
27
|
+
yield Path(tmpdir)
|
|
28
|
+
|
|
29
|
+
def _create_test_image(self, width: int, height: int, output_path: Path, format: str = "PNG"):
|
|
30
|
+
"""
|
|
31
|
+
Create a test image with specified dimensions.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
width: Image width in pixels
|
|
35
|
+
height: Image height in pixels
|
|
36
|
+
output_path: Path to save the image
|
|
37
|
+
format: Image format (PNG or JPEG)
|
|
38
|
+
"""
|
|
39
|
+
import numpy as np
|
|
40
|
+
from PIL import Image
|
|
41
|
+
|
|
42
|
+
# Create a simple gradient image
|
|
43
|
+
img_array = np.zeros((height, width, 3), dtype=np.uint8)
|
|
44
|
+
for i in range(height):
|
|
45
|
+
img_array[i, :, 0] = int((i / height) * 255) # Red gradient
|
|
46
|
+
for j in range(width):
|
|
47
|
+
img_array[:, j, 1] = int((j / width) * 255) # Green gradient
|
|
48
|
+
|
|
49
|
+
img = Image.fromarray(img_array, "RGB")
|
|
50
|
+
img.save(output_path, format=format)
|
|
51
|
+
|
|
52
|
+
def _create_large_image(self, output_path: Path, target_size_mb: float = 20):
|
|
53
|
+
"""
|
|
54
|
+
Create a large image file exceeding size limits.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
output_path: Path to save the image
|
|
58
|
+
target_size_mb: Target size in megabytes
|
|
59
|
+
"""
|
|
60
|
+
import numpy as np
|
|
61
|
+
from PIL import Image
|
|
62
|
+
|
|
63
|
+
# Calculate dimensions to achieve target file size
|
|
64
|
+
# PNG compression varies, so we'll create a large uncompressed image
|
|
65
|
+
# Rough estimate: width * height * 3 (RGB) should exceed target
|
|
66
|
+
pixels_needed = int((target_size_mb * 1024 * 1024) / 3)
|
|
67
|
+
side = int(pixels_needed**0.5)
|
|
68
|
+
|
|
69
|
+
# Create random noise image (doesn't compress well)
|
|
70
|
+
img_array = np.random.randint(0, 256, (side, side, 3), dtype=np.uint8)
|
|
71
|
+
img = Image.fromarray(img_array, "RGB")
|
|
72
|
+
img.save(output_path, format="PNG")
|
|
73
|
+
|
|
74
|
+
@pytest.mark.asyncio
|
|
75
|
+
async def test_image_within_limits(self, temp_dir):
|
|
76
|
+
"""Test that images within size and dimension limits are processed without resizing."""
|
|
77
|
+
from massgen.tool._multimodal_tools.understand_image import understand_image
|
|
78
|
+
|
|
79
|
+
# Create a small image within limits (512x512)
|
|
80
|
+
img_path = temp_dir / "small_image.png"
|
|
81
|
+
self._create_test_image(512, 512, img_path, format="PNG")
|
|
82
|
+
|
|
83
|
+
# Use real OpenAI API
|
|
84
|
+
result = await understand_image(str(img_path), prompt="Describe this test image in one sentence.")
|
|
85
|
+
|
|
86
|
+
# Check that it succeeded
|
|
87
|
+
assert result.output_blocks is not None
|
|
88
|
+
assert len(result.output_blocks) > 0
|
|
89
|
+
|
|
90
|
+
# Parse result JSON
|
|
91
|
+
import json
|
|
92
|
+
|
|
93
|
+
result_data = json.loads(result.output_blocks[0].data)
|
|
94
|
+
|
|
95
|
+
print("\n" + "=" * 80)
|
|
96
|
+
print("TEST: Image Within Limits (512x512)")
|
|
97
|
+
print("=" * 80)
|
|
98
|
+
print(json.dumps(result_data, indent=2))
|
|
99
|
+
print("=" * 80 + "\n")
|
|
100
|
+
|
|
101
|
+
assert result_data["success"] is True
|
|
102
|
+
|
|
103
|
+
@pytest.mark.asyncio
|
|
104
|
+
async def test_image_dimension_limit(self, temp_dir):
|
|
105
|
+
"""Test that images exceeding dimension limits are resized."""
|
|
106
|
+
from massgen.tool._multimodal_tools.understand_image import understand_image
|
|
107
|
+
|
|
108
|
+
# Create an image exceeding dimension limits (3000x4000)
|
|
109
|
+
img_path = temp_dir / "large_dimensions.jpg"
|
|
110
|
+
self._create_test_image(3000, 4000, img_path, format="JPEG")
|
|
111
|
+
|
|
112
|
+
# Check original size
|
|
113
|
+
from PIL import Image
|
|
114
|
+
|
|
115
|
+
with Image.open(img_path) as img:
|
|
116
|
+
original_width, original_height = img.size
|
|
117
|
+
assert original_width == 3000
|
|
118
|
+
assert original_height == 4000
|
|
119
|
+
|
|
120
|
+
# Use real OpenAI API - should resize internally and succeed
|
|
121
|
+
result = await understand_image(str(img_path), prompt="Describe this test image in one sentence.")
|
|
122
|
+
|
|
123
|
+
# Check that it succeeded (image was resized internally)
|
|
124
|
+
assert result.output_blocks is not None
|
|
125
|
+
import json
|
|
126
|
+
|
|
127
|
+
result_data = json.loads(result.output_blocks[0].data)
|
|
128
|
+
|
|
129
|
+
print("\n" + "=" * 80)
|
|
130
|
+
print("TEST: Image Exceeding Dimension Limits (3000x4000)")
|
|
131
|
+
print("=" * 80)
|
|
132
|
+
print(json.dumps(result_data, indent=2))
|
|
133
|
+
print("=" * 80 + "\n")
|
|
134
|
+
|
|
135
|
+
assert result_data["success"] is True
|
|
136
|
+
|
|
137
|
+
def test_image_dimension_calculation(self, temp_dir):
|
|
138
|
+
"""Test dimension limit calculation logic directly."""
|
|
139
|
+
# Test that we correctly identify when resizing is needed
|
|
140
|
+
max_short_side = 768
|
|
141
|
+
max_long_side = 2000
|
|
142
|
+
|
|
143
|
+
test_cases = [
|
|
144
|
+
# (width, height, needs_resize)
|
|
145
|
+
(512, 512, False), # Within limits
|
|
146
|
+
(768, 2000, False), # Exactly at limits
|
|
147
|
+
(2000, 768, False), # Rotated, exactly at limits
|
|
148
|
+
(800, 1000, True), # Short side exceeds
|
|
149
|
+
(1000, 2500, True), # Long side exceeds
|
|
150
|
+
(3000, 4000, True), # Both exceed
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
for width, height, expected_resize in test_cases:
|
|
154
|
+
short_side = min(width, height)
|
|
155
|
+
long_side = max(width, height)
|
|
156
|
+
needs_resize = short_side > max_short_side or long_side > max_long_side
|
|
157
|
+
|
|
158
|
+
assert needs_resize == expected_resize, f"Dimension check failed for {width}x{height}: expected resize={expected_resize}, got {needs_resize}"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class TestVideoFrameLimits:
|
|
162
|
+
"""Test suite for understand_video frame dimension limits."""
|
|
163
|
+
|
|
164
|
+
@pytest.fixture
|
|
165
|
+
def temp_dir(self):
|
|
166
|
+
"""Create a temporary directory for test files."""
|
|
167
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
168
|
+
yield Path(tmpdir)
|
|
169
|
+
|
|
170
|
+
def _create_test_video(self, width: int, height: int, output_path: Path, num_frames: int = 30):
|
|
171
|
+
"""
|
|
172
|
+
Create a test video with specified dimensions.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
width: Video width in pixels
|
|
176
|
+
height: Video height in pixels
|
|
177
|
+
output_path: Path to save the video
|
|
178
|
+
num_frames: Number of frames to generate
|
|
179
|
+
"""
|
|
180
|
+
import cv2
|
|
181
|
+
import numpy as np
|
|
182
|
+
|
|
183
|
+
# Define the codec and create VideoWriter object
|
|
184
|
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
|
185
|
+
fps = 10.0
|
|
186
|
+
video = cv2.VideoWriter(str(output_path), fourcc, fps, (width, height))
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
for i in range(num_frames):
|
|
190
|
+
# Create a frame with gradient (changes over time)
|
|
191
|
+
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
|
192
|
+
intensity = int((i / num_frames) * 255)
|
|
193
|
+
frame[:, :, 0] = intensity # Blue channel varies by frame
|
|
194
|
+
frame[: height // 2, :, 1] = 128 # Green in top half
|
|
195
|
+
frame[height // 2 :, :, 2] = 128 # Red in bottom half
|
|
196
|
+
|
|
197
|
+
video.write(frame)
|
|
198
|
+
finally:
|
|
199
|
+
video.release()
|
|
200
|
+
|
|
201
|
+
@pytest.mark.asyncio
|
|
202
|
+
async def test_video_with_large_frames(self, temp_dir):
|
|
203
|
+
"""Test that video with large frame dimensions processes correctly (frames are resized)."""
|
|
204
|
+
try:
|
|
205
|
+
import cv2 # noqa: F401
|
|
206
|
+
except ImportError:
|
|
207
|
+
pytest.skip("opencv-python not installed")
|
|
208
|
+
|
|
209
|
+
from massgen.tool._multimodal_tools.understand_video import understand_video
|
|
210
|
+
|
|
211
|
+
# Create a video with large dimensions (3000x4000)
|
|
212
|
+
video_path = temp_dir / "large_video.mp4"
|
|
213
|
+
self._create_test_video(3000, 4000, video_path, num_frames=10)
|
|
214
|
+
|
|
215
|
+
# Use real OpenAI API - should resize frames internally and succeed
|
|
216
|
+
result = await understand_video(
|
|
217
|
+
str(video_path),
|
|
218
|
+
num_frames=3,
|
|
219
|
+
prompt="Describe what you see in this test video in one sentence.",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Check that it succeeded
|
|
223
|
+
assert result.output_blocks is not None
|
|
224
|
+
import json
|
|
225
|
+
|
|
226
|
+
result_data = json.loads(result.output_blocks[0].data)
|
|
227
|
+
|
|
228
|
+
print("\n" + "=" * 80)
|
|
229
|
+
print("TEST: Video With Large Frames (3000x4000) - Frames Should Be Resized")
|
|
230
|
+
print("=" * 80)
|
|
231
|
+
print(json.dumps(result_data, indent=2))
|
|
232
|
+
print("=" * 80 + "\n")
|
|
233
|
+
|
|
234
|
+
assert result_data["success"] is True
|
|
235
|
+
|
|
236
|
+
@pytest.mark.asyncio
|
|
237
|
+
async def test_video_with_small_frames(self, temp_dir):
|
|
238
|
+
"""Test that video with small frame dimensions processes without resizing."""
|
|
239
|
+
try:
|
|
240
|
+
import cv2 # noqa: F401
|
|
241
|
+
except ImportError:
|
|
242
|
+
pytest.skip("opencv-python not installed")
|
|
243
|
+
|
|
244
|
+
from massgen.tool._multimodal_tools.understand_video import understand_video
|
|
245
|
+
|
|
246
|
+
# Create a video with small dimensions (640x480)
|
|
247
|
+
video_path = temp_dir / "small_video.mp4"
|
|
248
|
+
self._create_test_video(640, 480, video_path, num_frames=10)
|
|
249
|
+
|
|
250
|
+
# Use real OpenAI API
|
|
251
|
+
result = await understand_video(
|
|
252
|
+
str(video_path),
|
|
253
|
+
num_frames=3,
|
|
254
|
+
prompt="Describe what you see in this test video in one sentence.",
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Check that it succeeded
|
|
258
|
+
assert result.output_blocks is not None
|
|
259
|
+
import json
|
|
260
|
+
|
|
261
|
+
result_data = json.loads(result.output_blocks[0].data)
|
|
262
|
+
|
|
263
|
+
print("\n" + "=" * 80)
|
|
264
|
+
print("TEST: Video With Small Frames (640x480) - No Resize Needed")
|
|
265
|
+
print("=" * 80)
|
|
266
|
+
print(json.dumps(result_data, indent=2))
|
|
267
|
+
print("=" * 80 + "\n")
|
|
268
|
+
|
|
269
|
+
assert result_data["success"] is True
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class TestAudioSizeLimits:
|
|
273
|
+
"""Test suite for understand_audio file size limits."""
|
|
274
|
+
|
|
275
|
+
@pytest.fixture
|
|
276
|
+
def temp_dir(self):
|
|
277
|
+
"""Create a temporary directory for test files."""
|
|
278
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
279
|
+
yield Path(tmpdir)
|
|
280
|
+
|
|
281
|
+
def _create_test_audio(self, output_path: Path, duration_seconds: float = 1.0, sample_rate: int = 44100):
|
|
282
|
+
"""
|
|
283
|
+
Create a test audio file (WAV format).
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
output_path: Path to save the audio file
|
|
287
|
+
duration_seconds: Duration in seconds
|
|
288
|
+
sample_rate: Sample rate in Hz
|
|
289
|
+
"""
|
|
290
|
+
import wave
|
|
291
|
+
|
|
292
|
+
import numpy as np
|
|
293
|
+
|
|
294
|
+
# Generate a simple sine wave
|
|
295
|
+
frequency = 440.0 # A4 note
|
|
296
|
+
num_samples = int(sample_rate * duration_seconds)
|
|
297
|
+
t = np.linspace(0, duration_seconds, num_samples, False)
|
|
298
|
+
audio_data = np.sin(2 * np.pi * frequency * t)
|
|
299
|
+
|
|
300
|
+
# Convert to 16-bit PCM
|
|
301
|
+
audio_data = (audio_data * 32767).astype(np.int16)
|
|
302
|
+
|
|
303
|
+
# Write WAV file
|
|
304
|
+
with wave.open(str(output_path), "w") as wav_file:
|
|
305
|
+
wav_file.setnchannels(1) # Mono
|
|
306
|
+
wav_file.setsampwidth(2) # 16-bit
|
|
307
|
+
wav_file.setframerate(sample_rate)
|
|
308
|
+
wav_file.writeframes(audio_data.tobytes())
|
|
309
|
+
|
|
310
|
+
def _create_large_audio(self, output_path: Path, target_size_mb: float = 30):
|
|
311
|
+
"""
|
|
312
|
+
Create a large audio file exceeding size limits.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
output_path: Path to save the audio file
|
|
316
|
+
target_size_mb: Target size in megabytes
|
|
317
|
+
"""
|
|
318
|
+
# Calculate duration needed to achieve target size
|
|
319
|
+
# WAV: sample_rate * duration * 2 bytes (16-bit) * channels
|
|
320
|
+
sample_rate = 44100
|
|
321
|
+
bytes_per_second = sample_rate * 2 # 16-bit mono
|
|
322
|
+
duration_seconds = (target_size_mb * 1024 * 1024) / bytes_per_second
|
|
323
|
+
|
|
324
|
+
self._create_test_audio(output_path, duration_seconds=duration_seconds, sample_rate=sample_rate)
|
|
325
|
+
|
|
326
|
+
@pytest.mark.asyncio
|
|
327
|
+
async def test_audio_within_size_limit(self, temp_dir):
|
|
328
|
+
"""Test that audio files within size limit are accepted."""
|
|
329
|
+
from massgen.tool._multimodal_tools.understand_audio import understand_audio
|
|
330
|
+
|
|
331
|
+
# Create a small audio file (~1 second, ~88KB)
|
|
332
|
+
audio_path = temp_dir / "small_audio.wav"
|
|
333
|
+
self._create_test_audio(audio_path, duration_seconds=1.0)
|
|
334
|
+
|
|
335
|
+
file_size = audio_path.stat().st_size
|
|
336
|
+
assert file_size < 25 * 1024 * 1024, "Test audio should be under 25MB"
|
|
337
|
+
|
|
338
|
+
# Use real OpenAI API
|
|
339
|
+
result = await understand_audio([str(audio_path)])
|
|
340
|
+
|
|
341
|
+
# Check that it succeeded
|
|
342
|
+
assert result.output_blocks is not None
|
|
343
|
+
import json
|
|
344
|
+
|
|
345
|
+
result_data = json.loads(result.output_blocks[0].data)
|
|
346
|
+
|
|
347
|
+
print("\n" + "=" * 80)
|
|
348
|
+
print(f"TEST: Audio Within Size Limit (~{file_size/1024/1024:.2f}MB)")
|
|
349
|
+
print("=" * 80)
|
|
350
|
+
print(json.dumps(result_data, indent=2))
|
|
351
|
+
print("=" * 80 + "\n")
|
|
352
|
+
|
|
353
|
+
assert result_data["success"] is True
|
|
354
|
+
|
|
355
|
+
@pytest.mark.asyncio
|
|
356
|
+
async def test_audio_exceeds_size_limit(self, temp_dir):
|
|
357
|
+
"""Test that audio files exceeding 25MB limit are rejected."""
|
|
358
|
+
from massgen.tool._multimodal_tools.understand_audio import understand_audio
|
|
359
|
+
|
|
360
|
+
# Create a large audio file (~30MB)
|
|
361
|
+
audio_path = temp_dir / "large_audio.wav"
|
|
362
|
+
self._create_large_audio(audio_path, target_size_mb=30)
|
|
363
|
+
|
|
364
|
+
file_size = audio_path.stat().st_size
|
|
365
|
+
assert file_size > 25 * 1024 * 1024, f"Test audio should exceed 25MB, got {file_size / 1024 / 1024:.1f}MB"
|
|
366
|
+
|
|
367
|
+
# This should fail validation before calling OpenAI
|
|
368
|
+
result = await understand_audio([str(audio_path)])
|
|
369
|
+
|
|
370
|
+
# Check that it failed due to size limit
|
|
371
|
+
assert result.output_blocks is not None
|
|
372
|
+
import json
|
|
373
|
+
|
|
374
|
+
result_data = json.loads(result.output_blocks[0].data)
|
|
375
|
+
|
|
376
|
+
print("\n" + "=" * 80)
|
|
377
|
+
print(f"TEST: Audio Exceeds Size Limit ({file_size/1024/1024:.1f}MB > 25MB)")
|
|
378
|
+
print("=" * 80)
|
|
379
|
+
print(json.dumps(result_data, indent=2))
|
|
380
|
+
print("=" * 80 + "\n")
|
|
381
|
+
|
|
382
|
+
assert result_data["success"] is False
|
|
383
|
+
assert "too large" in result_data["error"].lower()
|
|
384
|
+
assert "25MB" in result_data["error"]
|
|
385
|
+
|
|
386
|
+
def test_audio_size_check(self, temp_dir):
|
|
387
|
+
"""Test audio file size checking logic."""
|
|
388
|
+
# Create audio files of different sizes
|
|
389
|
+
test_cases = [
|
|
390
|
+
(1.0, True), # 1 second (~88KB) - should pass
|
|
391
|
+
(10.0, True), # 10 seconds (~880KB) - should pass
|
|
392
|
+
]
|
|
393
|
+
|
|
394
|
+
for duration, should_pass in test_cases:
|
|
395
|
+
audio_path = temp_dir / f"audio_{duration}s.wav"
|
|
396
|
+
self._create_test_audio(audio_path, duration_seconds=duration)
|
|
397
|
+
|
|
398
|
+
file_size = audio_path.stat().st_size
|
|
399
|
+
max_size = 25 * 1024 * 1024
|
|
400
|
+
|
|
401
|
+
passes = file_size <= max_size
|
|
402
|
+
|
|
403
|
+
assert passes == should_pass, f"Size check failed for {duration}s audio ({file_size / 1024 / 1024:.1f}MB): " f"expected pass={should_pass}, got {passes}"
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
if __name__ == "__main__":
|
|
407
|
+
pytest.main([__file__, "-v"])
|
massgen/tool/_manager.py
CHANGED
|
@@ -312,9 +312,14 @@ class ToolManager:
|
|
|
312
312
|
return
|
|
313
313
|
|
|
314
314
|
tool_entry = self.registered_tools[tool_name]
|
|
315
|
+
|
|
316
|
+
# Merge parameters: model input first, then preset params override
|
|
317
|
+
# This ensures preset_params (like agent_cwd) always take precedence
|
|
318
|
+
# and won't be overridden by null values from model
|
|
319
|
+
model_input = tool_request.get("input", {}) or {}
|
|
315
320
|
exec_kwargs = {
|
|
316
|
-
**
|
|
317
|
-
**
|
|
321
|
+
**model_input,
|
|
322
|
+
**tool_entry.preset_params, # preset_params override model input
|
|
318
323
|
}
|
|
319
324
|
|
|
320
325
|
# Prepare post-processor if exists
|