Skip to content

Commit

Permalink
chatbot endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
TamiTakamiya committed Oct 16, 2024
1 parent 52de54e commit c36ace0
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 1 deletion.
31 changes: 31 additions & 0 deletions ansible_ai_connect/main/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright Red Hat
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rest_framework.permissions import IsAuthenticated


class IsAuthenticatedRHEmployee(IsAuthenticated):
"""
Allow access only to users who are authenticated Red Hat employees.
"""

code = "permission_denied__user_not_authenticated_rh_employee"
message = "The User is not an authenticated Red Hat employee."

def has_permission(self, request, view):
permitted = super().has_permission(request, view)
if not permitted:
return permitted

user = request.user
return user.rh_employee
17 changes: 17 additions & 0 deletions ansible_ai_connect/main/templates/chatbot/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Ansible AI Connect Chatbot</title>
</head>
<body>
<p>THIS IS A PLACEHOLDER</p>
</body>
{% block content %}
<noscript>You need to enable JavaScript to run this app.</noscript>
<!-- The chatbot needs to show the userName -->
<!-- It is safe to pass the value embedded in the host DOM -->
<!-- as it is only used for display purposes and servers no other value -->
<div id="user_name" hidden>{{user_name}}</div>
{% endblock content %}
</html>
86 changes: 86 additions & 0 deletions ansible_ai_connect/main/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,89 @@ def test_get_view_expired_trial(self):

user.delete()
trial_plan.delete()


@override_settings(CHATBOT_URL="http://127.0.0.1:8080")
@override_settings(CHATBOT_DEFAULT_PROVIDER="wisdom")
@override_settings(CHATBOT_DEFAULT_MODEL="granite-8b")
class TestChatbotView(TestCase):
CHATBOT_PAGE_TITLE = "<title>Ansible AI Connect Chatbot</title>"
DOCUMENT_URL = (
'href="https://access.redhat.com/documentation/en-us/'
'red_hat_ansible_lightspeed_with_ibm_watsonx_code_assistant/2.x_latest"'
)

def setUp(self):
super().setUp()
self.non_rh_user = get_user_model().objects.create_user(
username="non-rh-user",
password="non-rh-password",
email="non-rh-user@email.com",
rh_employee=False,
)
self.rh_user = get_user_model().objects.create_user(
username="rh-user",
password="rh-password",
email="rh-user@redhat.com",
rh_employee=True,
)

def tearDown(self):
self.non_rh_user.delete()
self.rh_user.delete()

def test_chatbot_link_with_anonymous_user(self):
r = self.client.get(reverse("home"))
self.assertEqual(r.status_code, HTTPStatus.OK)
self.assertContains(r, TestChatbotView.DOCUMENT_URL)
self.assertNotContains(r, "Chatbot")

def test_chatbot_link_with_non_rh_user(self):
self.client.force_login(user=self.non_rh_user)
r = self.client.get(reverse("home"))
self.assertEqual(r.status_code, HTTPStatus.OK)
self.assertContains(r, TestChatbotView.DOCUMENT_URL)
self.assertNotContains(r, "Chatbot")

@override_settings(CHATBOT_URL="")
@override_settings(CHATBOT_DEFAULT_PROVIDER="")
@override_settings(CHATBOT_DEFAULT_MODEL="")
def test_chatbot_link_with_rh_user_but_chatbot_disabled(self):
self.client.force_login(user=self.rh_user)
r = self.client.get(reverse("home"))
self.assertEqual(r.status_code, HTTPStatus.OK)
self.assertContains(r, TestChatbotView.DOCUMENT_URL)
self.assertNotContains(r, "Chatbot")

def test_chatbot_link_with_rh_user(self):
self.client.force_login(user=self.rh_user)
r = self.client.get(reverse("home"))
self.assertEqual(r.status_code, HTTPStatus.OK)
self.assertContains(r, TestChatbotView.DOCUMENT_URL)
self.assertContains(r, "Chatbot")

def test_chatbot_view_with_anonymous_user(self):
r = self.client.get(reverse("chatbot"))
self.assertEqual(r.status_code, HTTPStatus.FOUND)
self.assertEqual(r.url, "/login")

def test_chatbot_view_with_non_rh_user(self):
self.client.force_login(user=self.non_rh_user)
r = self.client.get(reverse("chatbot"))
self.assertEqual(r.status_code, HTTPStatus.FORBIDDEN)

