@@ -87,6 +87,90 @@ def _transcribe_audio(result, ai_providers, db):
8787 )
8888
8989
90+ def _generate_call_analysis (transcription , ai_providers , organization_id , result_id , db , agent = None , scenario = None ):
91+ """Generate call analysis (summary, sentiment, success) using LLM."""
92+ from app .services .ai .llm_service import llm_service
93+
94+ llm_provider = ModelProvider .OPENAI
95+ llm_model = "gpt-4o-mini"
96+
97+ chosen_provider = next (
98+ (p for p in ai_providers if provider_matches (p .provider , llm_provider )),
99+ None ,
100+ )
101+ if not chosen_provider :
102+ llm_provider = ModelProvider .GOOGLE
103+ llm_model = "gemini-2.0-flash"
104+ chosen_provider = next (
105+ (p for p in ai_providers if provider_matches (p .provider , llm_provider )),
106+ None ,
107+ )
108+ if not chosen_provider :
109+ logger .warning (f"[EvaluatorResult { result_id } ] No LLM provider available for call analysis" )
110+ return None
111+
112+ agent_context = ""
113+ if agent and agent .description :
114+ agent_context = f"\n \n Agent Description:\n { agent .description } "
115+ if scenario :
116+ scenario_name = getattr (scenario , 'name' , '' )
117+ scenario_desc = getattr (scenario , 'description' , '' )
118+ if scenario_name or scenario_desc :
119+ agent_context += f"\n \n Scenario: { scenario_name } "
120+ if scenario_desc :
121+ agent_context += f"\n Scenario Description: { scenario_desc } "
122+
123+ messages = [
124+ {
125+ "role" : "system" ,
126+ "content" : (
127+ "You are a call analysis expert. Analyze the following conversation transcript "
128+ "and provide a structured analysis. Respond ONLY with valid JSON, no markdown."
129+ ),
130+ },
131+ {
132+ "role" : "user" ,
133+ "content" : f"""Analyze this conversation transcript and provide:
134+ 1. A concise summary of the call (2-3 sentences)
135+ 2. The user/caller's overall sentiment (one of: Positive, Negative, Neutral, Mixed)
136+ 3. Whether the call was successful in achieving its objective (true/false)
137+ { agent_context }
138+
139+ Transcript:
140+ { transcription }
141+
142+ Respond in this exact JSON format:
143+ {{"call_summary": "...", "user_sentiment": "...", "call_successful": true/false}}""" ,
144+ },
145+ ]
146+
147+ try :
148+ llm_result = llm_service .generate_response (
149+ messages = messages ,
150+ llm_provider = llm_provider ,
151+ llm_model = llm_model ,
152+ organization_id = organization_id ,
153+ db = db ,
154+ temperature = 0.3 ,
155+ max_tokens = 500 ,
156+ )
157+ import json
158+ import re
159+ text = llm_result .get ("text" , "" )
160+ json_match = re .search (r'\{[^{}]*\}' , text , re .DOTALL )
161+ if json_match :
162+ analysis = json .loads (json_match .group ())
163+ required_keys = {"call_summary" , "user_sentiment" , "call_successful" }
164+ if required_keys .issubset (analysis .keys ()):
165+ logger .info (f"[EvaluatorResult { result_id } ] Call analysis generated successfully" )
166+ return analysis
167+ logger .warning (f"[EvaluatorResult { result_id } ] Could not parse call analysis from LLM response" )
168+ return None
169+ except Exception as e :
170+ logger .error (f"[EvaluatorResult { result_id } ] Call analysis failed: { e } " , exc_info = True )
171+ return None
172+
173+
90174def _categorize_metrics (enabled_metrics , has_audio ):
91175 """Split metrics into LLM-evaluable and audio-only categories."""
92176 llm_metrics = []
@@ -247,7 +331,26 @@ def process_evaluator_result_task(self, result_id: str):
247331 "skipping evaluation"
248332 )
249333
250- # Step 5: Complete
334+ # Step 5: Call Analysis
335+ if transcription and not (result .call_data and result .call_data .get ("call_analysis" )):
336+ try :
337+ call_analysis = _generate_call_analysis (
338+ transcription = transcription ,
339+ ai_providers = ai_providers ,
340+ organization_id = result .organization_id ,
341+ result_id = result .result_id ,
342+ db = db ,
343+ agent = agent ,
344+ scenario = scenario ,
345+ )
346+ if call_analysis :
347+ result .call_data = {"call_analysis" : call_analysis }
348+ except Exception as analysis_err :
349+ logger .warning (
350+ f"[EvaluatorResult { result .result_id } ] Call analysis failed (non-fatal): { analysis_err } "
351+ )
352+
353+ # Step 6: Complete
251354 result .metric_scores = metric_scores
252355 result .status = EvaluatorResultStatus .COMPLETED .value
253356 db .commit ()
0 commit comments