Gaurav vashistha commited on
Commit
2359eb3
·
1 Parent(s): d3a9776

Fix operations.get() API syntax: use positional arg instead of keyword

Browse files
Files changed (1) hide show
  1. agent.py +35 -40
agent.py CHANGED
@@ -14,6 +14,7 @@ logger = logging.getLogger(__name__)
14
 
15
 
16
  def get_file_hash(filepath):
 
17
  hash_md5 = hashlib.md5()
18
  with open(filepath, "rb") as f:
19
  for chunk in iter(lambda: f.read(4096), b""):
@@ -22,6 +23,7 @@ def get_file_hash(filepath):
22
 
23
 
24
  def get_or_upload_file(client, filepath):
 
25
  file_hash = get_file_hash(filepath)
26
  try:
27
  for f in client.files.list(config={'page_size': 50}):
@@ -37,6 +39,7 @@ def get_or_upload_file(client, filepath):
37
  def analyze_only(path_a, path_c, job_id=None):
38
  update_job_status(job_id, "analyzing", 10, "Director checking file cache...")
39
  client = genai.Client(api_key=Settings.GOOGLE_API_KEY)
 
40
  try:
41
  file_a = get_or_upload_file(client, path_a)
42
  file_c = get_or_upload_file(client, path_c)
@@ -53,27 +56,29 @@ def analyze_only(path_a, path_c, job_id=None):
53
  {
54
  "analysis_a": "Brief description of Video A",
55
  "analysis_c": "Brief description of Video C",
56
- "visual_prompt_b": "A surreal, seamless morphing prompt that transforms A into C."
57
  }
