Gaurav vashistha commited on
Commit
dae328a
·
1 Parent(s): 256d59e

Fix infinite loop with robust error handling in agent.py

Browse files
Files changed (1) hide show
  1. agent.py +22 -13
agent.py CHANGED
@@ -13,7 +13,6 @@ logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
  def get_file_hash(filepath):
16
- """Calculates MD5 hash of file to prevent duplicate uploads."""
17
  hash_md5 = hashlib.md5()
18
  with open(filepath, "rb") as f:
19
  for chunk in iter(lambda: f.read(4096), b""):
@@ -21,7 +20,6 @@ def get_file_hash(filepath):
21
  return hash_md5.hexdigest()
22
 
23
  def get_or_upload_file(client, filepath):
24
- """Uploads file only if it doesn't already exist in Gemini (deduplication)."""
25
  file_hash = get_file_hash(filepath)
26
  try:
27
  for f in client.files.list(config={'page_size': 50}):
@@ -46,7 +44,6 @@ def analyze_only(path_a, path_c, job_id=None):
46
  time.sleep(2)
47
  file_a = client.files.get(name=file_a.name)
48
  file_c = client.files.get(name=file_c.name)
49
-
50
  prompt = """
51
  You are a VFX Director. Analyze Video A and Video C.
52
  Return a JSON object with exactly these keys:
@@ -55,19 +52,17 @@ def analyze_only(path_a, path_c, job_id=None):
55
  "analysis_c": "Brief description of Video C's lighting, subject, and camera movement.",
56
  "visual_prompt_b": "A surreal, seamless morphing prompt that transforms A into C. DO NOT use words like 'dissolve' or 'cut'. Focus on shape and texture transformation."
57
  }
58
- DO NOT use markdown code blocks. Return RAW JSON only.
59
  """
60
  update_job_status(job_id, "analyzing", 30, "Director drafting creative morph...")
61
 
62
- # Request JSON output
63
  res = client.models.generate_content(
64
  model="gemini-2.0-flash-exp",
65
  contents=[prompt, file_a, file_c],
66
  config=types.GenerateContentConfig(response_mime_type="application/json")
67
  )
68
 
69
- # Robust JSON Cleaning
70
  text = res.text.strip()
 
71
  if text.startswith("```json"): text = text[7:]
72
  elif text.startswith("```"): text = text[3:]
73
  if text.endswith("```"): text = text[:-3]
@@ -75,10 +70,8 @@ def analyze_only(path_a, path_c, job_id=None):
75
 
76
  try:
77
  data = json.loads(text)
78
- # CRITICAL FIX: Handle list response
79
  if isinstance(data, list):
80
  data = data[0] if len(data) > 0 else {}
81
-
82
  except json.JSONDecodeError:
83
  logger.warning("JSON Decode failed, using raw text fallback")
84
  return {
@@ -87,11 +80,10 @@ def analyze_only(path_a, path_c, job_id=None):
87
  "prompt": text,
88
  "status": "success"
89
  }
90
-
91
  return {
92
  "analysis_a": data.get("analysis_a", ""),
93
  "analysis_c": data.get("analysis_c", ""),
94
- "prompt": data.get("visual_prompt_b", text), # Fallback to full text if key missing
95
  "status": "success"
96
  }
97
  except Exception as e:
@@ -104,6 +96,7 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
104
  if neg:
105
  full_prompt += f" --no {neg}"
106
 
 
107
  try:
108
  if Settings.GCP_PROJECT_ID:
109
  client = genai.Client(vertexai=True, project=Settings.GCP_PROJECT_ID, location=Settings.GCP_LOCATION)
@@ -133,9 +126,25 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
133
  update_job_status(job_id, "completed", 100, "Done!", video_url=bridge_path, merged_video_url=final_output)
134
  except Exception as e:
135
  logger.error(f"Stitch error: {e}")
 
136
  update_job_status(job_id, "completed", 100, "Stitch failed, showing bridge.", video_url=bridge_path)
137
- return
 
 
 
 
138
  except Exception as e:
 
139
  update_job_status(job_id, "error", 0, f"Error: {e}")
