Enhance AIEngine and ProgressModal for improved user feedback

- Added user-friendly messages for input description, preparation, AI call, parsing, and saving phases in AIEngine.
- Updated ProgressModal to display success messages and checklist-style progress steps based on function type.
- Improved handling of step logs and current phase determination for better user experience during asynchronous tasks.
This commit is contained in:
Desktop
2025-11-10 23:47:36 +05:00
parent bb4fe9d6c1
commit 1bd9ebc974
2 changed files with 291 additions and 181 deletions

View File

@@ -25,6 +25,66 @@ class AIEngine:
self.console_tracker = None # Will be initialized per function
self.cost_tracker = CostTracker()
def _get_input_description(self, function_name: str, payload: dict, count: int) -> str:
"""Get user-friendly input description"""
if function_name == 'auto_cluster':
return f"{count} keyword{'s' if count != 1 else ''}"
elif function_name == 'generate_ideas':
return f"{count} cluster{'s' if count != 1 else ''}"
elif function_name == 'generate_content':
return f"{count} task{'s' if count != 1 else ''}"
elif function_name == 'generate_images':
return f"{count} task{'s' if count != 1 else ''}"
return f"{count} item{'s' if count != 1 else ''}"
def _get_prep_message(self, function_name: str, count: int, data: Any) -> str:
"""Get user-friendly prep message"""
if function_name == 'auto_cluster':
return f"Loading {count} keyword{'s' if count != 1 else ''}"
elif function_name == 'generate_ideas':
return f"Loading {count} cluster{'s' if count != 1 else ''}"
elif function_name == 'generate_content':
return f"Preparing {count} content idea{'s' if count != 1 else ''}"
elif function_name == 'generate_images':
return f"Extracting image prompts from {count} task{'s' if count != 1 else ''}"
return f"Preparing {count} item{'s' if count != 1 else ''}"
def _get_ai_call_message(self, function_name: str, count: int) -> str:
"""Get user-friendly AI call message"""
if function_name == 'auto_cluster':
return f"Grouping {count} keyword{'s' if count != 1 else ''} into clusters"
elif function_name == 'generate_ideas':
return f"Generating content ideas for {count} cluster{'s' if count != 1 else ''}"
elif function_name == 'generate_content':
return f"Writing article{'s' if count != 1 else ''} with AI"
elif function_name == 'generate_images':
return f"Creating image{'s' if count != 1 else ''} with AI"
return f"Processing with AI"
def _get_parse_message(self, function_name: str) -> str:
"""Get user-friendly parse message"""
if function_name == 'auto_cluster':
return "Organizing clusters"
elif function_name == 'generate_ideas':
return "Structuring outlines"
elif function_name == 'generate_content':
return "Formatting content"
elif function_name == 'generate_images':
return "Processing images"
return "Processing results"
def _get_save_message(self, function_name: str, count: int) -> str:
"""Get user-friendly save message"""
if function_name == 'auto_cluster':
return f"Saving {count} cluster{'s' if count != 1 else ''}"
elif function_name == 'generate_ideas':
return f"Saving {count} idea{'s' if count != 1 else ''}"
elif function_name == 'generate_content':
return f"Saving {count} article{'s' if count != 1 else ''}"
elif function_name == 'generate_images':
return f"Saving {count} image{'s' if count != 1 else ''}"
return f"Saving {count} item{'s' if count != 1 else ''}"
def execute(self, fn: BaseAIFunction, payload: dict) -> dict:
"""
Unified execution pipeline for all AI functions.
@@ -46,18 +106,23 @@ class AIEngine:
try:
# Phase 1: INIT - Validation & Setup (0-10%)
self.console_tracker.prep("Validating input payload")
# Extract input data for user-friendly messages
ids = payload.get('ids', [])
input_count = len(ids) if ids else 0
input_description = self._get_input_description(function_name, payload, input_count)
self.console_tracker.prep(f"Validating {input_description}")
validated = fn.validate(payload, self.account)
if not validated['valid']:
self.console_tracker.error('ValidationError', validated['error'])
return self._handle_error(validated['error'], fn)
validation_message = f"Validating {input_description}"
self.console_tracker.prep("Validation complete")
self.step_tracker.add_request_step("INIT", "success", "Validation complete")
self.tracker.update("INIT", 10, "Validation complete", meta=self.step_tracker.get_meta())
self.step_tracker.add_request_step("INIT", "success", validation_message)
self.tracker.update("INIT", 10, validation_message, meta=self.step_tracker.get_meta())
# Phase 2: PREP - Data Loading & Prompt Building (10-25%)
self.console_tracker.prep("Loading data from database")
data = fn.prepare(payload, self.account)
if isinstance(data, (list, tuple)):
data_count = len(data)
@@ -68,15 +133,16 @@ class AIEngine:
elif 'keywords' in data:
data_count = len(data['keywords'])
else:
data_count = data.get('count', 1)
data_count = data.get('count', input_count)
else:
data_count = 1
data_count = input_count
self.console_tracker.prep(f"Building prompt from {data_count} items")
prep_message = self._get_prep_message(function_name, data_count, data)
self.console_tracker.prep(prep_message)
prompt = fn.build_prompt(data, self.account)
self.console_tracker.prep(f"Prompt built: {len(prompt)} characters")
self.step_tracker.add_request_step("PREP", "success", f"Loaded {data_count} items, built prompt ({len(prompt)} chars)")
self.tracker.update("PREP", 25, f"Data prepared: {data_count} items", meta=self.step_tracker.get_meta())
self.step_tracker.add_request_step("PREP", "success", prep_message)
self.tracker.update("PREP", 25, prep_message, meta=self.step_tracker.get_meta())
# Phase 3: AI_CALL - Provider API Call (25-70%)
ai_core = AICore(account=self.account)
@@ -111,19 +177,7 @@ class AIEngine:
exc_info=True,
)
# Track configured model information so it shows in the progress modal
self.step_tracker.add_request_step(
"PREP",
"success",
f"AI model in settings: {model_from_integration or 'Not set'}"
)
self.step_tracker.add_request_step(
"PREP",
"success",
f"AI model selected for request: {model or 'default'}"
)
# Debug logging: Show model configuration
# Debug logging: Show model configuration (console only, not in step tracker)
logger.info(f"[AIEngine] Model Configuration for {function_name}:")
logger.info(f" - Model from get_model_config: {model}")
logger.info(f" - Full model_config: {model_config}")
@@ -132,13 +186,10 @@ class AIEngine:
self.console_tracker.ai_call(f"Calling {model or 'default'} model with {len(prompt)} char prompt")
self.console_tracker.ai_call(f"Function ID: {function_id}")
# Track AI call start
self.step_tracker.add_response_step(
"AI_CALL",
"success",
f"Calling {model or 'default'} model..."
)
self.tracker.update("AI_CALL", 30, f"Sending to {model or 'default'}...", meta=self.step_tracker.get_meta())
# Track AI call start with user-friendly message
ai_call_message = self._get_ai_call_message(function_name, data_count)
self.step_tracker.add_response_step("AI_CALL", "success", ai_call_message)
self.tracker.update("AI_CALL", 50, ai_call_message, meta=self.step_tracker.get_meta())
try:
# Use centralized run_ai_request() with console logging (Stage 2 & 3 requirement)
@@ -186,7 +237,8 @@ class AIEngine:
# Phase 4: PARSE - Response Parsing (70-85%)
try:
self.console_tracker.parse("Parsing AI response")
parse_message = self._get_parse_message(function_name)
self.console_tracker.parse(parse_message)
response_content = raw_response.get('content', '')
parsed = fn.parse_response(response_content, self.step_tracker)
@@ -202,8 +254,8 @@ class AIEngine:
parsed_count = 1
self.console_tracker.parse(f"Successfully parsed {parsed_count} items from response")
self.step_tracker.add_response_step("PARSE", "success", f"Parsed {parsed_count} items from AI response")
self.tracker.update("PARSE", 85, f"Parsed {parsed_count} items", meta=self.step_tracker.get_meta())
self.step_tracker.add_response_step("PARSE", "success", parse_message)
self.tracker.update("PARSE", 85, parse_message, meta=self.step_tracker.get_meta())
except Exception as parse_error:
error_msg = f"Failed to parse AI response: {str(parse_error)}"
logger.error(f"AIEngine: {error_msg}", exc_info=True)
@@ -211,20 +263,19 @@ class AIEngine:
return self._handle_error(error_msg, fn)
# Phase 5: SAVE - Database Operations (85-98%)
self.console_tracker.save("Saving results to database")
# Pass step_tracker to save_output so it can add validation steps
save_result = fn.save_output(parsed, data, self.account, self.tracker, step_tracker=self.step_tracker)
clusters_created = save_result.get('clusters_created', 0)
keywords_updated = save_result.get('keywords_updated', 0)
count = save_result.get('count', 0)
# Build success message based on function type
# Use user-friendly save message based on function type
if clusters_created:
save_msg = f"Created {clusters_created} clusters, updated {keywords_updated} keywords"
save_msg = f"Saving {clusters_created} cluster{'s' if clusters_created != 1 else ''}"
elif count:
save_msg = f"Saved {count} items"
save_msg = self._get_save_message(function_name, count)
else:
save_msg = "Results saved successfully"
save_msg = self._get_save_message(function_name, data_count)
self.console_tracker.save(save_msg)
self.step_tracker.add_request_step("SAVE", "success", save_msg)