180 lines
7.3 KiB
Python
180 lines
7.3 KiB
Python
# intentAnalyzer.py
|
|
# Intent analysis for adaptive React mode - AI-based, language-agnostic
|
|
|
|
import json
|
|
import logging
|
|
from typing import Dict, Any, List
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class IntentAnalyzer:
|
|
"""Analyzes user intent using AI - language-agnostic and generic"""
|
|
|
|
def __init__(self, services=None):
|
|
self.services = services
|
|
|
|
async def analyzeUserIntent(self, userPrompt: str, context: Any) -> Dict[str, Any]:
|
|
"""Analyzes user intent from prompt and context using AI"""
|
|
try:
|
|
# Use AI to analyze intent
|
|
aiAnalysis = await self._analyzeIntentWithAI(userPrompt, context)
|
|
if aiAnalysis:
|
|
return aiAnalysis
|
|
|
|
# Fallback to basic analysis if AI fails
|
|
return self._createBasicIntentAnalysis(userPrompt)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error analyzing user intent: {str(e)}")
|
|
return self._createDefaultIntentAnalysis(userPrompt)
|
|
|
|
async def _analyzeIntentWithAI(self, userPrompt: str, context: Any) -> Dict[str, Any]:
|
|
"""Uses AI to analyze user intent - language-agnostic"""
|
|
try:
|
|
if not self.services or not hasattr(self.services, 'ai'):
|
|
return None
|
|
|
|
# Create AI analysis prompt
|
|
analysisPrompt = f"""
|
|
You are an intent analyzer. Analyze the user's request to understand what they want delivered.
|
|
|
|
USER REQUEST: {userPrompt}
|
|
|
|
CONTEXT: {getattr(context.task_step, 'objective', '') if hasattr(context, 'task_step') and context.task_step else ''}
|
|
|
|
Analyze the user's intent and determine:
|
|
1. What type of data/content they want (numbers, text, documents, analysis, code, etc.)
|
|
2. What format they expect (raw data, formatted, structured, visual, etc.)
|
|
3. What quality requirements they have (accuracy, completeness, format)
|
|
4. What specific success criteria define completion
|
|
|
|
CRITICAL: Respond with ONLY the JSON object below. Do not include any explanatory text, analysis, or other content before or after the JSON.
|
|
|
|
{{
|
|
"primaryGoal": "The main objective the user wants to achieve",
|
|
"dataType": "numbers|text|documents|analysis|code|unknown",
|
|
"expectedFormat": "raw_data|formatted|structured|visual|unknown",
|
|
"qualityRequirements": {{
|
|
"accuracyThreshold": 0.0-1.0,
|
|
"completenessThreshold": 0.0-1.0,
|
|
"formatRequirement": "any|formatted|raw|structured"
|
|
}},
|
|
"successCriteria": ["specific criterion 1", "specific criterion 2"],
|
|
"confidenceScore": 0.0-1.0
|
|
}}
|
|
"""
|
|
|
|
# Call AI service for analysis
|
|
from modules.datamodels.datamodelAi import AiCallOptions, OperationType
|
|
request_options = AiCallOptions()
|
|
request_options.operationType = OperationType.GENERAL
|
|
|
|
response = await self.services.ai.callAi(
|
|
prompt=analysisPrompt,
|
|
documents=None,
|
|
options=request_options
|
|
)
|
|
|
|
# If first attempt fails, try with more explicit prompt
|
|
if response and not self._isValidJsonResponse(response):
|
|
logger.debug("First AI intent analysis attempt failed, retrying with explicit JSON-only prompt")
|
|
explicitPrompt = f"""
|
|
{analysisPrompt}
|
|
|
|
IMPORTANT: You must respond with ONLY valid JSON. No explanations, no analysis, no text before or after. Just the JSON object.
|
|
"""
|
|
response = await self.services.ai.callAi(
|
|
prompt=explicitPrompt,
|
|
documents=None,
|
|
options=request_options
|
|
)
|
|
|
|
if not response or not response.strip():
|
|
logger.warning("AI intent analysis returned empty response")
|
|
return None
|
|
|
|
# Clean and extract JSON from response
|
|
result = response.strip()
|
|
logger.debug(f"AI intent analysis response length: {len(result)}")
|
|
|
|
# Try to find JSON in the response with multiple strategies
|
|
import re
|
|
|
|
# Strategy 1: Look for JSON in markdown code blocks
|
|
json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', result, re.DOTALL)
|
|
if json_match:
|
|
result = json_match.group(1)
|
|
logger.debug(f"Extracted JSON from markdown code block: {result[:200]}...")
|
|
else:
|
|
# Strategy 2: Look for JSON object with proper structure
|
|
json_match = re.search(r'\{[^{}]*"primaryGoal"[^{}]*\}', result, re.DOTALL)
|
|
if not json_match:
|
|
# Strategy 3: Look for any JSON object
|
|
json_match = re.search(r'\{.*\}', result, re.DOTALL)
|
|
|
|
if not json_match:
|
|
logger.warning(f"All AI intent analysis attempts failed - no JSON found in response: {result[:200]}...")
|
|
logger.debug(f"Full AI response: {result}")
|
|
return None
|
|
|
|
result = json_match.group(0)
|
|
logger.debug(f"Extracted JSON directly: {result[:200]}...")
|
|
|
|
try:
|
|
aiResult = json.loads(result)
|
|
logger.info("AI intent analysis JSON parsed successfully")
|
|
return aiResult
|
|
|
|
except json.JSONDecodeError as json_error:
|
|
logger.warning(f"All AI intent analysis attempts failed - invalid JSON: {str(json_error)}")
|
|
logger.debug(f"JSON content: {result}")
|
|
return None
|
|
|
|
return None
|
|
|
|
except Exception as e:
|
|
logger.error(f"AI intent analysis failed: {str(e)}")
|
|
return None
|
|
|
|
def _createBasicIntentAnalysis(self, userPrompt: str) -> Dict[str, Any]:
|
|
"""Creates basic intent analysis without AI"""
|
|
return {
|
|
"primaryGoal": userPrompt.strip(),
|
|
"dataType": "unknown",
|
|
"expectedFormat": "unknown",
|
|
"qualityRequirements": {
|
|
"accuracyThreshold": 0.8,
|
|
"completenessThreshold": 0.8,
|
|
"formatRequirement": "any"
|
|
},
|
|
"successCriteria": ["Delivers what the user requested"],
|
|
"confidenceScore": 0.5
|
|
}
|
|
|
|
def _createDefaultIntentAnalysis(self, userPrompt: str) -> Dict[str, Any]:
|
|
"""Creates a default intent analysis when analysis fails"""
|
|
return {
|
|
"primaryGoal": userPrompt,
|
|
"dataType": "unknown",
|
|
"expectedFormat": "unknown",
|
|
"qualityRequirements": {
|
|
"accuracyThreshold": 0.8,
|
|
"completenessThreshold": 0.8,
|
|
"formatRequirement": "any"
|
|
},
|
|
"successCriteria": ["Delivers what the user requested"],
|
|
"confidenceScore": 0.1
|
|
}
|
|
|
|
def _isValidJsonResponse(self, response: str) -> bool:
|
|
"""Checks if response contains valid JSON structure"""
|
|
try:
|
|
import re
|
|
# Look for JSON with expected structure
|
|
json_match = re.search(r'\{[^{}]*"primaryGoal"[^{}]*\}', response, re.DOTALL)
|
|
if json_match:
|
|
json.loads(json_match.group(0))
|
|
return True
|
|
return False
|
|
except:
|
|
return False
|