Skip to content

Commit 9948c2d

Browse files
authored
Merge pull request #262 from AvaCodeSolutions/feat/258/ai-tool
feat: #258 add feature to edit paragraph with AI assistant
2 parents debf92e + 31cada8 commit 9948c2d

17 files changed

Lines changed: 723 additions & 12 deletions

File tree

django_email_learning/ai/__init__.py

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from typing import Protocol
2+
3+
4+
class AiServiceProtocol(Protocol):
5+
def edit_text(self, text: str, model: str) -> str:
6+
...

django_email_learning/ai/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class AiConfig(AppConfig):
5+
name = "django_email_learning.ai"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from enum import Enum
2+
from .open_ai_adapter import OpenAiAdapter
3+
from .ai_service_protocol import AiServiceProtocol
4+
5+
6+
class LanguageModel(Enum):
7+
GPT_4O_MINI = ("gpt-4o-mini", OpenAiAdapter)
8+
GPT_5_NANO = ("gpt-5-nano", OpenAiAdapter)
9+
GPT_5_MINI = ("gpt-5-mini", OpenAiAdapter)
10+
11+
def __init__(self, model_name: str, adapter_class: type[AiServiceProtocol]) -> None:
12+
self.model_name = model_name
13+
self.adapter_class = adapter_class
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from .ai_service_protocol import AiServiceProtocol
2+
from openai import OpenAI
3+
from django.conf import settings
4+
import os
5+
6+
7+
DJANGO_EMAIL_LEARNING_CONFIG = getattr(settings, "DJANGO_EMAIL_LEARNING", {})
8+
9+
10+
class OpenAiAdapter(AiServiceProtocol):
11+
def __init__(self) -> None:
12+
API_KEY = DJANGO_EMAIL_LEARNING_CONFIG.get("AI", {}).get(
13+
"OPENAI_API_KEY", os.getenv("OPENAI_API_KEY")
14+
)
15+
if not API_KEY:
16+
raise ValueError(
17+
"OpenAI API key is required. Please set it in Django settings or as an environment variable."
18+
)
19+
self._client = OpenAI(api_key=API_KEY)
20+
21+
def edit_text(self, text: str, model: str) -> str:
22+
# Placeholder for OpenAI API integration
23+
response = self._client.responses.create(
24+
model=model,
25+
input=[
26+
{
27+
"role": "developer",
28+
"content": "You are a helpful assistant that edits text for grammar and clarity. Preserve the existing HTML structure and tags when present. Do not add wrapper containers like <ul>, <ol>, or <p> unless they already exist in the input. Return ONLY the edited text.",
29+
},
30+
{"role": "user", "content": text},
31+
],
32+
store=False,
33+
)
34+
return response.output_text
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from pydantic import BaseModel, Field
2+
from .language_models import LanguageModel
3+
4+
5+
class EditTextRequest(BaseModel):
6+
input: str = Field(
7+
min_length=40,
8+
max_length=500,
9+
description="The input to be edited by the AI model.",
10+
)
11+
model: LanguageModel = Field(default=LanguageModel.GPT_5_NANO)

django_email_learning/ai/urls.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from django.urls import path
2+
from .views import EditTextView
3+
4+
app_name = "django_email_learning"
5+
6+
urlpatterns = [
7+
path(
8+
"organizations/<int:organization_id>/edit-text/",
9+
EditTextView.as_view(),
10+
name="edit_text",
11+
),
12+
]

django_email_learning/ai/views.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from django.utils.decorators import method_decorator
2+
from django_email_learning.decorators import accessible_for
3+
from django.http import JsonResponse
4+
from django.views import View
5+
from pydantic import ValidationError
6+
from .serializers import EditTextRequest
7+
import json
8+
9+
10+
@method_decorator(accessible_for(roles={"admin", "editor"}), name="post")
11+
class EditTextView(View):
12+
def post(self, request, *args, **kwargs) -> JsonResponse: # type: ignore[no-untyped-def]
13+
payload = json.loads(request.body)
14+
try:
15+
serializer = EditTextRequest.model_validate(payload)
16+
input = serializer.input
17+
model = serializer.model
18+
ai_adapter = model.adapter_class()
19+
edited_text = ai_adapter.edit_text(input, model.model_name)
20+
return JsonResponse({"edited_text": edited_text})
21+
except ValidationError as e:
22+
return JsonResponse({"error": str(e)}, status=400)

django_email_learning/platform/views.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818

1919
DJANGO_EMAIL_LEARNING_SETTINGS: dict = getattr(settings, "DJANGO_EMAIL_LEARNING", {})
20+
AI_CONFIGURATIONS: dict = DJANGO_EMAIL_LEARNING_SETTINGS.get("AI", {})
2021

2122

2223
@method_decorator(login_required, name="dispatch")
@@ -66,6 +67,7 @@ def get_shared_context(self) -> Dict[str, Any]:
6667
and getattr(self.request.user, "has_platform_admin_role", False)
6768
)
6869
),
70+
"aiTextEditingModel": AI_CONFIGURATIONS.get("TEXT_EDITING_MODEL"),
6971
"customLogo": {
7072
"horizontalLight": DJANGO_EMAIL_LEARNING_SETTINGS.get("LOGO", {})
7173
.get("HORIZONTAL_LOCKUP", {})
@@ -261,6 +263,8 @@ def get_locale_messages(self) -> Dict[str, str]:
261263
"update_lesson": _("Update Lesson"),
262264
"new_quiz": _("New Quiz"),
263265
"update_quiz": _("Update Quiz"),
266+
"editing": _("Editing..."),
267+
"edit_with_ai": _("Edit with AI"),
264268
"lesson_title": _("Lesson Title"),
265269
"lesson_waiting_tooltip": _(
266270
"Set the amount of time that we should wait after the previous lesson or quiz submission before sending this lesson"

django_email_learning/urls.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@
3333
),
3434
),
3535
]
36+
37+
if apps.is_installed("django_email_learning.ai"):
38+
urlpatterns += [
39+
path(
40+
"api/ai/",
41+
include("django_email_learning.ai.urls", namespace="api_ai"),
42+
),
43+
]

0 commit comments

Comments
 (0)