58
  """
59
  update_job_status(job_id, "analyzing", 30, "Director drafting creative morph...")
 
60
  res = client.models.generate_content(
61
  model="gemini-2.0-flash-exp",
62
  contents=[prompt, file_a, file_c],
63
  config=types.GenerateContentConfig(response_mime_type="application/json")
64
  )
 
65
  text = res.text.strip()
66
  if text.startswith("```json"): text = text[7:]
67
  elif text.startswith("```"): text = text[3:]
68
  if text.endswith("```"): text = text[:-3]
 
69
 
70
- try:
71
- data = json.loads(text.strip())
72
- except:
73
- data = {}
74
- if isinstance(data, list):
75
- data = data[0] if len(data) > 0 else {}
76
-
77
  return {
78
  "analysis_a": data.get("analysis_a", ""),
79
  "analysis_c": data.get("analysis_c", ""),
@@ -96,21 +101,20 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
96
  if Settings.GCP_PROJECT_ID:
97
  client = genai.Client(vertexai=True, project=Settings.GCP_PROJECT_ID, location=Settings.GCP_LOCATION)
98
 
99
- # Start Job
100
  op = client.models.generate_videos(
101
  model='veo-3.1-generate-preview',
102
  prompt=full_prompt,
103
  config=types.GenerateVideosConfig(number_of_videos=1)
104
  )
105
 
106
- logger.info(f"Job started. Initial OP: {op}")
107
- # UNIVERSAL POLLING LOOP
108
  start_time = time.time()
109
  while True:
110
- if time.time() - start_time > 300: # 5 min timeout
111
- raise Exception("Generation timed out (5m).")
112
 
113
- # 1. Check for 'done' status (Object or Dict)
114
  is_done = False
115
  if hasattr(op, 'done'):
116
  is_done = op.done
@@ -118,30 +122,29 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
118
  is_done = True
119
 
120
  if is_done:
121
- logger.info("Job reported DONE.")
122
  break
123
 
124
  logger.info("Waiting for Veo...")
125
  time.sleep(10)
126
 
127
- # 2. Refresh Operation
128
  try:
129
  op_name = op.name if hasattr(op, 'name') else op.get('name')
130
  if op_name:
131
- # Fetch fresh status
132
- op = client.operations.get(name=op_name)
133
- except Exception as e:
134
- logger.warning(f"Refresh warning: {e}")
135
 
136
- # 3. Retrieve Result
137
- # Handle Object vs Dict vs Property vs Method
138
  result = None
139
  if hasattr(op, 'result'):
 
140
  result = op.result() if callable(op.result) else op.result
141
  elif isinstance(op, dict):
142
  result = op.get('result')
143
-
144
- # 4. Extract Video
145
  generated_videos = None
146
  if result:
147
  if hasattr(result, 'generated_videos'):
@@ -150,11 +153,10 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
150
  generated_videos = result.get('generated_videos')
151
 
152
  if generated_videos:
153
- # Handle list access (Object or Dict items)
154
  vid = generated_videos[0]
155
  bridge_path = None
156
 
157
- # Extract URI/Bytes
158
  uri = getattr(vid.video, 'uri', None) if hasattr(vid, 'video') else vid.get('video', {}).get('uri')
159
  video_bytes = getattr(vid.video, 'video_bytes', None) if hasattr(vid, 'video') else vid.get('video', {}).get('video_bytes')
160
 
@@ -165,29 +167,22 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
165
  bridge_path = save_video_bytes(video_bytes)
166
 
167
  if bridge_path:
168
- # STITCHING SUCCESS PATH
169
- update_job_status(job_id, "stitching", 85, "Stitching Final Cut...", video_url=bridge_path)
170
- final_cut = os.path.join("outputs", f"{job_id}_merged_temp.mp4")
171
 
172
- # Run Stitcher (Will succeed if FFmpeg is installed)
173
  merged_path = stitch_videos(path_a, bridge_path, path_c, final_cut)
174
 
175
- if merged_path:
176
- msg = "Done! (Merged Successfully)"
177
- logger.info("Stitch SUCCESS.")
178
- else:
179
- msg = "Done! (Bridge Only - Stitch Skipped)"
180
- logger.warning("Stitch Skipped (Check FFmpeg).")
181
-
182
  update_job_status(job_id, "completed", 100, msg, video_url=bridge_path, merged_video_url=merged_path)
183
  return
184
  else:
185
- raise Exception("Veo finished but returned no videos.")
186
  else:
187
  raise Exception("GCP_PROJECT_ID not set.")
188
 
189
  except Exception as e:
190
- logger.error(f"Gen Fatal Error: {e}")
191
  update_job_status(job_id, "error", 0, f"Error: {e}")
192
  job_failed = True
193
  finally:
@@ -195,6 +190,6 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
195
  try:
196
  with open(f"outputs/{job_id}.json", "r") as f:
197
  if json.load(f).get("status") not in ["completed", "error"]:
198
- update_job_status(job_id, "error", 0, "Job Process Terminated.")
199
  except:
200
  pass
 
14
 
15
 
16
  def get_file_hash(filepath):
17
+ """Calculates MD5 hash of file to prevent duplicate uploads."""
18
  hash_md5 = hashlib.md5()
19
  with open(filepath, "rb") as f:
20
  for chunk in iter(lambda: f.read(4096), b""):
 
23
 
24
 
25
  def get_or_upload_file(client, filepath):
26
+ """Uploads file only if it doesn't already exist in Gemini (deduplication)."""
27
  file_hash = get_file_hash(filepath)
28
  try:
29
  for f in client.files.list(config={'page_size': 50}):
 
39
  def analyze_only(path_a, path_c, job_id=None):
40
  update_job_status(job_id, "analyzing", 10, "Director checking file cache...")
41
  client = genai.Client(api_key=Settings.GOOGLE_API_KEY)
42
+
43
  try:
44
  file_a = get_or_upload_file(client, path_a)
45
  file_c = get_or_upload_file(client, path_c)
 
56
  {
57
  "analysis_a": "Brief description of Video A",
58
  "analysis_c": "Brief description of Video C",
59
+ "visual_prompt_b": "A surreal, seamless morphing prompt that transforms A into C. DO NOT use words like 'dissolve' or 'cut'."
60
  }
