AI Story Writer Backend Migration Complete, Frontend UI Components Added
This commit is contained in:
@@ -438,6 +438,45 @@ def llm_text_gen(prompt: str, system_prompt: Optional[str] = None, json_struct:
|
||||
current_tokens_before = 0
|
||||
new_tokens = 0
|
||||
|
||||
# Determine tracked tokens (after any safety capping)
|
||||
tracked_tokens_input = min(tokens_input, tokens_total)
|
||||
tracked_tokens_output = max(tokens_total - tracked_tokens_input, 0)
|
||||
|
||||
# Calculate and persist cost for this call
|
||||
try:
|
||||
cost_info = pricing.calculate_api_cost(
|
||||
provider=provider_enum,
|
||||
model_name=model,
|
||||
tokens_input=tracked_tokens_input,
|
||||
tokens_output=tracked_tokens_output,
|
||||
request_count=1
|
||||
)
|
||||
cost_total = cost_info.get('cost_total', 0.0) or 0.0
|
||||
except Exception as cost_error:
|
||||
cost_total = 0.0
|
||||
logger.error(f"[llm_text_gen] ❌ Failed to calculate API cost: {cost_error}", exc_info=True)
|
||||
|
||||
if cost_total > 0:
|
||||
logger.debug(f"[llm_text_gen] 💰 Calculated cost for {provider_name}: ${cost_total:.6f}")
|
||||
update_costs_query = text(f"""
|
||||
UPDATE usage_summaries
|
||||
SET {provider_name}_cost = COALESCE({provider_name}_cost, 0) + :cost,
|
||||
total_cost = COALESCE(total_cost, 0) + :cost
|
||||
WHERE user_id = :user_id AND billing_period = :period
|
||||
""")
|
||||
db_track.execute(update_costs_query, {
|
||||
'cost': cost_total,
|
||||
'user_id': user_id,
|
||||
'period': current_period
|
||||
})
|
||||
|
||||
# Keep ORM object in sync for logging/debugging
|
||||
current_provider_cost = getattr(summary, f"{provider_name}_cost", 0.0) or 0.0
|
||||
setattr(summary, f"{provider_name}_cost", current_provider_cost + cost_total)
|
||||
summary.total_cost = (summary.total_cost or 0.0) + cost_total
|
||||
else:
|
||||
logger.debug(f"[llm_text_gen] 💰 Cost calculation returned $0 for {provider_name} (tokens_input={tracked_tokens_input}, tokens_output={tracked_tokens_output})")
|
||||
|
||||
# Update totals using SQL UPDATE
|
||||
old_total_calls = summary.total_calls or 0
|
||||
old_total_tokens = summary.total_tokens or 0
|
||||
@@ -717,6 +756,39 @@ def llm_text_gen(prompt: str, system_prompt: Optional[str] = None, json_struct:
|
||||
current_tokens_before = 0
|
||||
new_tokens = 0
|
||||
|
||||
# Determine tracked tokens after any safety capping
|
||||
tracked_tokens_input = min(tokens_input, tokens_total)
|
||||
tracked_tokens_output = max(tokens_total - tracked_tokens_input, 0)
|
||||
|
||||
# Calculate and persist cost for this fallback call
|
||||
cost_total = 0.0
|
||||
try:
|
||||
cost_info = pricing.calculate_api_cost(
|
||||
provider=provider_enum,
|
||||
model_name=fallback_model,
|
||||
tokens_input=tracked_tokens_input,
|
||||
tokens_output=tracked_tokens_output,
|
||||
request_count=1
|
||||
)
|
||||
cost_total = cost_info.get('cost_total', 0.0) or 0.0
|
||||
except Exception as cost_error:
|
||||
logger.error(f"[llm_text_gen] ❌ Failed to calculate fallback cost: {cost_error}", exc_info=True)
|
||||
|
||||
if cost_total > 0:
|
||||
update_costs_query = text(f"""
|
||||
UPDATE usage_summaries
|
||||
SET {provider_name}_cost = COALESCE({provider_name}_cost, 0) + :cost,
|
||||
total_cost = COALESCE(total_cost, 0) + :cost
|
||||
WHERE user_id = :user_id AND billing_period = :period
|
||||
""")
|
||||
db_track.execute(update_costs_query, {
|
||||
'cost': cost_total,
|
||||
'user_id': user_id,
|
||||
'period': current_period
|
||||
})
|
||||
setattr(summary, f"{provider_name}_cost", (getattr(summary, f"{provider_name}_cost", 0.0) or 0.0) + cost_total)
|
||||
summary.total_cost = (summary.total_cost or 0.0) + cost_total
|
||||
|
||||
# Update totals (using potentially capped tokens_total from safety check)
|
||||
summary.total_calls = (summary.total_calls or 0) + 1
|
||||
summary.total_tokens = (summary.total_tokens or 0) + tokens_total
|
||||
|
||||
@@ -72,11 +72,11 @@ class StoryAudioGenerationService:
|
||||
logger.info(f"[StoryAudioGeneration] Generated audio using gTTS: {output_path}")
|
||||
return True
|
||||
|
||||
except ImportError:
|
||||
logger.error("[StoryAudioGeneration] gTTS not installed. Install with: pip install gtts")
|
||||
except ImportError as e:
|
||||
logger.error(f"[StoryAudioGeneration] gTTS not installed. ImportError: {e}. Install with: pip install gtts")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"[StoryAudioGeneration] Error generating audio with gTTS: {e}")
|
||||
logger.error(f"[StoryAudioGeneration] Error generating audio with gTTS: {type(e).__name__}: {e}")
|
||||
return False
|
||||
|
||||
def _generate_audio_pyttsx3(
|
||||
|
||||
@@ -72,8 +72,40 @@ class StoryVideoGenerationService:
|
||||
|
||||
# Import MoviePy
|
||||
try:
|
||||
from moviepy.editor import ImageClip, AudioFileClip, concatenate_videoclips, CompositeVideoClip
|
||||
except ImportError:
|
||||
# MoviePy v2.x exposes classes at top-level (moviepy.ImageClip, etc)
|
||||
from moviepy import ImageClip, AudioFileClip, concatenate_videoclips
|
||||
except Exception as _imp_err:
|
||||
# Detailed diagnostics to help users fix environment issues
|
||||
try:
|
||||
import sys as _sys
|
||||
import platform as _platform
|
||||
import importlib
|
||||
mv = None
|
||||
imv = None
|
||||
ff_path = "unresolved"
|
||||
try:
|
||||
mv = importlib.import_module("moviepy")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
imv = importlib.import_module("imageio")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
import imageio_ffmpeg as _iff
|
||||
ff_path = _iff.get_ffmpeg_exe()
|
||||
except Exception:
|
||||
pass
|
||||
logger.error(
|
||||
"[StoryVideoGeneration] MoviePy import failed. "
|
||||
f"py={_sys.executable} plat={_platform.platform()} "
|
||||
f"moviepy_ver={getattr(mv,'__version__', 'NA')} "
|
||||
f"imageio_ver={getattr(imv,'__version__', 'NA')} "
|
||||
f"ffmpeg_path={ff_path} err={_imp_err}"
|
||||
)
|
||||
except Exception:
|
||||
# best-effort diagnostics
|
||||
pass
|
||||
logger.error("[StoryVideoGeneration] MoviePy not installed. Install with: pip install moviepy imageio imageio-ffmpeg")
|
||||
raise RuntimeError("MoviePy is not installed. Please install it to generate videos.")
|
||||
|
||||
@@ -182,8 +214,38 @@ class StoryVideoGenerationService:
|
||||
|
||||
# Import MoviePy
|
||||
try:
|
||||
from moviepy.editor import ImageClip, AudioFileClip, concatenate_videoclips, CompositeVideoClip
|
||||
except ImportError:
|
||||
from moviepy import ImageClip, AudioFileClip, concatenate_videoclips
|
||||
except Exception as _imp_err:
|
||||
# Detailed diagnostics to help users fix environment issues
|
||||
try:
|
||||
import sys as _sys
|
||||
import platform as _platform
|
||||
import importlib
|
||||
mv = None
|
||||
imv = None
|
||||
ff_path = "unresolved"
|
||||
try:
|
||||
mv = importlib.import_module("moviepy")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
imv = importlib.import_module("imageio")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
import imageio_ffmpeg as _iff
|
||||
ff_path = _iff.get_ffmpeg_exe()
|
||||
except Exception:
|
||||
pass
|
||||
logger.error(
|
||||
"[StoryVideoGeneration] MoviePy import failed. "
|
||||
f"py={_sys.executable} plat={_platform.platform()} "
|
||||
f"moviepy_ver={getattr(mv,'__version__', 'NA')} "
|
||||
f"imageio_ver={getattr(imv,'__version__', 'NA')} "
|
||||
f"ffmpeg_path={ff_path} err={_imp_err}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
logger.error("[StoryVideoGeneration] MoviePy not installed. Install with: pip install moviepy imageio imageio-ffmpeg")
|
||||
raise RuntimeError("MoviePy is not installed. Please install it to generate videos.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user