fix: add task log
This commit is contained in:
@@ -42,7 +42,6 @@ class TaskBase(SQLModel):
|
|||||||
status: str # 'pending', 'processing', 'completed', 'failed'
|
status: str # 'pending', 'processing', 'completed', 'failed'
|
||||||
progress: int = 0 # 0-100
|
progress: int = 0 # 0-100
|
||||||
message: Optional[str] = None
|
message: Optional[str] = None
|
||||||
logs: List[str] = Field(default=[], sa_column=Column(JSON))
|
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
result: Dict = Field(default={}, sa_column=Column(JSON))
|
result: Dict = Field(default={}, sa_column=Column(JSON))
|
||||||
@@ -88,6 +87,7 @@ class Task(TaskBase, table=True):
|
|||||||
project_id: str = Field(foreign_key="project.id")
|
project_id: str = Field(foreign_key="project.id")
|
||||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
logs: List[str] = Field(default=[], sa_column=Column(JSON))
|
||||||
|
|
||||||
project: Project = Relationship(back_populates="tasks")
|
project: Project = Relationship(back_populates="tasks")
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import traceback
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
||||||
@@ -635,11 +636,19 @@ Ensure the character's expression and pose reflect their personality: {personali
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
log_task_event(session, task_id, f"Calling AI service for character {char.name}...")
|
log_task_event(session, task_id, f"Calling AI service for character {char.name}...")
|
||||||
image_bytes = ai.generate_image(
|
start_time = time.time()
|
||||||
prompt,
|
try:
|
||||||
aspect_ratio=char.project.aspect_ratio or "16:9",
|
image_bytes = ai.generate_image(
|
||||||
resolution=char.project.resolution or "2K"
|
prompt,
|
||||||
)
|
aspect_ratio=char.project.aspect_ratio or "16:9",
|
||||||
|
resolution=char.project.resolution or "2K"
|
||||||
|
)
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
log_task_event(session, task_id, f"AI generation finished in {elapsed:.2f}s. Image size: {len(image_bytes)} bytes.")
|
||||||
|
except Exception as e:
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
log_task_event(session, task_id, f"AI generation failed after {elapsed:.2f}s: {str(e)}")
|
||||||
|
raise e
|
||||||
|
|
||||||
relative_url = save_generated_image(session, char.project_id, "character", char.id, image_bytes)
|
relative_url = save_generated_image(session, char.project_id, "character", char.id, image_bytes)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import logging
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
from app.models.models import ModelConfig
|
from app.models.models import ModelConfig
|
||||||
@@ -8,6 +9,8 @@ from google.genai import types
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class AIService:
|
class AIService:
|
||||||
def __init__(self, session: Session):
|
def __init__(self, session: Session):
|
||||||
self.session = session
|
self.session = session
|
||||||
@@ -30,15 +33,20 @@ class AIService:
|
|||||||
|
|
||||||
full_prompt = f"{system_prompt}\n\nUser Input: {user_input}\n\nPlease generate the full storyboard in JSON format as requested."
|
full_prompt = f"{system_prompt}\n\nUser Input: {user_input}\n\nPlease generate the full storyboard in JSON format as requested."
|
||||||
|
|
||||||
try:
|
max_retries = 3
|
||||||
response = client.models.generate_content(
|
for attempt in range(max_retries):
|
||||||
model=model_name,
|
try:
|
||||||
contents=full_prompt
|
response = client.models.generate_content(
|
||||||
)
|
model=model_name,
|
||||||
return response.text
|
contents=full_prompt
|
||||||
except Exception as e:
|
)
|
||||||
print(f"Error generating storyboard: {e}")
|
return response.text
|
||||||
raise e
|
except Exception as e:
|
||||||
|
logger.error(f"Error generating storyboard (Attempt {attempt + 1}/{max_retries}): {e}")
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
time.sleep(2 ** attempt)
|
||||||
|
continue
|
||||||
|
raise e
|
||||||
|
|
||||||
def generate_image(self, prompt: str, context_images: List[str] = None, aspect_ratio: str = "16:9", resolution: str = "2K") -> bytes:
|
def generate_image(self, prompt: str, context_images: List[str] = None, aspect_ratio: str = "16:9", resolution: str = "2K") -> bytes:
|
||||||
client, model_name = self._get_client("image")
|
client, model_name = self._get_client("image")
|
||||||
@@ -51,15 +59,20 @@ class AIService:
|
|||||||
prev_img = Image.open(img_path)
|
prev_img = Image.open(img_path)
|
||||||
contents.append(prev_img)
|
contents.append(prev_img)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to load context image {img_path}: {e}")
|
logger.warning(f"Failed to load context image {img_path}: {e}")
|
||||||
else:
|
else:
|
||||||
# Log missing context image but don't fail, just skip it
|
# Log missing context image but don't fail, just skip it
|
||||||
print(f"Warning: Context image not found at {img_path}, skipping.")
|
logger.warning(f"Warning: Context image not found at {img_path}, skipping.")
|
||||||
|
|
||||||
# Retry loop
|
# Retry loop
|
||||||
max_retries = 3
|
max_retries = 3
|
||||||
for attempt in range(max_retries):
|
for attempt in range(max_retries):
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"DEBUG: Starting image generation attempt {attempt + 1}/{max_retries} with model {model_name}...")
|
||||||
|
logger.info(f"DEBUG: Prompt length: {len(prompt)}")
|
||||||
|
if context_images:
|
||||||
|
logger.info(f"DEBUG: Context images count: {len(context_images)}")
|
||||||
|
|
||||||
response = client.models.generate_content(
|
response = client.models.generate_content(
|
||||||
model=model_name,
|
model=model_name,
|
||||||
contents=contents,
|
contents=contents,
|
||||||
@@ -70,28 +83,30 @@ class AIService:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
logger.info(f"DEBUG: Generation API call completed for attempt {attempt + 1}")
|
||||||
|
|
||||||
if response.parts:
|
if response.parts:
|
||||||
for part in response.parts:
|
for part in response.parts:
|
||||||
if part.inline_data is not None:
|
if part.inline_data is not None:
|
||||||
image_data = part.inline_data.data
|
image_data = part.inline_data.data
|
||||||
if len(image_data) > 0:
|
if len(image_data) > 0:
|
||||||
|
logger.info(f"DEBUG: Successfully received image data ({len(image_data)} bytes)")
|
||||||
return image_data
|
return image_data
|
||||||
else:
|
else:
|
||||||
print(f"Warning: Received empty image data on attempt {attempt + 1}")
|
logger.warning(f"Warning: Received empty image data on attempt {attempt + 1}")
|
||||||
|
|
||||||
# Check for text refusal/error
|
# Check for text refusal/error
|
||||||
if response.text:
|
if response.text:
|
||||||
print(f"Model response text (no image): {response.text}")
|
logger.warning(f"Model response text (no image): {response.text}")
|
||||||
|
|
||||||
print(f"Attempt {attempt + 1} failed: No valid image data found in response.")
|
logger.warning(f"Attempt {attempt + 1} failed: No valid image data found in response.")
|
||||||
if attempt == max_retries - 1:
|
if attempt == max_retries - 1:
|
||||||
raise ValueError(f"No image found in response after {max_retries} retries. Last response: {response.text if response.text else 'Empty'}")
|
raise ValueError(f"No image found in response after {max_retries} retries. Last response: {response.text if response.text else 'Empty'}")
|
||||||
|
|
||||||
time.sleep(2 ** attempt)
|
time.sleep(2 ** attempt)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error generating image (Attempt {attempt + 1}/{max_retries}): {e}")
|
logger.error(f"Error generating image (Attempt {attempt + 1}/{max_retries}): {e}")
|
||||||
if attempt < max_retries - 1:
|
if attempt < max_retries - 1:
|
||||||
time.sleep(2 ** attempt)
|
time.sleep(2 ** attempt)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -59,10 +59,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Prompt (Existing) -->
|
<!-- Prompt (Existing) -->
|
||||||
<div class="detail-group mt-3" v-if="item.data.prompt">
|
<div class="detail-group mt-3">
|
||||||
<label>Full Prompt:</label>
|
<label>Full Prompt:</label>
|
||||||
<div class="detail-content prompt-text">
|
<div class="detail-content prompt-text">
|
||||||
{{ item.data.prompt }}
|
{{ item.data.prompt || 'No prompt set' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user