basic inference pipeline
Browse files- README.md +1 -0
- introduck/inference.py +43 -0
- introduck/routes.py +119 -99
- introduck/utils.py +16 -0
- requirements.txt +3 -0
README.md
CHANGED
@@ -8,6 +8,7 @@ sdk_version: 3.3.1
|
|
8 |
python_version: 3.10.4
|
9 |
app_file: main_hf_space.py
|
10 |
pinned: false
|
|
|
11 |
---
|
12 |
|
13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
8 |
python_version: 3.10.4
|
9 |
app_file: main_hf_space.py
|
10 |
pinned: false
|
11 |
+
license: mit
|
12 |
---
|
13 |
|
14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
introduck/inference.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import spacy
|
2 |
+
|
3 |
+
|
4 |
+
def extract_contacts_from_email(payload: str) -> list[list[str]]:
|
5 |
+
return [["John Doe", "Acme Corp.", "john@example.com", "ai, nn"]]
|
6 |
+
|
7 |
+
|
8 |
+
def generate_acceptance_reply(payload: str) -> str:
|
9 |
+
return "Good!"
|
10 |
+
|
11 |
+
|
12 |
+
def generate_rejection_reply(payload: str) -> str:
|
13 |
+
return "Not interested :("
|
14 |
+
|
15 |
+
|
16 |
+
def highlight_named_entities(payload: str, labels: list[str] = None) -> dict:
|
17 |
+
if labels is None:
|
18 |
+
labels = ["ORG", "PERSON"]
|
19 |
+
|
20 |
+
nlp: spacy.Language | None = None
|
21 |
+
if not hasattr(highlight_named_entities, "nlp"):
|
22 |
+
nlp = spacy.load(name="en_core_web_sm")
|
23 |
+
highlight_named_entities.nlp = nlp
|
24 |
+
print(f"Loaded {nlp.meta.get('name', 'unknown')} model from {nlp.path}")
|
25 |
+
else:
|
26 |
+
nlp = highlight_named_entities.nlp
|
27 |
+
print(f"Reused {nlp.meta.get('name', 'unknown')} model from {nlp.path}")
|
28 |
+
|
29 |
+
doc = nlp(payload)
|
30 |
+
|
31 |
+
entities: list = []
|
32 |
+
for ent in doc.ents:
|
33 |
+
if ent.label_ not in labels:
|
34 |
+
continue
|
35 |
+
|
36 |
+
entity: dict = {
|
37 |
+
"entity": ent.label_,
|
38 |
+
"start": ent.start_char,
|
39 |
+
"end": ent.end_char}
|
40 |
+
|
41 |
+
entities.append(entity)
|
42 |
+
|
43 |
+
return {"text": payload, "entities": entities}
|
introduck/routes.py
CHANGED
@@ -3,29 +3,14 @@ import gradio as gr
|
|
3 |
from fastapi import FastAPI
|
4 |
from gradio.routes import App as GradioApp
|
5 |
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
"""
|
15 |
-
|
16 |
-
contacts: list = [["John Doe", "Acme Corp.", "john@example.com", "ai, nn"]]
|
17 |
-
|
18 |
-
acceptance_reply: str = "Good!"
|
19 |
-
|
20 |
-
rejection_reply: str = "Not interested :("
|
21 |
-
|
22 |
-
return input_message, contacts, acceptance_reply, rejection_reply
|
23 |
-
|
24 |
-
|
25 |
-
def _use_message_template() -> (str, str):
|
26 |
-
subject: str = "Could you make an intro?"
|
27 |
-
|
28 |
-
message: str = """\
|
29 |
Hey,
|
30 |
|
31 |
hope you are doing well! :-)
|
@@ -45,71 +30,93 @@ Relevant info:
|
|
45 |
+ <highlights list>
|
46 |
|
47 |
Best,
|
48 |
-
<your name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
-
return
|
|
|
|
|
|
|
|
|
51 |
|
52 |
|
53 |
def create_base_route(title: str = "") -> FastAPI:
|
54 |
-
|
55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
# Introduck
|
57 |
-
##
|
58 |
-
No new tools. No complex setup. No hassle
|
59 |
""")
|
60 |
|
61 |
-
with gr.
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
lines=5,
|
84 |
-
max_lines=42,
|
85 |
-
placeholder="Type a message...",
|
86 |
-
interactive=True)
|
87 |
-
|
88 |
-
# TODO: Add support for attached files.
|
89 |
-
# email_attachments_input: gr.File = gr.File(
|
90 |
-
# label="Attachments:",
|
91 |
-
# file_count="multiple")
|
92 |
-
|
93 |
-
with gr.Row():
|
94 |
-
email_template_button: gr.Button = gr.Button(
|
95 |
-
value="Use template",
|
96 |
-
variant="secondary")
|
97 |
-
|
98 |
-
email_template_button.click(
|
99 |
-
fn=_use_message_template,
|
100 |
-
inputs=[],
|
101 |
-
outputs=[email_subject_input, email_message_input])
|
102 |
-
|
103 |
-
email_submit_button: gr.Button = gr.Button(
|
104 |
-
value="Submit",
|
105 |
-
variant="primary")
|
106 |
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
|
|
|
|
|
|
|
|
|
|
113 |
with gr.TabItem(label="Contacts", id="default"):
|
114 |
contacts_table_headers: list[str] = [
|
115 |
"Name",
|
@@ -122,28 +129,21 @@ def create_base_route(title: str = "") -> FastAPI:
|
|
122 |
max_cols=len(contacts_table_headers),
|
123 |
max_rows=16)
|
124 |
|
125 |
-
with gr.TabItem(label="
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
129 |
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
interactive=True)
|
134 |
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
email_subject_input,
|
141 |
-
email_message_input],
|
142 |
-
outputs=[
|
143 |
-
rfc822_output,
|
144 |
-
contacts_output,
|
145 |
-
acceptance_template_output,
|
146 |
-
rejection_template_output])
|
147 |
|
148 |
gr.Markdown("## FAQ")
|
149 |
|
@@ -162,6 +162,26 @@ def create_base_route(title: str = "") -> FastAPI:
|
|
162 |
|
163 |
gr.Markdown("**©** Introduck, 2022")
|
164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
base_route = GradioApp.create_app(blocks=base_blocks)
|
166 |
|
167 |
# TODO: overwrite /favicon.ico
|
|
|
3 |
from fastapi import FastAPI
|
4 |
from gradio.routes import App as GradioApp
|
5 |
|
6 |
+
from introduck.inference import extract_contacts_from_email
|
7 |
+
from introduck.inference import generate_acceptance_reply
|
8 |
+
from introduck.inference import generate_rejection_reply
|
9 |
+
from introduck.inference import highlight_named_entities
|
10 |
+
from introduck.utils import create_email_message
|
11 |
+
|
12 |
+
_INTRO_SUBJECT_EXAMPLE: str = "Could you make an intro?"
|
13 |
+
_INTRO_MESSAGE_EXAMPLE: str = """\
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
Hey,
|
15 |
|
16 |
hope you are doing well! :-)
|
|
|
30 |
+ <highlights list>
|
31 |
|
32 |
Best,
|
33 |
+
<your name>\
|
34 |
+
"""
|
35 |
+
|
36 |
+
|
37 |
+
def _analyze_message(sender: str, recipients: str, subject: str, body: str):
|
38 |
+
msg_data: dict = {
|
39 |
+
"from": sender,
|
40 |
+
"to": recipients,
|
41 |
+
"subject": subject,
|
42 |
+
"body": body}
|
43 |
+
|
44 |
+
msg: str = create_email_message(data=msg_data)
|
45 |
+
|
46 |
+
contacts: list = extract_contacts_from_email(payload=msg)
|
47 |
+
acceptance_reply: str = generate_acceptance_reply(payload=msg)
|
48 |
+
rejection_reply: str = generate_rejection_reply(payload=msg)
|
49 |
+
text_with_named_entities: dict = highlight_named_entities(payload=msg)
|
50 |
|
51 |
+
return contacts, acceptance_reply, rejection_reply, text_with_named_entities
|
52 |
+
|
53 |
+
|
54 |
+
def _use_message_template() -> (str, str):
|
55 |
+
return _INTRO_SUBJECT_EXAMPLE, _INTRO_MESSAGE_EXAMPLE
|
56 |
|
57 |
|
58 |
def create_base_route(title: str = "") -> FastAPI:
|
59 |
+
# TODO: Fix once resolved https://github.com/gradio-app/gradio/issues/1683
|
60 |
+
# TODO: ...and remove elem_id="htxt" from HighlightedText for RF822 output
|
61 |
+
css_workaround: str = "#htxt span {white-space: pre}"
|
62 |
+
|
63 |
+
base_blocks: gr.Blocks = gr.Blocks(
|
64 |
+
analytics_enabled=False,
|
65 |
+
title=title,
|
66 |
+
css=css_workaround)
|
67 |
+
|
68 |
+
with base_blocks:
|
69 |
+
gr.Markdown("""\
|
70 |
# Introduck
|
71 |
+
## Intro requests. Simplified. 🦆
|
72 |
+
No new tools. No complex setup. No hassle.\
|
73 |
""")
|
74 |
|
75 |
+
with gr.Box():
|
76 |
+
with gr.Column():
|
77 |
+
gr.Markdown("## New intro request")
|
78 |
+
|
79 |
+
email_sender_input: gr.Textbox = gr.Textbox(
|
80 |
+
label="From:",
|
81 |
+
lines=1,
|
82 |
+
max_lines=1,
|
83 |
+
placeholder="founder@acme.com")
|
84 |
+
|
85 |
+
email_recipients_input: gr.Textbox = gr.Textbox(
|
86 |
+
label="To:",
|
87 |
+
lines=1,
|
88 |
+
max_lines=1,
|
89 |
+
placeholder="vc@example.com")
|
90 |
+
|
91 |
+
email_subject_input: gr.Textbox = gr.Textbox(
|
92 |
+
show_label=False,
|
93 |
+
lines=1,
|
94 |
+
max_lines=1,
|
95 |
+
placeholder="Subject...",
|
96 |
+
interactive=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
+
email_body_input: gr.Textbox = gr.Textbox(
|
99 |
+
show_label=False,
|
100 |
+
lines=5,
|
101 |
+
max_lines=42,
|
102 |
+
placeholder="Type a message...",
|
103 |
+
interactive=True)
|
104 |
+
|
105 |
+
# TODO: Add support for attached files.
|
106 |
+
# email_attachments_input: gr.File = gr.File(
|
107 |
+
# label="Attachments:",
|
108 |
+
# file_count="multiple")
|
109 |
+
|
110 |
+
with gr.Row():
|
111 |
+
email_template_button: gr.Button = gr.Button(
|
112 |
+
value="Use template",
|
113 |
+
variant="secondary")
|
114 |
|
115 |
+
email_submit_button: gr.Button = gr.Button(
|
116 |
+
value="Submit",
|
117 |
+
variant="primary")
|
118 |
+
|
119 |
+
with gr.Tabs(selected="default"):
|
120 |
with gr.TabItem(label="Contacts", id="default"):
|
121 |
contacts_table_headers: list[str] = [
|
122 |
"Name",
|
|
|
129 |
max_cols=len(contacts_table_headers),
|
130 |
max_rows=16)
|
131 |
|
132 |
+
with gr.TabItem(label="Replies"):
|
133 |
+
with gr.Column():
|
134 |
+
acceptance_template_output: gr.Textbox = gr.Textbox(
|
135 |
+
label="Use this message to accept an intro request:",
|
136 |
+
interactive=True)
|
137 |
|
138 |
+
rejection_template_output: gr.Textbox = gr.Textbox(
|
139 |
+
label="Use this message to decline an intro request:",
|
140 |
+
interactive=True)
|
|
|
141 |
|
142 |
+
with gr.TabItem(label="RFC822"):
|
143 |
+
rfc822_output: gr.HighlightedText = gr.HighlightedText(
|
144 |
+
label="RFC822 message (for debugging purposes):",
|
145 |
+
show_legend=False,
|
146 |
+
elem_id="htxt") # temporary fix, see above for more info
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
gr.Markdown("## FAQ")
|
149 |
|
|
|
162 |
|
163 |
gr.Markdown("**©** Introduck, 2022")
|
164 |
|
165 |
+
# ui end, continue with signals and slots...
|
166 |
+
|
167 |
+
email_template_button.click(
|
168 |
+
fn=_use_message_template,
|
169 |
+
inputs=[],
|
170 |
+
outputs=[email_subject_input, email_body_input])
|
171 |
+
|
172 |
+
email_submit_button.click(
|
173 |
+
fn=_analyze_message,
|
174 |
+
inputs=[
|
175 |
+
email_sender_input,
|
176 |
+
email_recipients_input,
|
177 |
+
email_subject_input,
|
178 |
+
email_body_input],
|
179 |
+
outputs=[
|
180 |
+
contacts_output,
|
181 |
+
acceptance_template_output,
|
182 |
+
rejection_template_output,
|
183 |
+
rfc822_output])
|
184 |
+
|
185 |
base_route = GradioApp.create_app(blocks=base_blocks)
|
186 |
|
187 |
# TODO: overwrite /favicon.ico
|
introduck/utils.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from email.message import EmailMessage
|
2 |
+
|
3 |
+
|
4 |
+
def create_email_message(data: dict) -> str:
|
5 |
+
msg: EmailMessage = EmailMessage()
|
6 |
+
|
7 |
+
body_text: str = data.get("body", "")
|
8 |
+
# body_html: str = ""
|
9 |
+
|
10 |
+
msg["From"] = data.get("from", "")
|
11 |
+
msg["To"] = data.get("to", "")
|
12 |
+
msg["Subject"] = data.get("subject", "")
|
13 |
+
msg.set_content(body_text)
|
14 |
+
# msg.add_alternative(body_html, subtype="html")
|
15 |
+
|
16 |
+
return msg.as_string()
|
requirements.txt
CHANGED
@@ -1,3 +1,6 @@
|
|
1 |
fastapi[all]
|
2 |
gradio==3.3.1 # keep in sync with version from README.md metadata
|
|
|
3 |
uvicorn[standard]
|
|
|
|
|
|
1 |
fastapi[all]
|
2 |
gradio==3.3.1 # keep in sync with version from README.md metadata
|
3 |
+
spacy==3.4.0
|
4 |
uvicorn[standard]
|
5 |
+
|
6 |
+
https://huggingface.co/spacy/en_core_web_sm/resolve/main/en_core_web_sm-any-py3-none-any.whl
|