-
Notifications
You must be signed in to change notification settings - Fork 10
TTS Evaluation: TTS Results signed URLs #666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0e2b5a2
ce8720c
325d622
b34e4bb
4db2689
4928ce5
60714bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,11 @@ | |
|
|
||
| import logging | ||
|
|
||
| from fastapi import APIRouter, Body, Depends, HTTPException | ||
| from fastapi import APIRouter, Body, Depends, HTTPException, Query | ||
|
|
||
| from app.api.deps import AuthContextDep, SessionDep | ||
| from app.api.permissions import Permission, require_permission | ||
| from app.core.cloud import get_cloud_storage | ||
| from app.crud.tts_evaluations import ( | ||
| get_tts_result_by_id, | ||
| update_tts_human_feedback, | ||
|
|
@@ -92,6 +93,9 @@ def get_result( | |
| session: SessionDep, | ||
| auth_context: AuthContextDep, | ||
| result_id: int, | ||
| include_signed_url: bool = Query( | ||
| False, description="Include signed URL for generated audio file" | ||
| ), | ||
| ) -> APIResponse[TTSResultPublic]: | ||
| """Get a TTS result by ID.""" | ||
| result = get_tts_result_by_id( | ||
|
|
@@ -104,4 +108,13 @@ def get_result( | |
| if not result: | ||
| raise HTTPException(status_code=404, detail="Result not found") | ||
|
|
||
| return APIResponse.success_response(data=TTSResultPublic.from_model(result)) | ||
| signed_url = None | ||
| if include_signed_url and result.object_store_url is not None: | ||
| storage = get_cloud_storage( | ||
| session=session, project_id=auth_context.project_.id | ||
| ) | ||
| signed_url = storage.get_signed_url(result.object_store_url) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if result.object_store url is None then it would raise some issue . so add some condition if result.object_store_url is not None. if its not an issue then u can ignore it |
||
|
|
||
| return APIResponse.success_response( | ||
| data=TTSResultPublic.from_model(result, signed_url=signed_url) | ||
|
Comment on lines
+111
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling for cloud storage operations will cause 500 errors. Multiple unhandled exception sources:
All of these will return 500 errors instead of gracefully returning 🛡️ Proposed fix with comprehensive error handling signed_url = None
if include_signed_url:
- storage = get_cloud_storage(
- session=session, project_id=auth_context.project_.id
- )
- signed_url = storage.get_signed_url(result.object_store_url)
-
- return APIResponse.success_response(
- data=TTSResultPublic.from_model(result, signed_url=signed_url)
- )
+ if result.object_store_url:
+ try:
+ storage = get_cloud_storage(
+ session=session, project_id=auth_context.project_.id
+ )
+ signed_url = storage.get_signed_url(result.object_store_url)
+ except Exception:
+ logger.warning(
+ f"[get_result] Failed to generate signed URL for result {result_id}"
+ )
+ signed_url = None
+
+ return APIResponse.success_response(
+ data=TTSResultPublic.from_model(result, signed_url=signed_url)
+ )🤖 Prompt for AI Agents |
||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,17 +2,17 @@ | |
|
|
||
| import logging | ||
|
|
||
| from sqlmodel import Session, select, func | ||
| from sqlmodel import Session, func, select | ||
|
|
||
| from app.core.cloud.storage import CloudStorage | ||
| from app.core.exception_handlers import HTTPException | ||
| from app.core.util import now | ||
| from app.models.file import File | ||
| from app.core.cloud.storage import CloudStorage | ||
| from app.models.stt_evaluation import ( | ||
| STTResult, | ||
| STTResultWithSample, | ||
| STTSample, | ||
| STTSamplePublic, | ||
| STTResultWithSample, | ||
| ) | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -103,14 +103,7 @@ def get_results_by_run_id( | |
| # Convert to response models | ||
| results = [] | ||
| for result, sample, file in rows: | ||
| signed_url = None | ||
| if storage: | ||
| try: | ||
| signed_url = storage.get_signed_url(file.object_store_url) | ||
| except Exception as e: | ||
| logger.warning( | ||
| f"[get_results_by_run_id] Failed to generate signed URL: {e}" | ||
| ) | ||
| signed_url = storage.get_signed_url(file.object_store_url) if storage else None | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling for The This contradicts the PR description's claim of "improved error handling...to gracefully handle failures without raising exceptions" and breaks the established codebase pattern (see 🛡️ Proposed fix to restore defensive error handling results = []
for result, sample, file in rows:
- signed_url = storage.get_signed_url(file.object_store_url) if storage else None
+ signed_url = None
+ if storage:
+ try:
+ signed_url = storage.get_signed_url(file.object_store_url)
+ except Exception:
+ logger.warning(
+ f"[get_results_by_run_id] Failed to generate signed URL for file {file.id}"
+ )
+ signed_url = None🤖 Prompt for AI Agents |
||
|
|
||
| sample_public = STTSamplePublic( | ||
| id=sample.id, | ||
|
|
@@ -226,4 +219,4 @@ def count_results_by_status( | |
|
|
||
| rows = session.exec(statement).all() | ||
|
|
||
| return {status: count for status, count in rows} | ||
| return dict(rows) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
|
|
||
| from sqlmodel import Session, func, select | ||
|
|
||
| from app.core.cloud.storage import CloudStorage | ||
| from app.core.exception_handlers import HTTPException | ||
| from app.core.util import now | ||
| from app.models.job import JobStatus | ||
|
|
@@ -104,6 +105,7 @@ def get_results_by_run_id( | |
| run_id: int, | ||
| org_id: int, | ||
| project_id: int, | ||
| storage: CloudStorage | None = None, | ||
| ) -> tuple[list[TTSResultPublic], int]: | ||
| """Get all results for an evaluation run. | ||
|
|
||
|
|
@@ -112,6 +114,7 @@ def get_results_by_run_id( | |
| run_id: Run ID | ||
| org_id: Organization ID | ||
| project_id: Project ID | ||
| storage: Optional cloud storage instance for generating signed URLs | ||
|
|
||
| Returns: | ||
| tuple[list[TTSResultPublic], int]: Results and total count | ||
|
|
@@ -127,7 +130,12 @@ def get_results_by_run_id( | |
| rows = session.exec(statement).all() | ||
| total = len(rows) | ||
|
|
||
| results = [TTSResultPublic.from_model(result) for result in rows] | ||
| results = [] | ||
| for result in rows: | ||
| signed_url = ( | ||
| storage.get_signed_url(result.object_store_url) if storage else None | ||
| ) | ||
| results.append(TTSResultPublic.from_model(result, signed_url=signed_url)) | ||
|
Comment on lines
+133
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling and null check for signed URL generation. Two issues:
🛡️ Proposed fix with error handling and null check results = []
for result in rows:
- signed_url = (
- storage.get_signed_url(result.object_store_url) if storage else None
- )
+ signed_url = None
+ if storage and result.object_store_url:
+ try:
+ signed_url = storage.get_signed_url(result.object_store_url)
+ except Exception:
+ logger.warning(
+ f"[get_results_by_run_id] Failed to generate signed URL for result {result.id}"
+ )
+ signed_url = None
results.append(TTSResultPublic.from_model(result, signed_url=signed_url))🤖 Prompt for AI Agents |
||
|
|
||
| return results, total | ||
|
|
||
|
|
@@ -294,4 +302,4 @@ def count_results_by_status( | |
|
|
||
| rows = session.exec(statement).all() | ||
|
|
||
| return {status: count for status, count in rows} | ||
| return dict(rows) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,14 @@ | ||
| import pytest | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
| from fastapi.testclient import TestClient | ||
| from sqlmodel import Session | ||
|
|
||
| from app.models import EvaluationDataset, EvaluationRun, File, FileType | ||
| from app.models.stt_evaluation import STTSample, STTResult, EvaluationType | ||
| from app.core.util import now | ||
| from app.crud.language import get_language_by_locale | ||
| from app.models import EvaluationDataset, EvaluationRun, File, FileType | ||
| from app.models.stt_evaluation import EvaluationType, STTResult, STTSample | ||
|
Comment on lines
+3
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep the failure-path mocks exception-based so these tests cover the real contract. Line 744 and Line 1077 now return 🧪 Suggested fix+from app.core.cloud.storage import CloudStorageError
from app.core.util import now
@@
- mock_storage.get_signed_url.return_value = None
+ mock_storage.get_signed_url.side_effect = CloudStorageError("S3 error")
@@
- mock_storage.get_signed_url.return_value = None
+ mock_storage.get_signed_url.side_effect = CloudStorageError("S3 error")Also applies to: 744-745, 1077-1078 🤖 Prompt for AI Agents |
||
| from app.tests.utils.auth import TestAuthContext | ||
| from app.core.util import now | ||
|
|
||
|
|
||
| # Helper functions | ||
|
|
@@ -741,9 +741,7 @@ def test_get_stt_dataset_signed_url_failure( | |
| """Test getting an STT dataset when signed URL generation fails.""" | ||
| # Mock cloud storage to raise an exception | ||
| mock_storage = MagicMock() | ||
| mock_storage.get_signed_url.side_effect = Exception( | ||
| "Failed to generate signed URL" | ||
| ) | ||
| mock_storage.get_signed_url.return_value = None | ||
| mock_get_cloud_storage.return_value = mock_storage | ||
|
|
||
| dataset = create_test_stt_dataset( | ||
|
|
@@ -1076,9 +1074,7 @@ def test_get_stt_run_signed_url_failure( | |
| """Test getting an STT run when signed URL generation fails.""" | ||
| # Mock cloud storage to raise an exception | ||
| mock_storage = MagicMock() | ||
| mock_storage.get_signed_url.side_effect = Exception( | ||
| "Failed to generate signed URL" | ||
| ) | ||
| mock_storage.get_signed_url.return_value = None | ||
| mock_get_cloud_storage.return_value = mock_storage | ||
|
|
||
| # Create dataset, sample, run, and result | ||
|
|
@@ -1278,7 +1274,7 @@ def test_list_audio_files_with_signed_urls( | |
| mock_get_cloud_storage.return_value = mock_storage | ||
|
|
||
| # Create test file | ||
| file = create_test_file( | ||
| _file = create_test_file( | ||
| db=db, | ||
| organization_id=user_api_key.organization_id, | ||
| project_id=user_api_key.project_id, | ||
|
|
@@ -1314,7 +1310,7 @@ def test_list_audio_files_without_signed_urls( | |
| ) -> None: | ||
| """Test listing audio files without signed URLs.""" | ||
| # Create test file | ||
| file = create_test_file( | ||
| _file = create_test_file( | ||
| db=db, | ||
| organization_id=user_api_key.organization_id, | ||
| project_id=user_api_key.project_id, | ||
|
|
@@ -1345,7 +1341,7 @@ def test_list_audio_files_project_isolation( | |
| ) -> None: | ||
| """Test that audio files are isolated by project.""" | ||
| # Create file in user's project | ||
| user_file = create_test_file( | ||
| _user_file = create_test_file( | ||
| db=db, | ||
| organization_id=user_api_key.organization_id, | ||
| project_id=user_api_key.project_id, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for
storage.get_signed_url()will cause request failures.Same issue as in
backend/app/crud/stt_evaluations/result.py: theget_signed_urlmethod raisesCloudStorageErroron AWS failures. Without try/except, any URL generation failure will return a 500 error to the client instead of gracefully settingsigned_url = Nonefor that sample.🛡️ Proposed fix to restore defensive error handling
samples = [] for s in sample_records: signed_url = None - if storage and s.file_id in file_map: - signed_url = storage.get_signed_url( - file_map[s.file_id].object_store_url - ) + if storage and s.file_id in file_map: + try: + signed_url = storage.get_signed_url( + file_map[s.file_id].object_store_url + ) + except Exception: + logger.warning( + f"[get_dataset] Failed to generate signed URL for file {s.file_id}" + ) + signed_url = None🤖 Prompt for AI Agents