-
Notifications
You must be signed in to change notification settings - Fork 1
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 #7 from openradx/chat
Add report chat
- Loading branch information
Showing
13 changed files
with
315 additions
and
137 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
Large diffs are not rendered by default.
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 |
---|---|---|
|
@@ -6,7 +6,7 @@ authors = ["medihack <[email protected]>"] | |
license = "GPL-3.0-or-later" | ||
|
||
[tool.poetry.dependencies] | ||
adit-radis-shared = { git = "https://github.com/openradx/adit-radis-shared.git", tag = "v0.3.5" } | ||
adit-radis-shared = { git = "https://github.com/openradx/adit-radis-shared.git", tag = "v0.3.8" } | ||
adrf = "^0.1.4" | ||
aiofiles = "^23.1.0" | ||
asyncinotify = "^4.0.1" | ||
|
@@ -43,7 +43,7 @@ redis = "^5.0.3" | |
toml = "^0.10.2" | ||
Twisted = { extras = ["tls", "http2"], version = "^24.3.0" } | ||
wait-for-it = "^2.2.2" | ||
watchfiles = "^0.21.0" | ||
watchfiles = "^0.22.0" | ||
whitenoise = "^6.0.0" | ||
|
||
[tool.poetry.group.dev.dependencies] | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import logging | ||
from string import Template | ||
from typing import Literal | ||
|
||
from django.conf import settings | ||
from openai import OpenAI | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
YES_NO_GRAMMAR = """ | ||
root ::= Answer | ||
Answer ::= "$yes" | "$no" | ||
""" | ||
|
||
|
||
class ChatClient: | ||
def __init__(self): | ||
self._client = OpenAI(base_url=f"{settings.LLAMACPP_URL}/v1", api_key="none") | ||
|
||
def ask_question( | ||
self, report_body: str, language: str, question: str, grammar: str | None = None | ||
) -> str: | ||
system_prompt = settings.CHAT_SYSTEM_PROMPT[language] | ||
user_prompt = Template(settings.CHAT_USER_PROMPT[language]).substitute( | ||
{"report": report_body, "question": question} | ||
) | ||
|
||
log_msg = f"Sending to LLM:\n[System] {system_prompt}\n[User] {user_prompt}" | ||
if grammar: | ||
log_msg += f"\n[Grammar] {grammar}" | ||
logger.debug(log_msg) | ||
|
||
completion = self._client.chat.completions.create( | ||
model="none", | ||
messages=[ | ||
{"role": "system", "content": system_prompt}, | ||
{"role": "user", "content": user_prompt}, | ||
], | ||
extra_body={"grammar": grammar}, | ||
) | ||
|
||
answer = completion.choices[0].message.content | ||
assert answer is not None | ||
logger.debug("Received from LLM: %s", answer) | ||
|
||
return answer | ||
|
||
def ask_yes_no_question( | ||
self, report_body: str, language: str, question: str | ||
) -> Literal["yes", "no"]: | ||
grammar = Template(YES_NO_GRAMMAR).substitute( | ||
{ | ||
"yes": settings.CHAT_ANSWER_YES[language], | ||
"no": settings.CHAT_ANSWER_NO[language], | ||
} | ||
) | ||
|
||
llm_answer = self.ask_question(report_body, language, question, grammar) | ||
|
||
if llm_answer == settings.CHAT_ANSWER_YES[language]: | ||
return "yes" | ||
elif llm_answer == settings.CHAT_ANSWER_NO[language]: | ||
return "no" | ||
else: | ||
raise ValueError(f"Unexpected answer: {llm_answer}") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from crispy_forms.helper import FormHelper, Layout | ||
from crispy_forms.layout import Field, Submit | ||
from django import forms | ||
|
||
|
||
class PromptForm(forms.Form): | ||
prompt = forms.CharField(max_length=500) | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
self.helper = FormHelper() | ||
self.helper.form_show_labels = False | ||
self.helper.form_tag = False | ||
self.helper.layout = Layout( | ||
Field("prompt", placeholder="Ask the LLM a question about this report"), | ||
Submit( | ||
"yes_no_answer", | ||
"Yes/No answer", | ||
css_class="btn-primary", | ||
), | ||
Submit( | ||
"full_answer", | ||
"Full answer", | ||
css_class="btn-primary", | ||
), | ||
) |
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,37 @@ | ||
{% load crispy from crispy_forms_tags %} | ||
<div id="report_chat" class="mt-3 mx-2"> | ||
<div id="messages-container"> | ||
{% if messages %} | ||
<table class="table table-borderless"> | ||
<tbody> | ||
{% for message in messages %} | ||
<tr> | ||
<th scope="row">{{ message.role }}</th> | ||
<td class="w-100">{{ message.content }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
{% endif %} | ||
</div> | ||
<div id="loader" class="my-indicator htmx-indicator"> | ||
<div class="mb-3 d-flex justify-content-center gap-1"> | ||
<div class="spinner-grow spinner-grow-sm" role="status"> | ||
<span class="visually-hidden">Loading...</span> | ||
</div> | ||
<div class="spinner-grow spinner-grow-sm" role="status"> | ||
<span class="visually-hidden">Loading...</span> | ||
</div> | ||
<div class="spinner-grow spinner-grow-sm" role="status"> | ||
<span class="visually-hidden">Loading...</span> | ||
</div> | ||
</div> | ||
</div> | ||
<form hx-post="{% url 'report_chat' report.id %}" | ||
hx-on:submit="htmx.addClass(htmx.find('#messages-container'), 'd-none')" | ||
hx-target="#report_chat" | ||
hx-indicator="#loader" | ||
hx-disabled-elt="input"> | ||
{% crispy prompt_form %} | ||
</form> | ||
</div> |
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
Oops, something went wrong.