Spaces:
Sleeping
Sleeping
add ai improvement
Browse files- app/__pycache__/__init__.cpython-311.pyc +0 -0
- app/__pycache__/config.cpython-311.pyc +0 -0
- app/__pycache__/database.cpython-311.pyc +0 -0
- app/__pycache__/database.cpython-314.pyc +0 -0
- app/__pycache__/main.cpython-311.pyc +0 -0
- app/models/__pycache__/__init__.cpython-311.pyc +0 -0
- app/models/__pycache__/concept.cpython-311.pyc +0 -0
- app/models/__pycache__/content.cpython-311.pyc +0 -0
- app/models/__pycache__/help_request.cpython-311.pyc +0 -0
- app/models/__pycache__/notification.cpython-311.pyc +0 -0
- app/models/__pycache__/points.cpython-311.pyc +0 -0
- app/models/__pycache__/teacher.cpython-311.pyc +0 -0
- app/models/content.py +1 -0
- app/routes/__pycache__/__init__.cpython-311.pyc +0 -0
- app/routes/__pycache__/ai.cpython-311.pyc +0 -0
- app/routes/__pycache__/auth.cpython-311.pyc +0 -0
- app/routes/__pycache__/community.cpython-311.pyc +0 -0
- app/routes/__pycache__/concepts.cpython-311.pyc +0 -0
- app/routes/__pycache__/content.cpython-311.pyc +0 -0
- app/routes/__pycache__/help.cpython-311.pyc +0 -0
- app/routes/__pycache__/notifications.cpython-311.pyc +0 -0
- app/routes/__pycache__/points.cpython-311.pyc +0 -0
- app/routes/__pycache__/suggestions.cpython-311.pyc +0 -0
- app/routes/__pycache__/translate.cpython-311.pyc +0 -0
- app/routes/help.py +8 -6
- app/routes/suggestions.py +7 -5
- app/schemas/__pycache__/__init__.cpython-311.pyc +0 -0
- app/schemas/__pycache__/auth.cpython-311.pyc +0 -0
- app/schemas/__pycache__/concept.cpython-311.pyc +0 -0
- app/schemas/__pycache__/content.cpython-311.pyc +0 -0
- app/schemas/__pycache__/help_request.cpython-311.pyc +0 -0
- app/schemas/__pycache__/points.cpython-311.pyc +0 -0
- app/schemas/__pycache__/teacher.cpython-311.pyc +0 -0
- app/schemas/content.py +1 -0
- app/services/__pycache__/__init__.cpython-311.pyc +0 -0
- app/services/__pycache__/auth.cpython-311.pyc +0 -0
- app/services/__pycache__/cloudinary_service.cpython-311.pyc +0 -0
- app/services/__pycache__/concept_resolver.cpython-311.pyc +0 -0
- app/services/__pycache__/content_service.cpython-311.pyc +0 -0
- app/services/__pycache__/gemini_service.cpython-311.pyc +0 -0
- app/services/__pycache__/points_service.cpython-311.pyc +0 -0
- app/services/__pycache__/speech_service.cpython-311.pyc +0 -0
- app/services/__pycache__/translation_service.cpython-311.pyc +0 -0
- app/services/concept_resolver.py +10 -1
- app/services/content_service.py +38 -4
- app/services/gemini_service.py +51 -2
- app/services/speech_service.py +11 -2
app/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (129 Bytes). View file
|
|
|
app/__pycache__/config.cpython-311.pyc
ADDED
|
Binary file (1.85 kB). View file
|
|
|
app/__pycache__/database.cpython-311.pyc
ADDED
|
Binary file (1.46 kB). View file
|
|
|
app/__pycache__/database.cpython-314.pyc
CHANGED
|
Binary files a/app/__pycache__/database.cpython-314.pyc and b/app/__pycache__/database.cpython-314.pyc differ
|
|
|
app/__pycache__/main.cpython-311.pyc
ADDED
|
Binary file (2.73 kB). View file
|
|
|
app/models/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (921 Bytes). View file
|
|
|
app/models/__pycache__/concept.cpython-311.pyc
ADDED
|
Binary file (2.55 kB). View file
|
|
|
app/models/__pycache__/content.cpython-311.pyc
ADDED
|
Binary file (4.17 kB). View file
|
|
|
app/models/__pycache__/help_request.cpython-311.pyc
ADDED
|
Binary file (3.05 kB). View file
|
|
|
app/models/__pycache__/notification.cpython-311.pyc
ADDED
|
Binary file (1.91 kB). View file
|
|
|
app/models/__pycache__/points.cpython-311.pyc
ADDED
|
Binary file (1.58 kB). View file
|
|
|
app/models/__pycache__/teacher.cpython-311.pyc
ADDED
|
Binary file (1.53 kB). View file
|
|
|
app/models/content.py
CHANGED
|
@@ -36,6 +36,7 @@ class UploadedContent(Base):
|
|
| 36 |
title = Column(Text, nullable=False)
|
| 37 |
content_url = Column(Text) # URL or file path
|
| 38 |
description = Column(Text) # Brief summary
|
|
|
|
| 39 |
|
| 40 |
# Source tracking
|
| 41 |
source_type = Column(Text, default="internal") # internal, external
|
|
|
|
| 36 |
title = Column(Text, nullable=False)
|
| 37 |
content_url = Column(Text) # URL or file path
|
| 38 |
description = Column(Text) # Brief summary
|
| 39 |
+
ai_summary = Column(Text) # AI-generated teacher-friendly summary
|
| 40 |
|
| 41 |
# Source tracking
|
| 42 |
source_type = Column(Text, default="internal") # internal, external
|
app/routes/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (975 Bytes). View file
|
|
|
app/routes/__pycache__/ai.cpython-311.pyc
ADDED
|
Binary file (10.5 kB). View file
|
|
|
app/routes/__pycache__/auth.cpython-311.pyc
ADDED
|
Binary file (5.41 kB). View file
|
|
|
app/routes/__pycache__/community.cpython-311.pyc
ADDED
|
Binary file (4.04 kB). View file
|
|
|
app/routes/__pycache__/concepts.cpython-311.pyc
ADDED
|
Binary file (3.57 kB). View file
|
|
|
app/routes/__pycache__/content.cpython-311.pyc
ADDED
|
Binary file (18.9 kB). View file
|
|
|
app/routes/__pycache__/help.cpython-311.pyc
ADDED
|
Binary file (11.3 kB). View file
|
|
|
app/routes/__pycache__/notifications.cpython-311.pyc
ADDED
|
Binary file (6.53 kB). View file
|
|
|
app/routes/__pycache__/points.cpython-311.pyc
ADDED
|
Binary file (1.9 kB). View file
|
|
|
app/routes/__pycache__/suggestions.cpython-311.pyc
ADDED
|
Binary file (5.02 kB). View file
|
|
|
app/routes/__pycache__/translate.cpython-311.pyc
ADDED
|
Binary file (4.18 kB). View file
|
|
|
app/routes/help.py
CHANGED
|
@@ -80,7 +80,11 @@ def create_help_request(
|
|
| 80 |
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
| 81 |
detail="Speech recognition not available"
|
| 82 |
)
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
if not query_text:
|
| 86 |
raise HTTPException(
|
|
@@ -90,11 +94,9 @@ def create_help_request(
|
|
| 90 |
|
| 91 |
# CRITICAL: Resolve to concept_id
|
| 92 |
# This is the core of the multilingual search system
|
| 93 |
-
concept_id, language, normalized_text = ConceptResolver.resolve_concept(
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
if detected_language:
|
| 97 |
-
language = detected_language
|
| 98 |
|
| 99 |
# Store help request for analytics
|
| 100 |
help_request = HelpRequest(
|
|
|
|
| 80 |
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
| 81 |
detail="Speech recognition not available"
|
| 82 |
)
|
| 83 |
+
language_hint = current_teacher.language_preference
|
| 84 |
+
query_text, detected_language = SpeechService.transcribe_audio(
|
| 85 |
+
request.audio_base64,
|
| 86 |
+
language_hint=language_hint
|
| 87 |
+
)
|
| 88 |
|
| 89 |
if not query_text:
|
| 90 |
raise HTTPException(
|
|
|
|
| 94 |
|
| 95 |
# CRITICAL: Resolve to concept_id
|
| 96 |
# This is the core of the multilingual search system
|
| 97 |
+
concept_id, language, normalized_text = ConceptResolver.resolve_concept(
|
| 98 |
+
db, query_text, speech_language=detected_language
|
| 99 |
+
)
|
|
|
|
|
|
|
| 100 |
|
| 101 |
# Store help request for analytics
|
| 102 |
help_request = HelpRequest(
|
app/routes/suggestions.py
CHANGED
|
@@ -29,6 +29,7 @@ router = APIRouter(prefix="/suggestions", tags=["Suggestions"])
|
|
| 29 |
def get_suggestions(
|
| 30 |
concept_id: str = Query(..., description="The resolved concept ID"),
|
| 31 |
language: Optional[str] = Query(None, description="Language override"),
|
|
|
|
| 32 |
limit: int = Query(10, ge=1, le=50),
|
| 33 |
db: Session = Depends(get_db),
|
| 34 |
current_teacher: Teacher = Depends(get_current_teacher)
|
|
@@ -65,7 +66,8 @@ def get_suggestions(
|
|
| 65 |
db,
|
| 66 |
concept_id,
|
| 67 |
target_lang,
|
| 68 |
-
limit
|
|
|
|
| 69 |
)
|
| 70 |
|
| 71 |
# Build response
|
|
@@ -88,8 +90,8 @@ def get_suggestions(
|
|
| 88 |
source_type=content.source_type,
|
| 89 |
is_verified=content.is_verified,
|
| 90 |
created_at=content.created_at,
|
| 91 |
-
|
| 92 |
-
|
| 93 |
))
|
| 94 |
source = item["source"]
|
| 95 |
else:
|
|
@@ -115,8 +117,8 @@ def get_suggestions(
|
|
| 115 |
source_type=content.source_type,
|
| 116 |
is_verified=content.is_verified,
|
| 117 |
created_at=content.created_at,
|
| 118 |
-
|
| 119 |
-
|
| 120 |
))
|
| 121 |
|
| 122 |
# Add warning message for unverified content
|
|
|
|
| 29 |
def get_suggestions(
|
| 30 |
concept_id: str = Query(..., description="The resolved concept ID"),
|
| 31 |
language: Optional[str] = Query(None, description="Language override"),
|
| 32 |
+
problem_description: Optional[str] = Query(None, description="Detailed problem context from teacher"),
|
| 33 |
limit: int = Query(10, ge=1, le=50),
|
| 34 |
db: Session = Depends(get_db),
|
| 35 |
current_teacher: Teacher = Depends(get_current_teacher)
|
|
|
|
| 66 |
db,
|
| 67 |
concept_id,
|
| 68 |
target_lang,
|
| 69 |
+
limit,
|
| 70 |
+
problem_description
|
| 71 |
)
|
| 72 |
|
| 73 |
# Build response
|
|
|
|
| 90 |
source_type=content.source_type,
|
| 91 |
is_verified=content.is_verified,
|
| 92 |
created_at=content.created_at,
|
| 93 |
+
uploader_name=item["uploader_name"],
|
| 94 |
+
ai_summary=content.ai_summary
|
| 95 |
))
|
| 96 |
source = item["source"]
|
| 97 |
else:
|
|
|
|
| 117 |
source_type=content.source_type,
|
| 118 |
is_verified=content.is_verified,
|
| 119 |
created_at=content.created_at,
|
| 120 |
+
uploader_name="Google Search",
|
| 121 |
+
ai_summary=content.ai_summary
|
| 122 |
))
|
| 123 |
|
| 124 |
# Add warning message for unverified content
|
app/schemas/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.11 kB). View file
|
|
|
app/schemas/__pycache__/auth.cpython-311.pyc
ADDED
|
Binary file (1.21 kB). View file
|
|
|
app/schemas/__pycache__/concept.cpython-311.pyc
ADDED
|
Binary file (1.75 kB). View file
|
|
|
app/schemas/__pycache__/content.cpython-311.pyc
ADDED
|
Binary file (3.25 kB). View file
|
|
|
app/schemas/__pycache__/help_request.cpython-311.pyc
ADDED
|
Binary file (1.97 kB). View file
|
|
|
app/schemas/__pycache__/points.cpython-311.pyc
ADDED
|
Binary file (1.39 kB). View file
|
|
|
app/schemas/__pycache__/teacher.cpython-311.pyc
ADDED
|
Binary file (1.8 kB). View file
|
|
|
app/schemas/content.py
CHANGED
|
@@ -35,6 +35,7 @@ class ContentResponse(BaseModel):
|
|
| 35 |
# Computed fields for display
|
| 36 |
feedback_score: Optional[float] = None
|
| 37 |
uploader_name: Optional[str] = None
|
|
|
|
| 38 |
likes_count: int = 0
|
| 39 |
views_count: int = 0
|
| 40 |
user_liked: bool = False
|
|
|
|
| 35 |
# Computed fields for display
|
| 36 |
feedback_score: Optional[float] = None
|
| 37 |
uploader_name: Optional[str] = None
|
| 38 |
+
ai_summary: Optional[str] = None
|
| 39 |
likes_count: int = 0
|
| 40 |
views_count: int = 0
|
| 41 |
user_liked: bool = False
|
app/services/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (647 Bytes). View file
|
|
|
app/services/__pycache__/auth.cpython-311.pyc
ADDED
|
Binary file (4.96 kB). View file
|
|
|
app/services/__pycache__/cloudinary_service.cpython-311.pyc
ADDED
|
Binary file (7.69 kB). View file
|
|
|
app/services/__pycache__/concept_resolver.cpython-311.pyc
ADDED
|
Binary file (23 kB). View file
|
|
|
app/services/__pycache__/content_service.cpython-311.pyc
ADDED
|
Binary file (14.8 kB). View file
|
|
|
app/services/__pycache__/gemini_service.cpython-311.pyc
ADDED
|
Binary file (19.1 kB). View file
|
|
|
app/services/__pycache__/points_service.cpython-311.pyc
ADDED
|
Binary file (4.66 kB). View file
|
|
|
app/services/__pycache__/speech_service.cpython-311.pyc
ADDED
|
Binary file (4.36 kB). View file
|
|
|
app/services/__pycache__/translation_service.cpython-311.pyc
ADDED
|
Binary file (11.8 kB). View file
|
|
|
app/services/concept_resolver.py
CHANGED
|
@@ -165,13 +165,14 @@ class ConceptResolver:
|
|
| 165 |
return text
|
| 166 |
|
| 167 |
@staticmethod
|
| 168 |
-
def resolve_concept(db: Session, text: str) -> Tuple[Optional[str], str, str]:
|
| 169 |
"""
|
| 170 |
Main resolution function.
|
| 171 |
|
| 172 |
Args:
|
| 173 |
db: Database session
|
| 174 |
text: User input in any language
|
|
|
|
| 175 |
|
| 176 |
Returns:
|
| 177 |
Tuple of (concept_id, detected_language, normalized_text)
|
|
@@ -179,6 +180,14 @@ class ConceptResolver:
|
|
| 179 |
"""
|
| 180 |
# Step 1: Detect language
|
| 181 |
language = ConceptResolver.detect_language(text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
print(f"[ConceptResolver] Input: '{text}' | Language: {language}")
|
| 183 |
|
| 184 |
# Step 2: Normalize text
|
|
|
|
| 165 |
return text
|
| 166 |
|
| 167 |
@staticmethod
|
| 168 |
+
def resolve_concept(db: Session, text: str, speech_language: Optional[str] = None) -> Tuple[Optional[str], str, str]:
|
| 169 |
"""
|
| 170 |
Main resolution function.
|
| 171 |
|
| 172 |
Args:
|
| 173 |
db: Database session
|
| 174 |
text: User input in any language
|
| 175 |
+
speech_language: Optional language detected by speech engine
|
| 176 |
|
| 177 |
Returns:
|
| 178 |
Tuple of (concept_id, detected_language, normalized_text)
|
|
|
|
| 180 |
"""
|
| 181 |
# Step 1: Detect language
|
| 182 |
language = ConceptResolver.detect_language(text)
|
| 183 |
+
|
| 184 |
+
# If script detection says "en" but speech engine detected "kn" or "hi",
|
| 185 |
+
# it's likely transliterated text (Hinglish/Kanglish).
|
| 186 |
+
# In this case, trust the speech engine.
|
| 187 |
+
if language == "en" and speech_language in ["kn", "hi"]:
|
| 188 |
+
language = speech_language
|
| 189 |
+
print(f"[ConceptResolver] Overriding script language 'en' with speech language '{language}' (Transliteration detected)")
|
| 190 |
+
|
| 191 |
print(f"[ConceptResolver] Input: '{text}' | Language: {language}")
|
| 192 |
|
| 193 |
# Step 2: Normalize text
|
app/services/content_service.py
CHANGED
|
@@ -15,6 +15,7 @@ import uuid
|
|
| 15 |
|
| 16 |
from app.models.content import UploadedContent, ContentFeedback, ContentInteraction
|
| 17 |
from app.models.teacher import Teacher
|
|
|
|
| 18 |
from app.services.gemini_service import GeminiService
|
| 19 |
|
| 20 |
|
|
@@ -30,7 +31,8 @@ class ContentService:
|
|
| 30 |
db: Session,
|
| 31 |
concept_id: str,
|
| 32 |
teacher_language: str = "en",
|
| 33 |
-
limit: int = 10
|
|
|
|
| 34 |
) -> Tuple[List[UploadedContent], str]:
|
| 35 |
"""
|
| 36 |
Get content suggestions for a concept.
|
|
@@ -114,7 +116,11 @@ class ContentService:
|
|
| 114 |
|
| 115 |
# Step 4: Use Google Web Search via Gemini to find external content
|
| 116 |
print(f"[ContentService] No content found for '{concept_id}', trying Google Web Search...")
|
| 117 |
-
search_results = GeminiService.google_web_search(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
if search_results:
|
| 120 |
# Create/Get content entries from search results
|
|
@@ -130,6 +136,17 @@ class ContentService:
|
|
| 130 |
).first()
|
| 131 |
|
| 132 |
if existing_content:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
web_content.append(existing_content)
|
| 134 |
continue
|
| 135 |
|
|
@@ -140,14 +157,30 @@ class ContentService:
|
|
| 140 |
elif url.endswith(".pdf"):
|
| 141 |
content_type = "document"
|
| 142 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
# Persist new content (without specific uploader)
|
| 144 |
new_content = UploadedContent(
|
| 145 |
id=uuid.uuid4(), # Explicitly setting UUID or letting DB handle it
|
| 146 |
title=result.get("title", "External Resource"),
|
| 147 |
description=result.get("snippet", "Found via Google Search"),
|
|
|
|
| 148 |
content_type=content_type,
|
| 149 |
content_url=url,
|
| 150 |
concept_id=concept_id,
|
|
|
|
|
|
|
| 151 |
language="en", # Default to English for external content
|
| 152 |
source_type="external",
|
| 153 |
is_verified=False,
|
|
@@ -174,14 +207,15 @@ class ContentService:
|
|
| 174 |
db: Session,
|
| 175 |
concept_id: str,
|
| 176 |
teacher_language: str = "en",
|
| 177 |
-
limit: int = 10
|
|
|
|
| 178 |
) -> List[dict]:
|
| 179 |
"""
|
| 180 |
Get content with computed feedback scores.
|
| 181 |
Returns dictionaries with content and score.
|
| 182 |
"""
|
| 183 |
content_list, source = ContentService.get_suggestions(
|
| 184 |
-
db, concept_id, teacher_language, limit
|
| 185 |
)
|
| 186 |
|
| 187 |
# For Google Search results, return directly without DB queries
|
|
|
|
| 15 |
|
| 16 |
from app.models.content import UploadedContent, ContentFeedback, ContentInteraction
|
| 17 |
from app.models.teacher import Teacher
|
| 18 |
+
from app.models.concept import Concept
|
| 19 |
from app.services.gemini_service import GeminiService
|
| 20 |
|
| 21 |
|
|
|
|
| 31 |
db: Session,
|
| 32 |
concept_id: str,
|
| 33 |
teacher_language: str = "en",
|
| 34 |
+
limit: int = 10,
|
| 35 |
+
problem_description: Optional[str] = None
|
| 36 |
) -> Tuple[List[UploadedContent], str]:
|
| 37 |
"""
|
| 38 |
Get content suggestions for a concept.
|
|
|
|
| 116 |
|
| 117 |
# Step 4: Use Google Web Search via Gemini to find external content
|
| 118 |
print(f"[ContentService] No content found for '{concept_id}', trying Google Web Search...")
|
| 119 |
+
search_results = GeminiService.google_web_search(
|
| 120 |
+
concept_id.replace("_", " "),
|
| 121 |
+
num_results=5,
|
| 122 |
+
problem_description=problem_description
|
| 123 |
+
)
|
| 124 |
|
| 125 |
if search_results:
|
| 126 |
# Create/Get content entries from search results
|
|
|
|
| 136 |
).first()
|
| 137 |
|
| 138 |
if existing_content:
|
| 139 |
+
# If we have a specific problem description, re-generate summary even for existing content
|
| 140 |
+
# to ensure it's tailored to the current session.
|
| 141 |
+
if problem_description:
|
| 142 |
+
# Re-generate summary dynamically (won't persist to DB unless explicit save)
|
| 143 |
+
# This gives a "session-aware" summary
|
| 144 |
+
existing_content.ai_summary = GeminiService.generate_summary(
|
| 145 |
+
title=existing_content.title,
|
| 146 |
+
snippet=existing_content.description,
|
| 147 |
+
topic=concept_id.replace("_", " ").title(),
|
| 148 |
+
problem_description=problem_description
|
| 149 |
+
)
|
| 150 |
web_content.append(existing_content)
|
| 151 |
continue
|
| 152 |
|
|
|
|
| 157 |
elif url.endswith(".pdf"):
|
| 158 |
content_type = "document"
|
| 159 |
|
| 160 |
+
# Get concept details for subject and grade
|
| 161 |
+
concept = db.query(Concept).filter(Concept.concept_id == concept_id).first()
|
| 162 |
+
subject = concept.subject if concept else "General"
|
| 163 |
+
grade = concept.grade if concept else "All"
|
| 164 |
+
|
| 165 |
+
# Generate AI summary for better teacher experience
|
| 166 |
+
ai_summary = GeminiService.generate_summary(
|
| 167 |
+
title=result.get("title", "External Resource"),
|
| 168 |
+
snippet=result.get("snippet", ""),
|
| 169 |
+
topic=concept_id.replace("_", " ").title(),
|
| 170 |
+
problem_description=problem_description
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
# Persist new content (without specific uploader)
|
| 174 |
new_content = UploadedContent(
|
| 175 |
id=uuid.uuid4(), # Explicitly setting UUID or letting DB handle it
|
| 176 |
title=result.get("title", "External Resource"),
|
| 177 |
description=result.get("snippet", "Found via Google Search"),
|
| 178 |
+
ai_summary=ai_summary,
|
| 179 |
content_type=content_type,
|
| 180 |
content_url=url,
|
| 181 |
concept_id=concept_id,
|
| 182 |
+
subject=subject,
|
| 183 |
+
grade=grade,
|
| 184 |
language="en", # Default to English for external content
|
| 185 |
source_type="external",
|
| 186 |
is_verified=False,
|
|
|
|
| 207 |
db: Session,
|
| 208 |
concept_id: str,
|
| 209 |
teacher_language: str = "en",
|
| 210 |
+
limit: int = 10,
|
| 211 |
+
problem_description: Optional[str] = None
|
| 212 |
) -> List[dict]:
|
| 213 |
"""
|
| 214 |
Get content with computed feedback scores.
|
| 215 |
Returns dictionaries with content and score.
|
| 216 |
"""
|
| 217 |
content_list, source = ContentService.get_suggestions(
|
| 218 |
+
db, concept_id, teacher_language, limit, problem_description
|
| 219 |
)
|
| 220 |
|
| 221 |
# For Google Search results, return directly without DB queries
|
app/services/gemini_service.py
CHANGED
|
@@ -96,7 +96,7 @@ class GeminiService:
|
|
| 96 |
raise e
|
| 97 |
|
| 98 |
@staticmethod
|
| 99 |
-
def google_web_search(query: str, num_results: int = 5) -> List[dict]:
|
| 100 |
"""
|
| 101 |
Use Google Search via Gemini API to find educational content.
|
| 102 |
|
|
@@ -107,10 +107,14 @@ class GeminiService:
|
|
| 107 |
return []
|
| 108 |
|
| 109 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
# Use google_search tool with Gemini
|
| 111 |
data = {
|
| 112 |
"contents": [{
|
| 113 |
-
"parts": [{"text": f"Find {num_results}
|
| 114 |
}],
|
| 115 |
"tools": [{
|
| 116 |
"google_search": {}
|
|
@@ -396,3 +400,48 @@ Rules:
|
|
| 396 |
"search_keywords": [query],
|
| 397 |
"error": str(e)
|
| 398 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
raise e
|
| 97 |
|
| 98 |
@staticmethod
|
| 99 |
+
def google_web_search(query: str, num_results: int = 5, problem_description: str = None) -> List[dict]:
|
| 100 |
"""
|
| 101 |
Use Google Search via Gemini API to find educational content.
|
| 102 |
|
|
|
|
| 107 |
return []
|
| 108 |
|
| 109 |
try:
|
| 110 |
+
search_query = f"educational resources about {query}"
|
| 111 |
+
if problem_description:
|
| 112 |
+
search_query += f" specifically help for: {problem_description}"
|
| 113 |
+
|
| 114 |
# Use google_search tool with Gemini
|
| 115 |
data = {
|
| 116 |
"contents": [{
|
| 117 |
+
"parts": [{"text": f"Find {num_results} {search_query}. For each result, provide the title, URL, and a brief description. Focus on educational content suitable for teachers and students."}]
|
| 118 |
}],
|
| 119 |
"tools": [{
|
| 120 |
"google_search": {}
|
|
|
|
| 400 |
"search_keywords": [query],
|
| 401 |
"error": str(e)
|
| 402 |
}
|
| 403 |
+
|
| 404 |
+
@staticmethod
|
| 405 |
+
def generate_summary(
|
| 406 |
+
title: str,
|
| 407 |
+
snippet: str,
|
| 408 |
+
topic: str = None,
|
| 409 |
+
problem_description: str = None
|
| 410 |
+
) -> str:
|
| 411 |
+
"""
|
| 412 |
+
Generate a teacher-friendly summary for a suggested resource.
|
| 413 |
+
|
| 414 |
+
Args:
|
| 415 |
+
title: Title of the resource
|
| 416 |
+
snippet: Search snippet or context
|
| 417 |
+
topic: The concept/topic name
|
| 418 |
+
problem_description: Specific problem context provided by teacher
|
| 419 |
+
|
| 420 |
+
Returns:
|
| 421 |
+
A concise, high-quality summary
|
| 422 |
+
"""
|
| 423 |
+
if not GeminiService.is_available():
|
| 424 |
+
return snippet or "No summary available."
|
| 425 |
+
|
| 426 |
+
try:
|
| 427 |
+
prompt = f"""You are an expert educational consultant helping teachers in India.
|
| 428 |
+
Write a concise, teacher-friendly summary (2-3 sentences) explaining how an educational resource directly solves a teacher's specific classroom problem.
|
| 429 |
+
|
| 430 |
+
Resource Title: {title}
|
| 431 |
+
Resource Context: {snippet}
|
| 432 |
+
{f'Core Topic: {topic}' if topic else ''}
|
| 433 |
+
{f"Teacher's Specific Problem: {problem_description}" if problem_description else ""}
|
| 434 |
+
|
| 435 |
+
Rules:
|
| 436 |
+
1. FOCUS: If a specific problem is provided, the summary MUST explain how this resource solves THAT specific problem.
|
| 437 |
+
2. AVOID: Do not waste words describing the platform (e.g., "Khan Academy offers videos"). Instead, say "This resource helps you explain [Topic] using [Method] to address [Problem]".
|
| 438 |
+
3. DEPTH: Mention specific features or content (from the snippet) that are relevant to the problem.
|
| 439 |
+
4. TONE: Professional, practical, and highly encouraging.
|
| 440 |
+
5. CONCISENESS: Under 60 words. No intro/outro.
|
| 441 |
+
|
| 442 |
+
Summary:"""
|
| 443 |
+
|
| 444 |
+
return GeminiService._call_gemini(prompt).strip()
|
| 445 |
+
except Exception as e:
|
| 446 |
+
print(f"[GeminiService] Summary generation failed: {e}")
|
| 447 |
+
return snippet or "No summary available."
|
app/services/speech_service.py
CHANGED
|
@@ -50,12 +50,13 @@ class SpeechService:
|
|
| 50 |
"""
|
| 51 |
|
| 52 |
@staticmethod
|
| 53 |
-
def transcribe_audio(audio_base64: str) -> Tuple[str, str]:
|
| 54 |
"""
|
| 55 |
Transcribe base64-encoded audio to text.
|
| 56 |
|
| 57 |
Args:
|
| 58 |
audio_base64: Base64 encoded audio data
|
|
|
|
| 59 |
|
| 60 |
Returns:
|
| 61 |
Tuple of (transcribed_text, detected_language)
|
|
@@ -76,10 +77,18 @@ class SpeechService:
|
|
| 76 |
try:
|
| 77 |
model = get_whisper_model()
|
| 78 |
|
| 79 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
result = model.transcribe(
|
| 81 |
tmp_path,
|
| 82 |
task="transcribe",
|
|
|
|
| 83 |
)
|
| 84 |
|
| 85 |
text = result["text"].strip()
|
|
|
|
| 50 |
"""
|
| 51 |
|
| 52 |
@staticmethod
|
| 53 |
+
def transcribe_audio(audio_base64: str, language_hint: str = None) -> Tuple[str, str]:
|
| 54 |
"""
|
| 55 |
Transcribe base64-encoded audio to text.
|
| 56 |
|
| 57 |
Args:
|
| 58 |
audio_base64: Base64 encoded audio data
|
| 59 |
+
language_hint: Optional language code hint (en, kn, hi)
|
| 60 |
|
| 61 |
Returns:
|
| 62 |
Tuple of (transcribed_text, detected_language)
|
|
|
|
| 77 |
try:
|
| 78 |
model = get_whisper_model()
|
| 79 |
|
| 80 |
+
# Map our hint to Whisper language codes
|
| 81 |
+
whisper_lang = None
|
| 82 |
+
if language_hint == "kn":
|
| 83 |
+
whisper_lang = "kn"
|
| 84 |
+
elif language_hint == "hi":
|
| 85 |
+
whisper_lang = "hi"
|
| 86 |
+
|
| 87 |
+
# Transcribe with language detection/hint
|
| 88 |
result = model.transcribe(
|
| 89 |
tmp_path,
|
| 90 |
task="transcribe",
|
| 91 |
+
language=whisper_lang
|
| 92 |
)
|
| 93 |
|
| 94 |
text = result["text"].strip()
|