Spaces:
Runtime error
Runtime error
Fix formatting issues (#56)
Browse files* Rename formatter
* override response without sources
* Add gradio formatter
* add factory for responses
* Fix circular imports
- buster/apps/gradio_app.ipynb +8 -6
- buster/apps/slackbot.py +5 -8
- buster/chatbot.py +21 -17
- buster/formatter/__init__.py +16 -5
- buster/formatter/base.py +16 -8
- buster/formatter/factory.py +22 -0
- buster/formatter/gradio.py +28 -0
- buster/formatter/html.py +4 -4
- buster/formatter/markdown.py +3 -12
- buster/formatter/slack.py +3 -12
- pyproject.toml +1 -1
buster/apps/gradio_app.ipynb
CHANGED
@@ -9,6 +9,9 @@
|
|
9 |
},
|
10 |
"outputs": [],
|
11 |
"source": [
|
|
|
|
|
|
|
12 |
"import gradio as gr\n",
|
13 |
"\n",
|
14 |
"from buster.chatbot import Chatbot, ChatbotConfig\n",
|
@@ -24,9 +27,8 @@
|
|
24 |
" \"engine\": \"text-davinci-003\",\n",
|
25 |
" \"max_tokens\": 500,\n",
|
26 |
" },\n",
|
27 |
-
"
|
28 |
-
"
|
29 |
-
" text_after_response=\"I'm a bot π€ trained to answer huggingface π€ transformers questions. My answers aren't always perfect.\",\n",
|
30 |
" text_before_prompt=\"\"\"You are a slack chatbot assistant answering technical questions about huggingface transformers, a library to train transformers in python.\n",
|
31 |
"Make sure to format your answers in Markdown format, including code block and snippets.\n",
|
32 |
"Do not include any links to urls or hyperlinks in your answers.\n",
|
@@ -109,7 +111,7 @@
|
|
109 |
],
|
110 |
"metadata": {
|
111 |
"kernelspec": {
|
112 |
-
"display_name": "
|
113 |
"language": "python",
|
114 |
"name": "python3"
|
115 |
},
|
@@ -123,11 +125,11 @@
|
|
123 |
"name": "python",
|
124 |
"nbconvert_exporter": "python",
|
125 |
"pygments_lexer": "ipython3",
|
126 |
-
"version": "3.9
|
127 |
},
|
128 |
"vscode": {
|
129 |
"interpreter": {
|
130 |
-
"hash": "
|
131 |
}
|
132 |
}
|
133 |
},
|
|
|
9 |
},
|
10 |
"outputs": [],
|
11 |
"source": [
|
12 |
+
"%load_ext autoreload\n",
|
13 |
+
"%autoreload 2\n",
|
14 |
+
"\n",
|
15 |
"import gradio as gr\n",
|
16 |
"\n",
|
17 |
"from buster.chatbot import Chatbot, ChatbotConfig\n",
|
|
|
27 |
" \"engine\": \"text-davinci-003\",\n",
|
28 |
" \"max_tokens\": 500,\n",
|
29 |
" },\n",
|
30 |
+
" link_format=\"gradio\",\n",
|
31 |
+
" response_footnote=\"I'm a bot π€ trained to answer huggingface π€ transformers questions. My answers aren't always perfect.\",\n",
|
|
|
32 |
" text_before_prompt=\"\"\"You are a slack chatbot assistant answering technical questions about huggingface transformers, a library to train transformers in python.\n",
|
33 |
"Make sure to format your answers in Markdown format, including code block and snippets.\n",
|
34 |
"Do not include any links to urls or hyperlinks in your answers.\n",
|
|
|
111 |
],
|
112 |
"metadata": {
|
113 |
"kernelspec": {
|
114 |
+
"display_name": "buster",
|
115 |
"language": "python",
|
116 |
"name": "python3"
|
117 |
},
|
|
|
125 |
"name": "python",
|
126 |
"nbconvert_exporter": "python",
|
127 |
"pygments_lexer": "ipython3",
|
128 |
+
"version": "3.10.9"
|
129 |
},
|
130 |
"vscode": {
|
131 |
"interpreter": {
|
132 |
+
"hash": "bfa91706490f6a3314a87f4853806d905e46027cd889e58fcad4739e8600f624"
|
133 |
}
|
134 |
}
|
135 |
},
|
buster/apps/slackbot.py
CHANGED
@@ -26,8 +26,8 @@ mila_doc_cfg = ChatbotConfig(
|
|
26 |
"max_tokens": 200,
|
27 |
},
|
28 |
separator="\n",
|
29 |
-
|
30 |
-
|
31 |
For more info, view the full documentation here (https://docs.mila.quebec/) or contact [email protected]
|
32 |
""",
|
33 |
text_before_prompt="""
|
@@ -62,8 +62,7 @@ orion_cfg = ChatbotConfig(
|
|
62 |
"max_tokens": 200,
|
63 |
},
|
64 |
separator="\n",
|
65 |
-
|
66 |
-
text_after_response="I'm a bot π€ and not always perfect.",
|
67 |
text_before_prompt="""You are a slack chatbot assistant answering technical questions about orion, a hyperparameter optimization library written in python.
|
68 |
Make sure to format your answers in Markdown format, including code block and snippets.
|
69 |
Do not include any links to urls or hyperlinks in your answers.
|
@@ -95,8 +94,7 @@ pytorch_cfg = ChatbotConfig(
|
|
95 |
"max_tokens": 500,
|
96 |
},
|
97 |
separator="\n",
|
98 |
-
|
99 |
-
text_after_response="I'm a bot π€ and not always perfect.",
|
100 |
text_before_prompt="""You are a slack chatbot assistant answering technical questions about pytorch, a library to train neural networks written in python.
|
101 |
Make sure to format your answers in Markdown format, including code block and snippets.
|
102 |
Do not include any links to urls or hyperlinks in your answers.
|
@@ -128,8 +126,7 @@ hf_transformers_cfg = ChatbotConfig(
|
|
128 |
"max_tokens": 500,
|
129 |
},
|
130 |
separator="\n",
|
131 |
-
|
132 |
-
text_after_response="I'm a bot π€ and not always perfect.",
|
133 |
text_before_prompt="""You are a slack chatbot assistant answering technical questions about huggingface transformers, a library to train transformers in python.
|
134 |
Make sure to format your answers in Markdown format, including code block and snippets.
|
135 |
Do not include any links to urls or hyperlinks in your answers.
|
|
|
26 |
"max_tokens": 200,
|
27 |
},
|
28 |
separator="\n",
|
29 |
+
response_format="slack",
|
30 |
+
response_footnote="""I'm a bot π€ and not always perfect.
|
31 |
For more info, view the full documentation here (https://docs.mila.quebec/) or contact [email protected]
|
32 |
""",
|
33 |
text_before_prompt="""
|
|
|
62 |
"max_tokens": 200,
|
63 |
},
|
64 |
separator="\n",
|
65 |
+
response_format="slack",
|
|
|
66 |
text_before_prompt="""You are a slack chatbot assistant answering technical questions about orion, a hyperparameter optimization library written in python.
|
67 |
Make sure to format your answers in Markdown format, including code block and snippets.
|
68 |
Do not include any links to urls or hyperlinks in your answers.
|
|
|
94 |
"max_tokens": 500,
|
95 |
},
|
96 |
separator="\n",
|
97 |
+
response_format="slack",
|
|
|
98 |
text_before_prompt="""You are a slack chatbot assistant answering technical questions about pytorch, a library to train neural networks written in python.
|
99 |
Make sure to format your answers in Markdown format, including code block and snippets.
|
100 |
Do not include any links to urls or hyperlinks in your answers.
|
|
|
126 |
"max_tokens": 500,
|
127 |
},
|
128 |
separator="\n",
|
129 |
+
response_format="slack",
|
|
|
130 |
text_before_prompt="""You are a slack chatbot assistant answering technical questions about huggingface transformers, a library to train transformers in python.
|
131 |
Make sure to format your answers in Markdown format, including code block and snippets.
|
132 |
Do not include any links to urls or hyperlinks in your answers.
|
buster/chatbot.py
CHANGED
@@ -10,11 +10,12 @@ import promptlayer
|
|
10 |
from openai.embeddings_utils import cosine_similarity, get_embedding
|
11 |
|
12 |
from buster.documents import get_documents_manager_from_extension
|
13 |
-
from buster.formatter import
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
18 |
|
19 |
logger = logging.getLogger(__name__)
|
20 |
logging.basicConfig(level=logging.INFO)
|
@@ -41,10 +42,10 @@ class ChatbotConfig:
|
|
41 |
max_words: maximum number of words the retrieved documents can be. Will truncate otherwise.
|
42 |
completion_kwargs: kwargs for the OpenAI.Completion() method
|
43 |
separator: the separator to use, can be either "\n" or <p> depending on rendering.
|
44 |
-
|
45 |
unknown_prompt: Prompt to use to generate the "I don't know" embedding to compare to.
|
46 |
text_before_prompt: Text to prompt GPT with before the user prompt, but after the documentation.
|
47 |
-
|
48 |
"""
|
49 |
|
50 |
documents_file: str = "buster/data/document_embeddings.tar.gz"
|
@@ -65,11 +66,11 @@ class ChatbotConfig:
|
|
65 |
}
|
66 |
)
|
67 |
separator: str = "\n"
|
68 |
-
|
69 |
unknown_prompt: str = "I Don't know how to answer your question."
|
70 |
text_before_documents: str = "You are a chatbot answering questions.\n"
|
71 |
text_before_prompt: str = "Answer the following question:\n"
|
72 |
-
|
73 |
|
74 |
|
75 |
class Chatbot:
|
@@ -78,6 +79,12 @@ class Chatbot:
|
|
78 |
self.cfg = cfg
|
79 |
self._init_documents()
|
80 |
self._init_unk_embedding()
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
|
82 |
def _init_documents(self):
|
83 |
filepath = self.cfg.documents_file
|
@@ -183,10 +190,12 @@ class Chatbot:
|
|
183 |
)
|
184 |
if relevant:
|
185 |
sources = (
|
186 |
-
Source(dct["
|
187 |
for dct in matched_documents.to_dict(orient="records")
|
188 |
)
|
189 |
else:
|
|
|
|
|
190 |
sources = tuple()
|
191 |
|
192 |
return response, sources
|
@@ -211,16 +220,11 @@ class Chatbot:
|
|
211 |
# Likely that the answer is meaningful, add the top sources
|
212 |
return score < unk_threshold
|
213 |
|
214 |
-
def process_input(self, question: str, formatter:
|
215 |
"""
|
216 |
Main function to process the input question and generate a formatted output.
|
217 |
"""
|
218 |
|
219 |
-
if formatter is None and self.cfg.link_format not in FORMATTERS:
|
220 |
-
raise ValueError(f"Unknown link format {self.cfg.link_format}")
|
221 |
-
elif formatter is None:
|
222 |
-
formatter = FORMATTERS[self.cfg.link_format]()
|
223 |
-
|
224 |
logger.info(f"User Question:\n{question}")
|
225 |
|
226 |
# We make sure there is always a newline at the end of the question to avoid completing the question.
|
@@ -241,4 +245,4 @@ class Chatbot:
|
|
241 |
)
|
242 |
response, sources = self.generate_response(prompt, matched_documents, self.cfg.unknown_prompt)
|
243 |
|
244 |
-
return
|
|
|
10 |
from openai.embeddings_utils import cosine_similarity, get_embedding
|
11 |
|
12 |
from buster.documents import get_documents_manager_from_extension
|
13 |
+
from buster.formatter import (
|
14 |
+
Response,
|
15 |
+
ResponseFormatter,
|
16 |
+
Source,
|
17 |
+
response_formatter_factory,
|
18 |
+
)
|
19 |
|
20 |
logger = logging.getLogger(__name__)
|
21 |
logging.basicConfig(level=logging.INFO)
|
|
|
42 |
max_words: maximum number of words the retrieved documents can be. Will truncate otherwise.
|
43 |
completion_kwargs: kwargs for the OpenAI.Completion() method
|
44 |
separator: the separator to use, can be either "\n" or <p> depending on rendering.
|
45 |
+
response_format: the type of format to render links with, e.g. slack or markdown
|
46 |
unknown_prompt: Prompt to use to generate the "I don't know" embedding to compare to.
|
47 |
text_before_prompt: Text to prompt GPT with before the user prompt, but after the documentation.
|
48 |
+
reponse_footnote: Generic response to add the the chatbot's reply.
|
49 |
"""
|
50 |
|
51 |
documents_file: str = "buster/data/document_embeddings.tar.gz"
|
|
|
66 |
}
|
67 |
)
|
68 |
separator: str = "\n"
|
69 |
+
response_format: str = "slack"
|
70 |
unknown_prompt: str = "I Don't know how to answer your question."
|
71 |
text_before_documents: str = "You are a chatbot answering questions.\n"
|
72 |
text_before_prompt: str = "Answer the following question:\n"
|
73 |
+
response_footnote: str = "I'm a bot π€ and not always perfect."
|
74 |
|
75 |
|
76 |
class Chatbot:
|
|
|
79 |
self.cfg = cfg
|
80 |
self._init_documents()
|
81 |
self._init_unk_embedding()
|
82 |
+
self._init_response_formatter()
|
83 |
+
|
84 |
+
def _init_response_formatter(self):
|
85 |
+
self.response_formatter = response_formatter_factory(
|
86 |
+
format=self.cfg.response_format, response_footnote=self.cfg.response_footnote
|
87 |
+
)
|
88 |
|
89 |
def _init_documents(self):
|
90 |
filepath = self.cfg.documents_file
|
|
|
190 |
)
|
191 |
if relevant:
|
192 |
sources = (
|
193 |
+
Source(dct["source"], dct["url"], dct["similarity"])
|
194 |
for dct in matched_documents.to_dict(orient="records")
|
195 |
)
|
196 |
else:
|
197 |
+
# Override the answer with a generic unknown prompt, without sources.
|
198 |
+
response = Response(text=self.cfg.unknown_prompt)
|
199 |
sources = tuple()
|
200 |
|
201 |
return response, sources
|
|
|
220 |
# Likely that the answer is meaningful, add the top sources
|
221 |
return score < unk_threshold
|
222 |
|
223 |
+
def process_input(self, question: str, formatter: ResponseFormatter = None) -> str:
|
224 |
"""
|
225 |
Main function to process the input question and generate a formatted output.
|
226 |
"""
|
227 |
|
|
|
|
|
|
|
|
|
|
|
228 |
logger.info(f"User Question:\n{question}")
|
229 |
|
230 |
# We make sure there is always a newline at the end of the question to avoid completing the question.
|
|
|
245 |
)
|
246 |
response, sources = self.generate_response(prompt, matched_documents, self.cfg.unknown_prompt)
|
247 |
|
248 |
+
return self.response_formatter(response, sources)
|
buster/formatter/__init__.py
CHANGED
@@ -1,6 +1,17 @@
|
|
1 |
-
from .base import
|
2 |
-
from .
|
3 |
-
from .
|
4 |
-
from .
|
|
|
|
|
5 |
|
6 |
-
__all__ = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .base import Response, ResponseFormatter, Source
|
2 |
+
from .factory import response_formatter_factory
|
3 |
+
from .gradio import GradioResponseFormatter
|
4 |
+
from .html import HTMLResponseFormatter
|
5 |
+
from .markdown import MarkdownResponseFormatter
|
6 |
+
from .slack import SlackResponseFormatter
|
7 |
|
8 |
+
__all__ = [
|
9 |
+
Source,
|
10 |
+
Response,
|
11 |
+
ResponseFormatter,
|
12 |
+
HTMLResponseFormatter,
|
13 |
+
MarkdownResponseFormatter,
|
14 |
+
SlackResponseFormatter,
|
15 |
+
GradioResponseFormatter,
|
16 |
+
response_formatter_factory,
|
17 |
+
]
|
buster/formatter/base.py
CHANGED
@@ -4,7 +4,7 @@ from typing import Iterable, NamedTuple
|
|
4 |
|
5 |
# Should be from the `documents` module.
|
6 |
class Source(NamedTuple):
|
7 |
-
|
8 |
url: str
|
9 |
question_similarity: float
|
10 |
# TODO Add answer similarity.
|
@@ -20,12 +20,18 @@ class Response:
|
|
20 |
|
21 |
|
22 |
@dataclass
|
23 |
-
class
|
|
|
24 |
source_template: str = "{source.name} (relevance: {source.question_similarity:2.3f})"
|
25 |
-
error_msg_template: str = "Something went wrong
|
26 |
error_fallback_template: str = "Something went very wrong."
|
27 |
-
sourced_answer_template: str =
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
def source_item(self, source: Source) -> str:
|
31 |
"""Format a single source item."""
|
@@ -48,10 +54,12 @@ class Formatter:
|
|
48 |
def answer(self, response: Response, sources: Iterable[Source]) -> str:
|
49 |
"""Format an answer and its sources."""
|
50 |
sources_list = self.sources_list(sources)
|
51 |
-
if
|
52 |
-
return self.sourced_answer_template.format(
|
|
|
|
|
53 |
|
54 |
-
return self.unsourced_answer_template.format(response=response)
|
55 |
|
56 |
def __call__(self, response: Response, sources: Iterable[Source]) -> str:
|
57 |
"""Format an answer and its sources, or an error message."""
|
|
|
4 |
|
5 |
# Should be from the `documents` module.
|
6 |
class Source(NamedTuple):
|
7 |
+
source: str
|
8 |
url: str
|
9 |
question_similarity: float
|
10 |
# TODO Add answer similarity.
|
|
|
20 |
|
21 |
|
22 |
@dataclass
|
23 |
+
class ResponseFormatter:
|
24 |
+
response_footnote: str
|
25 |
source_template: str = "{source.name} (relevance: {source.question_similarity:2.3f})"
|
26 |
+
error_msg_template: str = """Something went wrong:\n{response.error_msg}"""
|
27 |
error_fallback_template: str = "Something went very wrong."
|
28 |
+
sourced_answer_template: str = (
|
29 |
+
"""{response.text}\n\n"""
|
30 |
+
"""π Here are the sources I used to answer your question:\n"""
|
31 |
+
"""{sources}\n\n"""
|
32 |
+
"""{footnote}"""
|
33 |
+
)
|
34 |
+
unsourced_answer_template: str = "{response.text}\n\n{footnote}"
|
35 |
|
36 |
def source_item(self, source: Source) -> str:
|
37 |
"""Format a single source item."""
|
|
|
54 |
def answer(self, response: Response, sources: Iterable[Source]) -> str:
|
55 |
"""Format an answer and its sources."""
|
56 |
sources_list = self.sources_list(sources)
|
57 |
+
if sources_list:
|
58 |
+
return self.sourced_answer_template.format(
|
59 |
+
response=response, sources=sources_list, footnote=self.response_footnote
|
60 |
+
)
|
61 |
|
62 |
+
return self.unsourced_answer_template.format(response=response, footnote=self.response_footnote)
|
63 |
|
64 |
def __call__(self, response: Response, sources: Iterable[Source]) -> str:
|
65 |
"""Format an answer and its sources, or an error message."""
|
buster/formatter/factory.py
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
|
3 |
+
import buster.formatter as F
|
4 |
+
|
5 |
+
logger = logging.getLogger(__name__)
|
6 |
+
logging.basicConfig(level=logging.INFO)
|
7 |
+
|
8 |
+
|
9 |
+
def response_formatter_factory(format: str, **kwargs):
|
10 |
+
logger.info(f"Using formatter: {format}")
|
11 |
+
if format == "text":
|
12 |
+
return F.ResponseFormatter(**kwargs)
|
13 |
+
elif format == "slack":
|
14 |
+
return F.SlackResponseFormatter(**kwargs)
|
15 |
+
elif format == "HTML":
|
16 |
+
return F.HTMLResponseFormatter(**kwargs)
|
17 |
+
elif format == "gradio":
|
18 |
+
return F.GradioResponseFormatter(**kwargs)
|
19 |
+
elif format == "markdown":
|
20 |
+
return F.MarkdownResponseFormatter(**kwargs)
|
21 |
+
else:
|
22 |
+
raise ValueError(f"Undefined {format=}")
|
buster/formatter/gradio.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dataclasses import dataclass
|
2 |
+
from typing import Iterable
|
3 |
+
|
4 |
+
from buster.formatter import ResponseFormatter, Source
|
5 |
+
|
6 |
+
|
7 |
+
@dataclass
|
8 |
+
class GradioResponseFormatter(ResponseFormatter):
|
9 |
+
"""Format the answer for gradio chat interface."""
|
10 |
+
|
11 |
+
error_msg_template: str = """Something went wrong:<br>{response.error_msg}"""
|
12 |
+
error_fallback_template: str = "Something went very wrong."
|
13 |
+
sourced_answer_template: str = (
|
14 |
+
"""{response.text}<br><br>"""
|
15 |
+
"""π Here are the sources I used to answer your question:<br>"""
|
16 |
+
"""{sources}<br><br>"""
|
17 |
+
"""{footnote}"""
|
18 |
+
)
|
19 |
+
unsourced_answer_template: str = "{response.text}<br><br>{footnote}"
|
20 |
+
source_template: str = """[π {source.source}]({source.url}), relevance: {source.question_similarity:2.3f}"""
|
21 |
+
|
22 |
+
def sources_list(self, sources: Iterable[Source]) -> str | None:
|
23 |
+
"""Format sources into a list."""
|
24 |
+
items = [self.source_item(source) for source in sources]
|
25 |
+
if not items:
|
26 |
+
return None # No list needed.
|
27 |
+
|
28 |
+
return "<br>".join(items)
|
buster/formatter/html.py
CHANGED
@@ -2,14 +2,14 @@ import html
|
|
2 |
from dataclasses import dataclass
|
3 |
from typing import Iterable
|
4 |
|
5 |
-
from buster.formatter.base import
|
6 |
|
7 |
|
8 |
@dataclass
|
9 |
-
class
|
10 |
"""Format the answer in HTML."""
|
11 |
|
12 |
-
source_template: str = """<li><a href='{source.url}'>π {source.
|
13 |
error_msg_template: str = """<div class="error">Something went wrong:\n<p>{response.error_msg}</p></div>"""
|
14 |
error_fallback_template: str = """<div class="error">Something went very wrong.</div>"""
|
15 |
sourced_answer_template: str = (
|
@@ -37,5 +37,5 @@ class HTMLFormatter(Formatter):
|
|
37 |
response.error,
|
38 |
html.escape(response.error_msg) if response.error_msg else response.error_msg,
|
39 |
)
|
40 |
-
sources = (Source(html.escape(source.
|
41 |
return super().__call__(response, sources)
|
|
|
2 |
from dataclasses import dataclass
|
3 |
from typing import Iterable
|
4 |
|
5 |
+
from buster.formatter.base import Response, ResponseFormatter, Source
|
6 |
|
7 |
|
8 |
@dataclass
|
9 |
+
class HTMLResponseFormatter(ResponseFormatter):
|
10 |
"""Format the answer in HTML."""
|
11 |
|
12 |
+
source_template: str = """<li><a href='{source.url}'>π {source.source}</a></li>"""
|
13 |
error_msg_template: str = """<div class="error">Something went wrong:\n<p>{response.error_msg}</p></div>"""
|
14 |
error_fallback_template: str = """<div class="error">Something went very wrong.</div>"""
|
15 |
sourced_answer_template: str = (
|
|
|
37 |
response.error,
|
38 |
html.escape(response.error_msg) if response.error_msg else response.error_msg,
|
39 |
)
|
40 |
+
sources = (Source(html.escape(source.source), source.url, source.question_similarity) for source in sources)
|
41 |
return super().__call__(response, sources)
|
buster/formatter/markdown.py
CHANGED
@@ -1,23 +1,14 @@
|
|
1 |
from dataclasses import dataclass
|
2 |
from typing import Iterable
|
3 |
|
4 |
-
from buster.formatter.base import
|
5 |
|
6 |
|
7 |
@dataclass
|
8 |
-
class
|
9 |
"""Format the answer in markdown."""
|
10 |
|
11 |
-
source_template: str = """[π {source.
|
12 |
-
error_msg_template: str = """Something went wrong:\n{response.error_msg}"""
|
13 |
-
error_fallback_template: str = """Something went very wrong."""
|
14 |
-
sourced_answer_template: str = (
|
15 |
-
"""{response.text}\n\n"""
|
16 |
-
"""π Here are the sources I used to answer your question:\n"""
|
17 |
-
"""{sources}\n\n"""
|
18 |
-
"""I'm a chatbot, bleep bloop."""
|
19 |
-
)
|
20 |
-
unsourced_answer_template: str = """{response.text}\n\nI'm a chatbot, bleep bloop."""
|
21 |
|
22 |
def sources_list(self, sources: Iterable[Source]) -> str | None:
|
23 |
"""Format sources into a list."""
|
|
|
1 |
from dataclasses import dataclass
|
2 |
from typing import Iterable
|
3 |
|
4 |
+
from buster.formatter.base import ResponseFormatter, Source
|
5 |
|
6 |
|
7 |
@dataclass
|
8 |
+
class MarkdownResponseFormatter(ResponseFormatter):
|
9 |
"""Format the answer in markdown."""
|
10 |
|
11 |
+
source_template: str = """[π {source.source}]({source.url}), relevance: {source.question_similarity:2.3f}"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
def sources_list(self, sources: Iterable[Source]) -> str | None:
|
14 |
"""Format sources into a list."""
|
buster/formatter/slack.py
CHANGED
@@ -1,23 +1,14 @@
|
|
1 |
from dataclasses import dataclass
|
2 |
from typing import Iterable
|
3 |
|
4 |
-
from buster.formatter
|
5 |
|
6 |
|
7 |
@dataclass
|
8 |
-
class
|
9 |
"""Format the answer for Slack."""
|
10 |
|
11 |
-
source_template: str = """<{source.url}|π {source.
|
12 |
-
error_msg_template: str = """Something went wrong:\n{response.error_msg}"""
|
13 |
-
error_fallback_template: str = """Something went very wrong."""
|
14 |
-
sourced_answer_template: str = (
|
15 |
-
"""{response.text}\n\n"""
|
16 |
-
"""π Here are the sources I used to answer your question:\n"""
|
17 |
-
"""{sources}\n\n"""
|
18 |
-
"""I'm a chatbot, bleep bloop."""
|
19 |
-
)
|
20 |
-
unsourced_answer_template: str = """{response.text}\n\nI'm a chatbot, bleep bloop."""
|
21 |
|
22 |
def sources_list(self, sources: Iterable[Source]) -> str | None:
|
23 |
"""Format sources into a list."""
|
|
|
1 |
from dataclasses import dataclass
|
2 |
from typing import Iterable
|
3 |
|
4 |
+
from buster.formatter import ResponseFormatter, Source
|
5 |
|
6 |
|
7 |
@dataclass
|
8 |
+
class SlackResponseFormatter(ResponseFormatter):
|
9 |
"""Format the answer for Slack."""
|
10 |
|
11 |
+
source_template: str = """<{source.url}|π {source.source}>, relevance: {source.question_similarity:2.3f}"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
def sources_list(self, sources: Iterable[Source]) -> str | None:
|
14 |
"""Format sources into a list."""
|
pyproject.toml
CHANGED
@@ -7,7 +7,7 @@ name = "buster"
|
|
7 |
version = "0.0.1"
|
8 |
description = "buster the bot for the mila cluster"
|
9 |
readme = "README.md"
|
10 |
-
requires-python = ">=3.
|
11 |
dynamic = ["dependencies"]
|
12 |
|
13 |
[tool.setuptools.dynamic]
|
|
|
7 |
version = "0.0.1"
|
8 |
description = "buster the bot for the mila cluster"
|
9 |
readme = "README.md"
|
10 |
+
requires-python = ">=3.10"
|
11 |
dynamic = ["dependencies"]
|
12 |
|
13 |
[tool.setuptools.dynamic]
|