@override_settings(CHATBOT_URL="")
@override_settings(CHATBOT_DEFAULT_PROVIDER="")
@override_settings(CHATBOT_DEFAULT_MODEL="")
def test_chatbot_view_with_rh_user_but_chatbot_disabled(self):
self.client.force_login(user=self.rh_user)
r = self.client.get(reverse("chatbot"))
self.assertEqual(r.status_code, HTTPStatus.FOUND)
self.assertEqual(r.url, "/")

def test_chatbot_view_with_rh_user(self):
self.client.force_login(user=self.rh_user)
r = self.client.get(reverse("chatbot"))
self.assertEqual(r.status_code, HTTPStatus.OK)
self.assertContains(r, TestChatbotView.CHATBOT_PAGE_TITLE)
self.assertContains(r, self.rh_user.username)
2 changes: 2 additions & 0 deletions ansible_ai_connect/main/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
WisdomServiceLivenessProbeView,
)
from ansible_ai_connect.main.views import (
ChatbotView,
ConsoleView,
LoginView,
LogoutView,
Expand Down Expand Up @@ -100,6 +101,7 @@
TelemetrySettingsView.as_view(),
name="telemetry_settings",
),
path("chatbot/", ChatbotView.as_view(), name="chatbot"),
]

if settings.DEBUG:
Expand Down
30 changes: 30 additions & 0 deletions ansible_ai_connect/main/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
IsOrganisationLightspeedSubscriber,
)
from ansible_ai_connect.main.base_views import ProtectedTemplateView
from ansible_ai_connect.main.permissions import IsAuthenticatedRHEmployee
from ansible_ai_connect.main.settings.base import SOCIAL_AUTH_OIDC_KEY

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -110,6 +111,35 @@ def get_context_data(self, **kwargs):
return context


class ChatbotView(ProtectedTemplateView):
template_name = "chatbot/index.html"

permission_classes = [
IsAuthenticatedRHEmployee,
]

def get(self, request):
# Open the chatbot page when the chatbot service is configured.
if (
settings.CHATBOT_URL
and settings.CHATBOT_DEFAULT_MODEL
and settings.CHATBOT_DEFAULT_PROVIDER
):
return super().get(request)

# Otherwise, redirect to the home page.
return HttpResponseRedirect("/")

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["project_name"] = settings.ANSIBLE_AI_PROJECT_NAME
user = self.request.user
if user:
context["user_name"] = user.username

return context


class PlainTextRenderer(BaseRenderer):
media_type = "text/plain"
format = "txt"
Expand Down
3 changes: 3 additions & 0 deletions ansible_ai_connect/users/templates/users/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ <h1 class="pf-c-title pf-m-lg">{{ project_name }}</h1>
{% if deployment_mode == 'saas' and user.is_authenticated and user.rh_user_is_org_admin %}
<a class="pf-l-level__item" href="/console"><span class="fas fa-solid fa-cog"></span> Admin Portal</a>
{% endif %}
{% if chatbot_enabled and deployment_mode == 'saas' and user.is_authenticated and user.rh_employee %}
<a class="pf-l-level__item" href="/chatbot"><span class="fas fa-solid fa-comments"></span> Chatbot</a>
{% endif %}
</div>
</div>
</div>
Expand Down
11 changes: 10 additions & 1 deletion ansible_ai_connect/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from ansible_ai_connect.ai.api.utils.segment import send_schema1_event
from ansible_ai_connect.main.cache.cache_per_user import cache_per_user
from ansible_ai_connect.users.constants import TRIAL_PLAN_NAME
from ansible_ai_connect.users.models import Plan
from ansible_ai_connect.users.models import Plan, User
from ansible_ai_connect.users.one_click_trial import OneClickTrial

from .serializers import MarkdownUserResponseSerializer, UserResponseSerializer
Expand Down Expand Up @@ -86,6 +86,15 @@ def get_context_data(self, **kwargs):

context["documentation_url"] = settings.COMMERCIAL_DOCUMENTATION_URL

user = self.request.user
context["rh_employee"] = user.rh_employee if isinstance(user, User) else False

context["chatbot_enabled"] = (
settings.CHATBOT_URL
and settings.CHATBOT_DEFAULT_MODEL
and settings.CHATBOT_DEFAULT_PROVIDER
)

return context


Expand Down

0 comments on commit c36ace0

Please sign in to comment.