Skip to content

Commit 02509df

Browse files
felipemontoyaclaude
andcommitted
feat: moving the aiworkflowview from regular django view to drf apiview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 972f2fe commit 02509df

4 files changed

Lines changed: 19 additions & 20 deletions

File tree

backend/openedx_ai_extensions/api/v1/workflows/views.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
import logging
88
from datetime import datetime, timezone
99

10-
from django.contrib.auth.decorators import login_required
1110
from django.core.exceptions import ValidationError
1211
from django.http import JsonResponse, StreamingHttpResponse
1312
from django.utils.decorators import method_decorator
14-
from django.views import View
1513
from opaque_keys import InvalidKeyError
1614
from opaque_keys.edx.keys import CourseKey, UsageKey
1715
from rest_framework import status
@@ -81,27 +79,22 @@ def get_context_from_request(request):
8179
return validated_context
8280

8381

84-
@method_decorator(login_required, name="dispatch")
85-
@method_decorator(handle_ai_errors, name="dispatch")
86-
class AIGenericWorkflowView(View):
82+
class AIGenericWorkflowView(APIView):
8783
"""
8884
AI Workflow API endpoint
8985
"""
9086

87+
permission_classes = [IsAuthenticated]
88+
89+
@method_decorator(handle_ai_errors)
9190
def post(self, request):
9291
"""Common handler for GET and POST requests"""
9392

9493
context = get_context_from_request(request)
9594
workflow_profile = AIWorkflowScope.get_profile(**context)
9695

97-
request_body = {}
98-
if request.body:
99-
try:
100-
request_body = json.loads(request.body.decode("utf-8"))
101-
except json.JSONDecodeError as e:
102-
raise ValidationError("Invalid JSON format in request body.") from e
103-
action = request_body.get("action", "")
104-
user_input = request_body.get("user_input", {})
96+
action = request.data.get("action", "")
97+
user_input = request.data.get("user_input", {})
10598

10699
result = workflow_profile.execute(
107100
user_input=user_input,

backend/openedx_ai_extensions/decorators.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
APIConnectionError,
1313
AuthenticationError,
1414
ContextWindowExceededError,
15+
NotFoundError,
1516
RateLimitError,
1617
ServiceUnavailableError,
1718
Timeout,
@@ -28,6 +29,11 @@
2829
"message": "The AI service is currently unavailable due to an authentication error.",
2930
"status": status.HTTP_500_INTERNAL_SERVER_ERROR,
3031
},
32+
NotFoundError: {
33+
"code": "llm_config_error",
34+
"message": "The AI service is misconfigured. Please check the LLM settings.",
35+
"status": status.HTTP_500_INTERNAL_SERVER_ERROR,
36+
},
3137
RateLimitError: {
3238
"code": "rate_limit_exceeded",
3339
"message": "The AI service is currently busy. Please try again later.",

backend/openedx_ai_extensions/workflows/orchestrators/session_based_orchestrator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ def _execute_orchestrator_async(task_self, session_id, action, params=None):
102102
raise
103103

104104
except Exception as e:
105-
logger.error(f"Task {task_id}: Error executing {action} for session {session_id}: {str(e)}")
105+
logger.error(
106+
"Task %s: Error executing %s for session %s: %s",
107+
task_id, action, session_id, str(e),
108+
exc_info=True,
109+
)
106110
session.metadata['task_status'] = 'error'
107111
session.metadata['task_error'] = str(e)
108112
session.save(update_fields=['metadata'])

backend/tests/test_api.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,9 @@ def test_workflows_endpoint_requires_authentication(api_client): # pylint: disa
126126
"""
127127
url = reverse("openedx_ai_extensions:api:v1:aiext_workflows")
128128

129-
# Test POST without authentication
129+
# DRF IsAuthenticated with SessionAuthentication returns 403 (no WWW-Authenticate challenge)
130130
response = api_client.post(url, {}, format="json")
131-
assert response.status_code == 302 # Redirect to login
132-
133-
# Test GET without authentication
134-
response = api_client.get(url)
135-
assert response.status_code == 302 # Redirect to login
131+
assert response.status_code == 403
136132

137133

138134
@pytest.mark.django_db

0 commit comments

Comments
 (0)