140
- return
141
- update_job_status(job_id, "error", 0, "Generation failed.")
 
 
 
 
 
 
 
 
 
 
 
13
  logger = logging.getLogger(__name__)
14
 
15
  def get_file_hash(filepath):
 
16
  hash_md5 = hashlib.md5()
17
  with open(filepath, "rb") as f:
18
  for chunk in iter(lambda: f.read(4096), b""):
 
20
  return hash_md5.hexdigest()
21
 
22
  def get_or_upload_file(client, filepath):
 
23
  file_hash = get_file_hash(filepath)
24
  try:
25
  for f in client.files.list(config={'page_size': 50}):
 
44
  time.sleep(2)
45
  file_a = client.files.get(name=file_a.name)
46
  file_c = client.files.get(name=file_c.name)
 
47
  prompt = """
48
  You are a VFX Director. Analyze Video A and Video C.
49
  Return a JSON object with exactly these keys:
 
52
  "analysis_c": "Brief description of Video C's lighting, subject, and camera movement.",
53
  "visual_prompt_b": "A surreal, seamless morphing prompt that transforms A into C. DO NOT use words like 'dissolve' or 'cut'. Focus on shape and texture transformation."
54
  }
 
55
  """
56
  update_job_status(job_id, "analyzing", 30, "Director drafting creative morph...")
57
 
 
58
  res = client.models.generate_content(
59
  model="gemini-2.0-flash-exp",
60
  contents=[prompt, file_a, file_c],
61
  config=types.GenerateContentConfig(response_mime_type="application/json")
62
  )
63
 
 
64
  text = res.text.strip()
65
+ # Robust markdown stripping
66
  if text.startswith("```json"): text = text[7:]
67
  elif text.startswith("```"): text = text[3:]
68
  if text.endswith("```"): text = text[:-3]
 
70
 
71
  try:
72
  data = json.loads(text)
 
73
  if isinstance(data, list):
74
  data = data[0] if len(data) > 0 else {}
 
75
  except json.JSONDecodeError:
76
  logger.warning("JSON Decode failed, using raw text fallback")
77
  return {
 
80
  "prompt": text,
81
  "status": "success"
82
  }
 
83
  return {
84
  "analysis_a": data.get("analysis_a", ""),
85
  "analysis_c": data.get("analysis_c", ""),
86
+ "prompt": data.get("visual_prompt_b", text),
87
  "status": "success"
88
  }
89
  except Exception as e:
 
96
  if neg:
97
  full_prompt += f" --no {neg}"
98
 
99
+ job_failed = False
100
  try:
101
  if Settings.GCP_PROJECT_ID:
102
  client = genai.Client(vertexai=True, project=Settings.GCP_PROJECT_ID, location=Settings.GCP_LOCATION)
 
126
  update_job_status(job_id, "completed", 100, "Done!", video_url=bridge_path, merged_video_url=final_output)
127
  except Exception as e:
128
  logger.error(f"Stitch error: {e}")
129
+ # Still mark as completed, just without the merged video
130
  update_job_status(job_id, "completed", 100, "Stitch failed, showing bridge.", video_url=bridge_path)
131
+ return # Exit successfully
132
+ else:
133
+ raise Exception("No video generated by Veo model.")
134
+ else:
135
+ raise Exception("GCP_PROJECT_ID is not set in environment variables.")
136
  except Exception as e:
137
+ logger.error(f"Generation failed: {e}")
138
  update_job_status(job_id, "error", 0, f"Error: {e}")
139
+ job_failed = True
140
+ finally:
141
+ # Ensure job is not left in a non-terminal state if it didn't complete successfully
142
+ if not job_failed:
143
+ # Check if status was set to 'completed'
144
+ try:
145
+ with open(f"outputs/{job_id}.json", "r") as f:
146
+ status_data = json.load(f)
147
+ if status_data.get("status") != "completed":
148
+ update_job_status(job_id, "error", 0, "Job finished without completion status.")
149
+ except FileNotFoundError:
150
+ update_job_status(job_id, "error", 0, "Job file lost during processing.")