Spaces:
Running
on
Zero
Running
on
Zero
feat: Enhance get_completion and update functions with error handling and command display for FFMPEG execution
Browse files
app.py
CHANGED
@@ -96,7 +96,7 @@ def get_files_infos(files):
|
|
96 |
return results
|
97 |
|
98 |
|
99 |
-
def get_completion(prompt, files_info, top_p, temperature, model_choice):
|
100 |
# Create table header
|
101 |
files_info_string = "| Type | Name | Dimensions | Duration | Audio Channels |\n"
|
102 |
files_info_string += "|------|------|------------|-----------|--------|\n"
|
@@ -115,6 +115,35 @@ def get_completion(prompt, files_info, top_p, temperature, model_choice):
|
|
115 |
|
116 |
files_info_string += f"| {file_info['type']} | {file_info['name']} | {dimensions} | {duration} | {audio} |\n"
|
117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
messages = [
|
119 |
{
|
120 |
"role": "system",
|
@@ -145,20 +174,7 @@ Remember: Simpler is better. Only use advanced ffmpeg features if absolutely nec
|
|
145 |
},
|
146 |
{
|
147 |
"role": "user",
|
148 |
-
"content":
|
149 |
-
The current assets and objective follow.
|
150 |
-
|
151 |
-
AVAILABLE ASSETS LIST:
|
152 |
-
|
153 |
-
{files_info_string}
|
154 |
-
|
155 |
-
OBJECTIVE: {prompt} and output at "output.mp4"
|
156 |
-
|
157 |
-
First, think step-by-step about what I'm asking for and reformulate it into a clear technical specification.
|
158 |
-
Then provide the FFMPEG command that will accomplish this task.
|
159 |
-
|
160 |
-
YOUR RESPONSE:
|
161 |
-
""",
|
162 |
},
|
163 |
]
|
164 |
try:
|
@@ -286,14 +302,18 @@ def update(
|
|
286 |
raise gr.Error("Please make sure all files are less than 100MB in size.")
|
287 |
|
288 |
attempts = 0
|
|
|
|
|
|
|
|
|
289 |
while attempts < 2:
|
290 |
-
print("ATTEMPT", attempts)
|
291 |
try:
|
292 |
command_string = get_completion(
|
293 |
-
prompt, files_info, top_p, temperature, model_choice
|
294 |
)
|
295 |
print(
|
296 |
-
f"""///
|
297 |
)
|
298 |
|
299 |
# split command string into list of arguments
|
@@ -308,35 +328,77 @@ def update(
|
|
308 |
shutil.copy(file_path, Path(temp_dir) / sanitized_name)
|
309 |
|
310 |
# test if ffmpeg command is valid dry run
|
311 |
-
|
312 |
args + ["-f", "null", "-"],
|
313 |
stderr=subprocess.PIPE,
|
314 |
text=True,
|
315 |
cwd=temp_dir,
|
316 |
)
|
317 |
-
|
|
|
|
|
|
|
|
|
318 |
print("Command is valid.")
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
else:
|
320 |
print("Command is not valid. Error output:")
|
321 |
-
print(
|
322 |
-
|
323 |
-
|
324 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
|
326 |
output_file_name = f"output_{uuid.uuid4()}.mp4"
|
327 |
output_file_path = str((Path(temp_dir) / output_file_name).resolve())
|
328 |
execute_ffmpeg_command(args, temp_dir, output_file_path)
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
return output_file_path, gr.update(value=generated_command)
|
335 |
except Exception as e:
|
336 |
attempts += 1
|
337 |
if attempts >= 2:
|
338 |
print("FROM UPDATE", e)
|
339 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
|
341 |
|
342 |
# Create MCP-compatible interface
|
|
|
96 |
return results
|
97 |
|
98 |
|
99 |
+
def get_completion(prompt, files_info, top_p, temperature, model_choice, previous_error=None, previous_command=None):
|
100 |
# Create table header
|
101 |
files_info_string = "| Type | Name | Dimensions | Duration | Audio Channels |\n"
|
102 |
files_info_string += "|------|------|------------|-----------|--------|\n"
|
|
|
115 |
|
116 |
files_info_string += f"| {file_info['type']} | {file_info['name']} | {dimensions} | {duration} | {audio} |\n"
|
117 |
|
118 |
+
# Build the user message with optional error feedback
|
119 |
+
user_content = f"""Always output the media as video/mp4 and output file with "output.mp4".
|
120 |
+
The current assets and objective follow.
|
121 |
+
|
122 |
+
AVAILABLE ASSETS LIST:
|
123 |
+
|
124 |
+
{files_info_string}
|
125 |
+
|
126 |
+
OBJECTIVE: {prompt} and output at "output.mp4"
|
127 |
+
|
128 |
+
First, think step-by-step about what I'm asking for and reformulate it into a clear technical specification.
|
129 |
+
Then provide the FFMPEG command that will accomplish this task."""
|
130 |
+
|
131 |
+
# Add error feedback if this is a retry
|
132 |
+
if previous_error and previous_command:
|
133 |
+
user_content += f"""
|
134 |
+
|
135 |
+
IMPORTANT: This is a retry attempt. The previous command failed with the following error:
|
136 |
+
|
137 |
+
PREVIOUS COMMAND (FAILED):
|
138 |
+
{previous_command}
|
139 |
+
|
140 |
+
ERROR MESSAGE:
|
141 |
+
{previous_error}
|
142 |
+
|
143 |
+
Please analyze the error and generate a corrected command that addresses the specific issue."""
|
144 |
+
|
145 |
+
user_content += "\n\nYOUR RESPONSE:"
|
146 |
+
|
147 |
messages = [
|
148 |
{
|
149 |
"role": "system",
|
|
|
174 |
},
|
175 |
{
|
176 |
"role": "user",
|
177 |
+
"content": user_content,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
},
|
179 |
]
|
180 |
try:
|
|
|
302 |
raise gr.Error("Please make sure all files are less than 100MB in size.")
|
303 |
|
304 |
attempts = 0
|
305 |
+
command_attempts = []
|
306 |
+
previous_error = None
|
307 |
+
previous_command = None
|
308 |
+
|
309 |
while attempts < 2:
|
310 |
+
print("ATTEMPT", attempts + 1)
|
311 |
try:
|
312 |
command_string = get_completion(
|
313 |
+
prompt, files_info, top_p, temperature, model_choice, previous_error, previous_command
|
314 |
)
|
315 |
print(
|
316 |
+
f"""///PROMPT {prompt} \n\n/// START OF COMMAND ///:\n\n{command_string}\n\n/// END OF COMMAND ///\n\n"""
|
317 |
)
|
318 |
|
319 |
# split command string into list of arguments
|
|
|
328 |
shutil.copy(file_path, Path(temp_dir) / sanitized_name)
|
329 |
|
330 |
# test if ffmpeg command is valid dry run
|
331 |
+
ffmpeg_dry_run = subprocess.run(
|
332 |
args + ["-f", "null", "-"],
|
333 |
stderr=subprocess.PIPE,
|
334 |
text=True,
|
335 |
cwd=temp_dir,
|
336 |
)
|
337 |
+
|
338 |
+
# Extract command for display
|
339 |
+
command_for_display = f"ffmpeg {' '.join(args[1:])} -y output.mp4"
|
340 |
+
|
341 |
+
if ffmpeg_dry_run.returncode == 0:
|
342 |
print("Command is valid.")
|
343 |
+
# Add successful command to attempts
|
344 |
+
command_attempts.append({
|
345 |
+
"command": command_for_display,
|
346 |
+
"status": "β
Valid",
|
347 |
+
"attempt": attempts + 1
|
348 |
+
})
|
349 |
else:
|
350 |
print("Command is not valid. Error output:")
|
351 |
+
print(ffmpeg_dry_run.stderr)
|
352 |
+
|
353 |
+
# Add failed command to attempts with error
|
354 |
+
command_attempts.append({
|
355 |
+
"command": command_for_display,
|
356 |
+
"status": "β Invalid",
|
357 |
+
"error": ffmpeg_dry_run.stderr,
|
358 |
+
"attempt": attempts + 1
|
359 |
+
})
|
360 |
+
|
361 |
+
# Store error details for next retry
|
362 |
+
previous_error = ffmpeg_dry_run.stderr
|
363 |
+
previous_command = command_for_display
|
364 |
+
|
365 |
+
raise Exception(f"FFMPEG command validation failed: {ffmpeg_dry_run.stderr}")
|
366 |
|
367 |
output_file_name = f"output_{uuid.uuid4()}.mp4"
|
368 |
output_file_path = str((Path(temp_dir) / output_file_name).resolve())
|
369 |
execute_ffmpeg_command(args, temp_dir, output_file_path)
|
370 |
+
|
371 |
+
# Generate command display with all attempts
|
372 |
+
command_display = generate_command_display(command_attempts)
|
373 |
+
return output_file_path, gr.update(value=command_display)
|
374 |
+
|
|
|
375 |
except Exception as e:
|
376 |
attempts += 1
|
377 |
if attempts >= 2:
|
378 |
print("FROM UPDATE", e)
|
379 |
+
# Show all attempted commands even on final failure
|
380 |
+
command_display = generate_command_display(command_attempts)
|
381 |
+
command_display += f"\n\n### Final Error\nβ All attempts failed. Last error: {str(e)}"
|
382 |
+
return None, gr.update(value=command_display)
|
383 |
+
|
384 |
+
|
385 |
+
def generate_command_display(command_attempts):
|
386 |
+
"""Generate a markdown display of all command attempts"""
|
387 |
+
if not command_attempts:
|
388 |
+
return "### No commands generated"
|
389 |
+
|
390 |
+
display = "### Generated Commands\n\n"
|
391 |
+
|
392 |
+
for attempt in command_attempts:
|
393 |
+
display += f"**Attempt {attempt['attempt']}** {attempt['status']}\n"
|
394 |
+
display += f"```bash\n{attempt['command']}\n```\n"
|
395 |
+
|
396 |
+
if attempt['status'] == "β Invalid" and 'error' in attempt:
|
397 |
+
display += f"<details>\n<summary>π Error Details</summary>\n\n```\n{attempt['error']}\n```\n</details>\n\n"
|
398 |
+
else:
|
399 |
+
display += "\n"
|
400 |
+
|
401 |
+
return display
|
402 |
|
403 |
|
404 |
# Create MCP-compatible interface
|