-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3238 from digitalfabrik/develop
Release `2024.12.0`
- Loading branch information
Showing
88 changed files
with
2,637 additions
and
796 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ | |
import json | ||
import logging | ||
import random | ||
import socket | ||
from typing import TYPE_CHECKING | ||
|
||
from django.http import HttpResponse, JsonResponse | ||
|
@@ -17,8 +16,8 @@ | |
|
||
from ....cms.models import ABTester, AttachmentMap, Language, Region, UserChat | ||
from ...decorators import json_response | ||
from .chat_bot import ChatBot | ||
from .zammad_api import ZammadChatAPI | ||
from .utils.chat_bot import process_answer, process_user_message | ||
from .utils.zammad_api import ZammadChatAPI | ||
|
||
if TYPE_CHECKING: | ||
from django.http import HttpRequest | ||
|
@@ -227,15 +226,13 @@ def zammad_webhook(request: HttpRequest) -> JsonResponse: | |
""" | ||
Receive webhooks from Zammad to update the latest article translation | ||
""" | ||
zammad_url = ( | ||
f"https://{socket.getnameinfo((request.META.get('REMOTE_ADDR'), 0), 0)[0]}" | ||
region = get_object_or_404( | ||
Region, zammad_webhook_token=request.GET.get("token", None) | ||
) | ||
region = get_object_or_404(Region, zammad_url=zammad_url) | ||
client = ZammadChatAPI(region) | ||
if not region.integreat_chat_enabled: | ||
return JsonResponse({"status": "Integreat Chat disabled"}) | ||
webhook_message = json.loads(request.body) | ||
message_text = webhook_message["article"]["body"] | ||
zammad_chat = UserChat.objects.get(zammad_id=webhook_message["ticket"]["id"]) | ||
chat_bot = ChatBot() | ||
|
||
actions = [] | ||
if webhook_message["article"]["internal"]: | ||
|
@@ -249,34 +246,14 @@ def zammad_webhook(request: HttpRequest) -> JsonResponse: | |
webhook_message["article"]["created_by"]["login"] | ||
== "[email protected]" | ||
): | ||
actions.append("question translation") | ||
client.send_message( | ||
zammad_chat.zammad_id, | ||
chat_bot.automatic_translation( | ||
message_text, zammad_chat.language.slug, region.default_language.slug | ||
), | ||
True, | ||
True, | ||
actions.append("question translation queued") | ||
process_user_message.apply_async( | ||
args=[message_text, region.slug, webhook_message["ticket"]["id"]] | ||
) | ||
if answer := chat_bot.automatic_answer( | ||
message_text, region, zammad_chat.language.slug | ||
): | ||
actions.append("automatic answer") | ||
client.send_message( | ||
zammad_chat.zammad_id, | ||
answer, | ||
False, | ||
True, | ||
) | ||
else: | ||
actions.append("answer translation") | ||
client.send_message( | ||
zammad_chat.zammad_id, | ||
chat_bot.automatic_translation( | ||
message_text, region.default_language.slug, zammad_chat.language.slug | ||
), | ||
False, | ||
True, | ||
actions.append("answer translation queued") | ||
process_answer.apply_async( | ||
args=[message_text, region.slug, webhook_message["ticket"]["id"]] | ||
) | ||
return JsonResponse( | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
""" | ||
Utils for the Integreat Chat | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
""" | ||
Wrapper for the Chat Bot / LLM API | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
import requests | ||
from celery import shared_task | ||
from django.conf import settings | ||
|
||
from integreat_cms.cms.models import Region, UserChat | ||
from integreat_cms.cms.utils.content_translation_utils import ( | ||
get_public_translation_for_link, | ||
) | ||
|
||
from .zammad_api import ZammadChatAPI | ||
|
||
|
||
def format_message(response: dict) -> str: | ||
""" | ||
Transform JSON into readable message | ||
""" | ||
if "answer" not in response or not response["answer"]: | ||
raise ValueError("Could not format message, no answer attribute in response") | ||
if "sources" not in response or not response["sources"]: | ||
return response["answer"] | ||
sources = "".join( | ||
[ | ||
f"<li><a href='{settings.WEBAPP_URL}{path}'>{title}</a></li>" | ||
for path in response["sources"] | ||
if (title := get_public_translation_for_link(settings.WEBAPP_URL + path)) | ||
] | ||
) | ||
return f"{response['answer']}\n<ul>{sources}</ul>" | ||
|
||
|
||
def automatic_answer(message: str, region: Region, language_slug: str) -> str | None: | ||
""" | ||
Get automatic answer to question | ||
""" | ||
url = ( | ||
f"https://{settings.INTEGREAT_CHAT_BACK_END_DOMAIN}/chatanswers/extract_answer/" | ||
) | ||
body = {"message": message, "language": language_slug, "region": region.slug} | ||
r = requests.post(url, json=body, timeout=120) | ||
return format_message(r.json()) | ||
|
||
|
||
def automatic_translation( | ||
message: str, source_language_slug: str, target_language_slug: str | ||
) -> str: | ||
""" | ||
Use LLM to translate message | ||
""" | ||
url = f"https://{settings.INTEGREAT_CHAT_BACK_END_DOMAIN}/chatanswers/translate_message/" | ||
body = { | ||
"message": message, | ||
"source_language": source_language_slug, | ||
"target_language": target_language_slug, | ||
} | ||
response = requests.post(url, json=body, timeout=120).json() | ||
if "status" in response and response["status"] == "success": | ||
return response["translation"] | ||
raise ValueError("Did not receive success response for translation request.") | ||
|
||
|
||
@shared_task | ||
def process_user_message( | ||
message_text: str, region_slug: str, zammad_ticket_id: int | ||
) -> None: | ||
""" | ||
Process the message from an Integreat App user | ||
""" | ||
zammad_chat = UserChat.objects.get(zammad_id=zammad_ticket_id) | ||
region = Region.objects.get(slug=region_slug) | ||
client = ZammadChatAPI(region) | ||
if translation := automatic_translation( | ||
message_text, zammad_chat.language.slug, region.default_language.slug | ||
): | ||
client.send_message( | ||
zammad_chat.zammad_id, | ||
translation, | ||
True, | ||
True, | ||
) | ||
if answer := automatic_answer(message_text, region, zammad_chat.language.slug): | ||
client.send_message( | ||
zammad_chat.zammad_id, | ||
answer, | ||
False, | ||
True, | ||
) | ||
|
||
|
||
@shared_task | ||
def process_answer(message_text: str, region_slug: str, zammad_ticket_id: int) -> None: | ||
""" | ||
Process automatic or counselor answers | ||
""" | ||
zammad_chat = UserChat.objects.get(zammad_id=zammad_ticket_id) | ||
region = Region.objects.get(slug=region_slug) | ||
client = ZammadChatAPI(region) | ||
if translation := automatic_translation( | ||
message_text, region.default_language.slug, zammad_chat.language.slug | ||
): | ||
client.send_message( | ||
zammad_chat.zammad_id, | ||
translation, | ||
False, | ||
True, | ||
) |
Oops, something went wrong.