Spaces:
Running
Running
Upload simple_casl_app.py
Browse files- simple_casl_app.py +192 -23
simple_casl_app.py
CHANGED
@@ -107,7 +107,7 @@ else:
|
|
107 |
logger.warning("Claude API key not found - using demo mode")
|
108 |
|
109 |
def validate_analysis_completeness(response_text):
|
110 |
-
"""Validate that all 12 sections are present in the analysis"""
|
111 |
required_sections = [
|
112 |
"1. SPEECH FACTORS",
|
113 |
"2. LANGUAGE SKILLS ASSESSMENT",
|
@@ -124,25 +124,129 @@ def validate_analysis_completeness(response_text):
|
|
124 |
]
|
125 |
|
126 |
missing_sections = []
|
|
|
|
|
|
|
127 |
for section in required_sections:
|
128 |
-
|
|
|
|
|
129 |
missing_sections.append(section)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
|
131 |
if missing_sections:
|
132 |
print(f"\n⚠️ MISSING SECTIONS: {missing_sections}")
|
133 |
-
return False
|
|
|
|
|
|
|
134 |
else:
|
135 |
-
print(f"\n✅ ALL 12 SECTIONS PRESENT")
|
136 |
-
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
|
138 |
def call_claude_api_with_continuation(prompt, max_continuations=3):
|
139 |
"""Call Claude API with continuation prompting to ensure complete responses"""
|
140 |
if not ANTHROPIC_API_KEY:
|
141 |
return "❌ Claude API key not configured. Please set ANTHROPIC_API_KEY environment variable."
|
142 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
try:
|
144 |
-
|
145 |
continuation_count = 0
|
|
|
146 |
|
147 |
# Add continuation instruction to original prompt
|
148 |
initial_prompt = prompt + "\n\nIMPORTANT: If your response is cut off or incomplete, end with <CONTINUE> to indicate more content is needed. Ensure you complete all sections of the analysis."
|
@@ -190,29 +294,28 @@ def call_claude_api_with_continuation(prompt, max_continuations=3):
|
|
190 |
print(f"Last 200 chars: {response_text[-200:]}...")
|
191 |
print("=" * 50)
|
192 |
|
193 |
-
#
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
|
|
199 |
|
200 |
# Check if response indicates continuation is needed
|
201 |
needs_continuation = "<CONTINUE>" in response_text
|
202 |
|
203 |
print(f"Needs continuation: {needs_continuation}")
|
204 |
print(f"Continuation count: {continuation_count}/{max_continuations}")
|
|
|
|
|
205 |
|
206 |
# Continue if <CONTINUE> is present and we haven't reached max
|
207 |
if needs_continuation and continuation_count < max_continuations:
|
208 |
-
# Remove the CONTINUE marker
|
209 |
-
full_response = full_response.replace("<CONTINUE>", "")
|
210 |
continuation_count += 1
|
211 |
logger.info(f"Continuing analysis (attempt {continuation_count}/{max_continuations})")
|
212 |
continue
|
213 |
else:
|
214 |
-
# Clean up any remaining continuation markers
|
215 |
-
full_response = full_response.replace("<CONTINUE>", "")
|
216 |
break
|
217 |
else:
|
218 |
logger.error(f"Claude API error: {response.status_code} - {response.text}")
|
@@ -222,16 +325,36 @@ def call_claude_api_with_continuation(prompt, max_continuations=3):
|
|
222 |
logger.error(f"Error calling Claude API: {str(e)}")
|
223 |
return f"❌ Error: {str(e)}"
|
224 |
|
225 |
-
#
|
226 |
-
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
228 |
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
print("=" * 50)
|
234 |
|
|
|
|
|
|
|
|
|
235 |
# Print the entire final response for debugging
|
236 |
print(f"\n=== ENTIRE FINAL RESPONSE ===")
|
237 |
print(full_response)
|
@@ -750,6 +873,30 @@ def analyze_transcript_content(transcript_content, age, gender, slp_notes):
|
|
750 |
|
751 |
# Get analysis from Claude API
|
752 |
result = call_claude_api_with_continuation(prompt, max_continuations=5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
753 |
return result
|
754 |
|
755 |
def analyze_transcript(file, age, gender, slp_notes):
|
@@ -874,6 +1021,14 @@ def targeted_analysis(transcript, custom_question, age, gender, slp_notes):
|
|
874 |
|
875 |
# Get targeted analysis from Claude API
|
876 |
result = call_claude_api_with_continuation(prompt, max_continuations=3)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
877 |
return result
|
878 |
|
879 |
# Create enhanced interface with tabs
|
@@ -1312,6 +1467,20 @@ with gr.Blocks(title="Enhanced CASL Analysis", theme=gr.themes.Soft()) as app:
|
|
1312 |
"""
|
1313 |
|
1314 |
result = call_claude_api_with_continuation(prompt, max_continuations=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1315 |
progress_msg = "✅ Quick analysis completed" if "[Analysis completed in" in result else "🔄 Quick analysis in progress..."
|
1316 |
return result, progress_msg
|
1317 |
|
|
|
107 |
logger.warning("Claude API key not found - using demo mode")
|
108 |
|
109 |
def validate_analysis_completeness(response_text):
|
110 |
+
"""Validate that all 12 sections are present in the analysis exactly once"""
|
111 |
required_sections = [
|
112 |
"1. SPEECH FACTORS",
|
113 |
"2. LANGUAGE SKILLS ASSESSMENT",
|
|
|
124 |
]
|
125 |
|
126 |
missing_sections = []
|
127 |
+
duplicate_sections = []
|
128 |
+
section_counts = {}
|
129 |
+
|
130 |
for section in required_sections:
|
131 |
+
count = response_text.count(section)
|
132 |
+
section_counts[section] = count
|
133 |
+
if count == 0:
|
134 |
missing_sections.append(section)
|
135 |
+
elif count > 1:
|
136 |
+
duplicate_sections.append(section)
|
137 |
+
|
138 |
+
# Log detailed validation results
|
139 |
+
print(f"\n=== COMPREHENSIVE VALIDATION ===")
|
140 |
+
print(f"Total response length: {len(response_text)} characters")
|
141 |
+
print(f"Missing sections: {missing_sections}")
|
142 |
+
print(f"Duplicate sections: {duplicate_sections}")
|
143 |
+
print(f"Section counts: {section_counts}")
|
144 |
|
145 |
if missing_sections:
|
146 |
print(f"\n⚠️ MISSING SECTIONS: {missing_sections}")
|
147 |
+
return False, missing_sections, duplicate_sections, section_counts
|
148 |
+
elif duplicate_sections:
|
149 |
+
print(f"\n⚠️ DUPLICATE SECTIONS: {duplicate_sections}")
|
150 |
+
return False, missing_sections, duplicate_sections, section_counts
|
151 |
else:
|
152 |
+
print(f"\n✅ ALL 12 SECTIONS PRESENT EXACTLY ONCE")
|
153 |
+
return True, missing_sections, duplicate_sections, section_counts
|
154 |
+
|
155 |
+
def fix_incomplete_analysis(response_text, missing_sections):
|
156 |
+
"""Attempt to fix incomplete analysis by requesting missing sections"""
|
157 |
+
if not missing_sections:
|
158 |
+
return response_text
|
159 |
+
|
160 |
+
if not ANTHROPIC_API_KEY:
|
161 |
+
return response_text + "\n\n❌ Cannot fix incomplete analysis - API key not configured"
|
162 |
+
|
163 |
+
try:
|
164 |
+
# Create a focused prompt for missing sections
|
165 |
+
missing_sections_text = "\n".join([f"- {section}" for section in missing_sections])
|
166 |
+
|
167 |
+
fix_prompt = f"""
|
168 |
+
The following sections are missing from the CASL analysis. Please provide ONLY these missing sections:
|
169 |
+
|
170 |
+
{missing_sections_text}
|
171 |
+
|
172 |
+
IMPORTANT:
|
173 |
+
- Provide ONLY the missing sections listed above
|
174 |
+
- Do not repeat any sections that are already present
|
175 |
+
- Use the exact section headers as shown above
|
176 |
+
- Make each section comprehensive and detailed
|
177 |
+
- Ensure clinical accuracy and appropriate depth for SLP assessment
|
178 |
+
"""
|
179 |
+
|
180 |
+
headers = {
|
181 |
+
"Content-Type": "application/json",
|
182 |
+
"x-api-key": ANTHROPIC_API_KEY,
|
183 |
+
"anthropic-version": "2023-06-01"
|
184 |
+
}
|
185 |
+
|
186 |
+
data = {
|
187 |
+
"model": "claude-3-5-sonnet-20241022",
|
188 |
+
"max_tokens": 4096,
|
189 |
+
"messages": [
|
190 |
+
{
|
191 |
+
"role": "user",
|
192 |
+
"content": fix_prompt
|
193 |
+
}
|
194 |
+
]
|
195 |
+
}
|
196 |
+
|
197 |
+
response = requests.post(
|
198 |
+
"https://api.anthropic.com/v1/messages",
|
199 |
+
headers=headers,
|
200 |
+
json=data,
|
201 |
+
timeout=90
|
202 |
+
)
|
203 |
+
|
204 |
+
if response.status_code == 200:
|
205 |
+
response_json = response.json()
|
206 |
+
fix_text = response_json['content'][0]['text']
|
207 |
+
|
208 |
+
# Combine original response with fix
|
209 |
+
complete_response = response_text + "\n\n" + fix_text
|
210 |
+
|
211 |
+
print(f"\n=== FIXED INCOMPLETE ANALYSIS ===")
|
212 |
+
print(f"Added missing sections: {missing_sections}")
|
213 |
+
print(f"Fix text length: {len(fix_text)} characters")
|
214 |
+
print("=" * 50)
|
215 |
+
|
216 |
+
return complete_response
|
217 |
+
else:
|
218 |
+
logger.error(f"Error fixing incomplete analysis: {response.status_code}")
|
219 |
+
return response_text + f"\n\n❌ Error fixing incomplete analysis: {response.status_code}"
|
220 |
+
|
221 |
+
except Exception as e:
|
222 |
+
logger.error(f"Error in fix_incomplete_analysis: {str(e)}")
|
223 |
+
return response_text + f"\n\n❌ Error fixing incomplete analysis: {str(e)}"
|
224 |
|
225 |
def call_claude_api_with_continuation(prompt, max_continuations=3):
|
226 |
"""Call Claude API with continuation prompting to ensure complete responses"""
|
227 |
if not ANTHROPIC_API_KEY:
|
228 |
return "❌ Claude API key not configured. Please set ANTHROPIC_API_KEY environment variable."
|
229 |
|
230 |
+
# Define all required sections
|
231 |
+
required_sections = [
|
232 |
+
"1. SPEECH FACTORS",
|
233 |
+
"2. LANGUAGE SKILLS ASSESSMENT",
|
234 |
+
"3. COMPLEX SENTENCE ANALYSIS",
|
235 |
+
"4. FIGURATIVE LANGUAGE ANALYSIS",
|
236 |
+
"5. PRAGMATIC LANGUAGE ASSESSMENT",
|
237 |
+
"6. VOCABULARY AND SEMANTIC ANALYSIS",
|
238 |
+
"7. MORPHOLOGICAL AND PHONOLOGICAL ANALYSIS",
|
239 |
+
"8. COGNITIVE-LINGUISTIC FACTORS",
|
240 |
+
"9. FLUENCY AND RHYTHM ANALYSIS",
|
241 |
+
"10. QUANTITATIVE METRICS",
|
242 |
+
"11. CLINICAL IMPLICATIONS",
|
243 |
+
"12. PROGNOSIS AND SUMMARY"
|
244 |
+
]
|
245 |
+
|
246 |
try:
|
247 |
+
response_parts = [] # Store each part as a separate item
|
248 |
continuation_count = 0
|
249 |
+
completed_sections = set() # Track which sections have been completed
|
250 |
|
251 |
# Add continuation instruction to original prompt
|
252 |
initial_prompt = prompt + "\n\nIMPORTANT: If your response is cut off or incomplete, end with <CONTINUE> to indicate more content is needed. Ensure you complete all sections of the analysis."
|
|
|
294 |
print(f"Last 200 chars: {response_text[-200:]}...")
|
295 |
print("=" * 50)
|
296 |
|
297 |
+
# Store this part
|
298 |
+
response_parts.append(response_text)
|
299 |
+
|
300 |
+
# Check which sections are present in this part
|
301 |
+
for section in required_sections:
|
302 |
+
if section in response_text:
|
303 |
+
completed_sections.add(section)
|
304 |
|
305 |
# Check if response indicates continuation is needed
|
306 |
needs_continuation = "<CONTINUE>" in response_text
|
307 |
|
308 |
print(f"Needs continuation: {needs_continuation}")
|
309 |
print(f"Continuation count: {continuation_count}/{max_continuations}")
|
310 |
+
print(f"Completed sections: {len(completed_sections)}/12")
|
311 |
+
print(f"Missing sections: {[s for s in required_sections if s not in completed_sections]}")
|
312 |
|
313 |
# Continue if <CONTINUE> is present and we haven't reached max
|
314 |
if needs_continuation and continuation_count < max_continuations:
|
|
|
|
|
315 |
continuation_count += 1
|
316 |
logger.info(f"Continuing analysis (attempt {continuation_count}/{max_continuations})")
|
317 |
continue
|
318 |
else:
|
|
|
|
|
319 |
break
|
320 |
else:
|
321 |
logger.error(f"Claude API error: {response.status_code} - {response.text}")
|
|
|
325 |
logger.error(f"Error calling Claude API: {str(e)}")
|
326 |
return f"❌ Error: {str(e)}"
|
327 |
|
328 |
+
# Combine all parts and clean up
|
329 |
+
full_response = "\n\n".join(response_parts)
|
330 |
+
full_response = full_response.replace("<CONTINUE>", "")
|
331 |
+
|
332 |
+
# Validate completeness
|
333 |
+
missing_sections = []
|
334 |
+
duplicate_sections = []
|
335 |
+
section_counts = {}
|
336 |
|
337 |
+
for section in required_sections:
|
338 |
+
count = full_response.count(section)
|
339 |
+
section_counts[section] = count
|
340 |
+
if count == 0:
|
341 |
+
missing_sections.append(section)
|
342 |
+
elif count > 1:
|
343 |
+
duplicate_sections.append(section)
|
344 |
+
|
345 |
+
# Log validation results
|
346 |
+
print(f"\n=== VALIDATION RESULTS ===")
|
347 |
+
print(f"Total response length: {len(full_response)} characters")
|
348 |
+
print(f"Number of parts: {len(response_parts)}")
|
349 |
+
print(f"Missing sections: {missing_sections}")
|
350 |
+
print(f"Duplicate sections: {duplicate_sections}")
|
351 |
+
print(f"Section counts: {section_counts}")
|
352 |
print("=" * 50)
|
353 |
|
354 |
+
# Add completion indicator
|
355 |
+
if len(response_parts) > 1:
|
356 |
+
full_response += f"\n\n[Analysis completed in {len(response_parts)} parts]"
|
357 |
+
|
358 |
# Print the entire final response for debugging
|
359 |
print(f"\n=== ENTIRE FINAL RESPONSE ===")
|
360 |
print(full_response)
|
|
|
873 |
|
874 |
# Get analysis from Claude API
|
875 |
result = call_claude_api_with_continuation(prompt, max_continuations=5)
|
876 |
+
|
877 |
+
# Validate completeness and fix if needed
|
878 |
+
is_complete, missing_sections, duplicate_sections, section_counts = validate_analysis_completeness(result)
|
879 |
+
|
880 |
+
if not is_complete:
|
881 |
+
print(f"\n🔧 ATTEMPTING TO FIX INCOMPLETE ANALYSIS...")
|
882 |
+
print(f"Missing sections: {missing_sections}")
|
883 |
+
print(f"Duplicate sections: {duplicate_sections}")
|
884 |
+
|
885 |
+
# Try to fix missing sections
|
886 |
+
if missing_sections:
|
887 |
+
result = fix_incomplete_analysis(result, missing_sections)
|
888 |
+
|
889 |
+
# Re-validate after fix
|
890 |
+
is_complete_after_fix, missing_after_fix, duplicate_after_fix, counts_after_fix = validate_analysis_completeness(result)
|
891 |
+
|
892 |
+
if not is_complete_after_fix:
|
893 |
+
print(f"\n⚠️ ANALYSIS STILL INCOMPLETE AFTER FIX ATTEMPT")
|
894 |
+
print(f"Still missing: {missing_after_fix}")
|
895 |
+
print(f"Still duplicate: {duplicate_after_fix}")
|
896 |
+
result += f"\n\n⚠️ WARNING: Analysis may be incomplete. Missing sections: {missing_after_fix}"
|
897 |
+
else:
|
898 |
+
print(f"\n✅ ANALYSIS FIXED SUCCESSFULLY")
|
899 |
+
|
900 |
return result
|
901 |
|
902 |
def analyze_transcript(file, age, gender, slp_notes):
|
|
|
1021 |
|
1022 |
# Get targeted analysis from Claude API
|
1023 |
result = call_claude_api_with_continuation(prompt, max_continuations=3)
|
1024 |
+
|
1025 |
+
# For targeted analysis, we don't need the full 12-section validation
|
1026 |
+
# but we can still validate that the response is complete and well-structured
|
1027 |
+
if len(result.strip()) < 500: # Basic length check
|
1028 |
+
print(f"\n⚠️ TARGETED ANALYSIS MAY BE INCOMPLETE")
|
1029 |
+
print(f"Response length: {len(result)} characters")
|
1030 |
+
result += f"\n\n⚠️ WARNING: This targeted analysis may be incomplete. Please review the results carefully."
|
1031 |
+
|
1032 |
return result
|
1033 |
|
1034 |
# Create enhanced interface with tabs
|
|
|
1467 |
"""
|
1468 |
|
1469 |
result = call_claude_api_with_continuation(prompt, max_continuations=2)
|
1470 |
+
|
1471 |
+
# For quick analysis, validate that all selected questions were addressed
|
1472 |
+
if questions and len(questions) > 0:
|
1473 |
+
missing_questions = []
|
1474 |
+
for question in questions:
|
1475 |
+
# Check if the question was addressed (basic check)
|
1476 |
+
if question.lower() not in result.lower():
|
1477 |
+
missing_questions.append(question)
|
1478 |
+
|
1479 |
+
if missing_questions:
|
1480 |
+
print(f"\n⚠️ QUICK ANALYSIS MAY BE INCOMPLETE")
|
1481 |
+
print(f"Missing questions: {missing_questions}")
|
1482 |
+
result += f"\n\n⚠️ WARNING: Some selected questions may not have been fully addressed: {missing_questions}"
|
1483 |
+
|
1484 |
progress_msg = "✅ Quick analysis completed" if "[Analysis completed in" in result else "🔄 Quick analysis in progress..."
|
1485 |
return result, progress_msg
|
1486 |
|