61
  """
62
  update_job_status(job_id, "analyzing", 30, "Director drafting creative morph...")
63
+
64
  res = client.models.generate_content(
65
  model="gemini-2.0-flash-exp",
66
  contents=[prompt, file_a, file_c],
67
  config=types.GenerateContentConfig(response_mime_type="application/json")
68
  )
69
+
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]
74
+ text = text.strip()
75
 
76
+ try:
77
+ data = json.loads(text)
78
+ if isinstance(data, list): data = data[0] if len(data) > 0 else {}
79
+ except json.JSONDecodeError:
80
+ return {"prompt": text, "status": "success"}
81
+
 
82
  return {
83
  "analysis_a": data.get("analysis_a", ""),
84
  "analysis_c": data.get("analysis_c", ""),
 
101
  if Settings.GCP_PROJECT_ID:
102
  client = genai.Client(vertexai=True, project=Settings.GCP_PROJECT_ID, location=Settings.GCP_LOCATION)
103
 
104
+ # 1. Start Job
105
  op = client.models.generate_videos(
106
  model='veo-3.1-generate-preview',
107
  prompt=full_prompt,
108
  config=types.GenerateVideosConfig(number_of_videos=1)
109
  )
110
 
111
+ # 2. ACTIVE POLLING LOOP
 
112
  start_time = time.time()
113
  while True:
114
+ if time.time() - start_time > 180: # 3 min timeout
115
+ raise Exception("Generation timed out.")
116
 
117
+ # Check completion
118
  is_done = False
119
  if hasattr(op, 'done'):
120
  is_done = op.done
 
122
  is_done = True
123
 
124
  if is_done:
 
125
  break
126
 
127
  logger.info("Waiting for Veo...")
128
  time.sleep(10)
129
 
130
+ # 3. FORCE REFRESH (Fixed Syntax)
131
  try:
132
  op_name = op.name if hasattr(op, 'name') else op.get('name')
133
  if op_name:
134
+ # FIX: Use positional argument instead of 'name=' keyword
135
+ op = client.operations.get(op_name)
136
+ except Exception as refresh_err:
137
+ logger.warning(f"Refresh warning: {refresh_err}")
138
 
139
+ # 4. Get Result
 
140
  result = None
141
  if hasattr(op, 'result'):
142
+ # Safely handle method vs property
143
  result = op.result() if callable(op.result) else op.result
144
  elif isinstance(op, dict):
145
  result = op.get('result')
146
+
147
+ # 5. Extract Video
148
  generated_videos = None
149
  if result:
150
  if hasattr(result, 'generated_videos'):
 
153
  generated_videos = result.get('generated_videos')
154
 
155
  if generated_videos:
 
156
  vid = generated_videos[0]
157
  bridge_path = None
158
 
159
+ # Handle Object vs Dict access
160
  uri = getattr(vid.video, 'uri', None) if hasattr(vid, 'video') else vid.get('video', {}).get('uri')
161
  video_bytes = getattr(vid.video, 'video_bytes', None) if hasattr(vid, 'video') else vid.get('video', {}).get('video_bytes')
162
 
 
167
  bridge_path = save_video_bytes(video_bytes)
168
 
169
  if bridge_path:
170
+ # 6. STITCHING (With Fallback)
171
+ update_job_status(job_id, "stitching", 80, "Checking Stitch Capability...", video_url=bridge_path)
 
172
 
173
+ final_cut = os.path.join("outputs", f"{job_id}_merged_temp.mp4")
174
  merged_path = stitch_videos(path_a, bridge_path, path_c, final_cut)
175
 
176
+ msg = "Done! (Merged)" if merged_path else "Done! (Bridge Only)"
 
 
 
 
 
 
177
  update_job_status(job_id, "completed", 100, msg, video_url=bridge_path, merged_video_url=merged_path)
178
  return
179
  else:
180
+ raise Exception("Veo returned no videos.")
181
  else:
182
  raise Exception("GCP_PROJECT_ID not set.")
183
 
184
  except Exception as e:
185
+ logger.error(f"Gen Fatal: {e}")
186
  update_job_status(job_id, "error", 0, f"Error: {e}")
187
  job_failed = True
188
  finally:
 
190
  try:
191
  with open(f"outputs/{job_id}.json", "r") as f:
192
  if json.load(f).get("status") not in ["completed", "error"]:
193
+ update_job_status(job_id, "error", 0, "Job timed out.")
194
  except:
195
  pass