Spaces:
Sleeping
Sleeping
add new files for docker and fast api
Browse files- .vscode/settings.json +16 -0
- Dockerfile +38 -0
- LICENSE +21 -0
- app.py +9 -21
- docker-compose.yml +29 -0
- main.py +42 -34
- run.py +9 -0
- static/css/style.css +64 -0
- templates/error.html +11 -0
- templates/home.html +11 -0
- tox.ini +5 -0
.vscode/settings.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"python.linting.pylint" : false,
|
3 |
+
"python.linting.flake8" : true,
|
4 |
+
"python.linting.enabled" : true,
|
5 |
+
"python.formatting.black" : true,
|
6 |
+
"editor.formatOnSave": true,
|
7 |
+
"python.linting.flake8Args":[
|
8 |
+
"--max-line-length=88"
|
9 |
+
],
|
10 |
+
"python.testing.unittestEnabled": false,
|
11 |
+
"python.testing.pytestEnabled": true,
|
12 |
+
"python.testing.autoTestDiscoverOnSaveEnabled": true,
|
13 |
+
"python.testing.pytestArgs": [
|
14 |
+
"tests"
|
15 |
+
],
|
16 |
+
}
|
Dockerfile
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.10-slim as builder
|
2 |
+
|
3 |
+
# set environment variables
|
4 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
5 |
+
PYTHONFAULTHANDLER=1 \
|
6 |
+
PYTHONUNBUFFERED=1
|
7 |
+
ENV POETRY_NO_INTERACTION=1 \
|
8 |
+
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
9 |
+
POETRY_VIRTUALENVS_CREATE=1 \
|
10 |
+
POETRY_CACHE_DIR=/tmp/poetry_cache
|
11 |
+
|
12 |
+
RUN mkdir -p /app
|
13 |
+
WORKDIR /app
|
14 |
+
RUN pip install poetry
|
15 |
+
COPY poetry.lock pyproject.toml ./
|
16 |
+
RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --no-root
|
17 |
+
|
18 |
+
|
19 |
+
FROM python:3.10-slim as base
|
20 |
+
|
21 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
22 |
+
PYTHONFAULTHANDLER=1 \
|
23 |
+
PYTHONUNBUFFERED=1
|
24 |
+
|
25 |
+
RUN apt-get update && apt-get install -y curl
|
26 |
+
WORKDIR /app
|
27 |
+
ENV VIRTUAL_ENV=/app/.venv \
|
28 |
+
PATH="/app/.venv/bin:$PATH"
|
29 |
+
|
30 |
+
COPY --from=builder /app/.venv /app/.venv
|
31 |
+
|
32 |
+
# Necessary Files
|
33 |
+
COPY main.py app.py /app/
|
34 |
+
|
35 |
+
# Expose port
|
36 |
+
EXPOSE 8000
|
37 |
+
|
38 |
+
CMD ["uvicorn","main:app","--proxy-headers","--host","0.0.0.0","--port","8000","--workers","3"]
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2023 Alexander Frantsev
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
app.py
CHANGED
@@ -75,10 +75,10 @@ class Summarizer():
|
|
75 |
self.ru_sentiment_pipe = pipeline("sentiment-analysis", model=RU_SENTIMENT_MODEL)
|
76 |
self.en_summary_pipe = sum_pipe
|
77 |
self.en_sentiment_pipe = pipeline("sentiment-analysis", model=EN_SENTIMENT_MODEL)
|
78 |
-
|
79 |
-
|
80 |
-
def mT5_summarize(self, text: str) -> str:
|
81 |
|
|
|
|
|
|
|
82 |
WHITESPACE_HANDLER = lambda k: re.sub('\s+', ' ', re.sub('\n+', ' ', k.strip()))
|
83 |
|
84 |
input_ids = self.sum_tokenizer(
|
@@ -111,23 +111,8 @@ class Summarizer():
|
|
111 |
sentiment = {'en': self.en_sentiment_pipe,
|
112 |
'ru': self.ru_sentiment_pipe,}
|
113 |
return summary[lang], sentiment[lang]
|
114 |
-
|
115 |
-
# def summarize(self, text: str, lang: str = 'en') -> str:
|
116 |
-
# result = {}
|
117 |
-
# sum_pipe, sent_pipe = self.get_pipe(lang)
|
118 |
-
|
119 |
-
# response_summary = sum_pipe(text)
|
120 |
-
# logger.info(response_summary)
|
121 |
-
# result["summary"] = response_summary[0]["summary_text"]
|
122 |
-
|
123 |
-
# response_sentiment = sent_pipe(text)
|
124 |
-
# logger.info(response_sentiment)
|
125 |
-
# result["sentiment_label"] = response_sentiment[0]["label"]
|
126 |
-
# result["sentiment_score"] = response_sentiment[0]["score"]
|
127 |
-
|
128 |
-
# return f"Summary: {result['summary']}\n Sentiment: {result['sentiment_label']} ({result['sentiment_score']:.3f})"
|
129 |
|
130 |
-
def summarize(self, text: Request, lang: str = 'en') ->
|
131 |
sum_pipe, sent_pipe = self.get_pipe(lang)
|
132 |
response_summary = sum_pipe(text)
|
133 |
logger.info(response_summary)
|
@@ -139,6 +124,9 @@ class Summarizer():
|
|
139 |
sentiment_score=response_sentiment[0]["score"],
|
140 |
)
|
141 |
return result
|
|
|
|
|
|
|
142 |
|
143 |
if __name__ == "__main__":
|
144 |
pipe = Summarizer()
|
@@ -161,12 +149,12 @@ if __name__ == "__main__":
|
|
161 |
ru_inbtn = gr.Button("Запустить")
|
162 |
|
163 |
en_inbtn.click(
|
164 |
-
pipe.
|
165 |
[en_inputs, en_lang],
|
166 |
[en_outputs],
|
167 |
)
|
168 |
ru_inbtn.click(
|
169 |
-
pipe.
|
170 |
[ru_inputs, ru_lang],
|
171 |
[ru_outputs],
|
172 |
)
|
|
|
75 |
self.ru_sentiment_pipe = pipeline("sentiment-analysis", model=RU_SENTIMENT_MODEL)
|
76 |
self.en_summary_pipe = sum_pipe
|
77 |
self.en_sentiment_pipe = pipeline("sentiment-analysis", model=EN_SENTIMENT_MODEL)
|
|
|
|
|
|
|
78 |
|
79 |
+
def mT5_summarize(self, text: str) -> str:
|
80 |
+
'''Handle text with mT5 model without pipeline'''
|
81 |
+
|
82 |
WHITESPACE_HANDLER = lambda k: re.sub('\s+', ' ', re.sub('\n+', ' ', k.strip()))
|
83 |
|
84 |
input_ids = self.sum_tokenizer(
|
|
|
111 |
sentiment = {'en': self.en_sentiment_pipe,
|
112 |
'ru': self.ru_sentiment_pipe,}
|
113 |
return summary[lang], sentiment[lang]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
+
def summarize(self, text: Request, lang: str = 'en') -> Result:
|
116 |
sum_pipe, sent_pipe = self.get_pipe(lang)
|
117 |
response_summary = sum_pipe(text)
|
118 |
logger.info(response_summary)
|
|
|
124 |
sentiment_score=response_sentiment[0]["score"],
|
125 |
)
|
126 |
return result
|
127 |
+
|
128 |
+
def summ(self, text: Request, lang: str = 'en') -> str:
|
129 |
+
return self.summarize(text, lang).to_str()
|
130 |
|
131 |
if __name__ == "__main__":
|
132 |
pipe = Summarizer()
|
|
|
149 |
ru_inbtn = gr.Button("Запустить")
|
150 |
|
151 |
en_inbtn.click(
|
152 |
+
pipe.summ,
|
153 |
[en_inputs, en_lang],
|
154 |
[en_outputs],
|
155 |
)
|
156 |
ru_inbtn.click(
|
157 |
+
pipe.summ,
|
158 |
[ru_inputs, ru_lang],
|
159 |
[ru_outputs],
|
160 |
)
|
docker-compose.yml
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3.8'
|
2 |
+
services:
|
3 |
+
app:
|
4 |
+
container_name: summary-app
|
5 |
+
restart: unless-stopped
|
6 |
+
# env_file:
|
7 |
+
# - .env
|
8 |
+
# environment:
|
9 |
+
# PGHOST: 'db'
|
10 |
+
# PGDATABASE: ${DATABASE_NAME}
|
11 |
+
# PGUSER: ${DATABASE_USER}
|
12 |
+
# PGPASSWORD: ${DATABASE_PASS}
|
13 |
+
build:
|
14 |
+
context: .
|
15 |
+
dockerfile: Dockerfile
|
16 |
+
ports:
|
17 |
+
- "8000:8000"
|
18 |
+
expose:
|
19 |
+
- 8000
|
20 |
+
healthcheck:
|
21 |
+
test: curl --fail -s http://localhost:8000/ || exit 1
|
22 |
+
interval: 10s
|
23 |
+
timeout: 5s
|
24 |
+
retries: 3
|
25 |
+
start_period: 10s
|
26 |
+
command: ["uvicorn","main:app","--proxy-headers","--host","0.0.0.0","--port","8000","--workers","3"]
|
27 |
+
|
28 |
+
|
29 |
+
|
main.py
CHANGED
@@ -8,40 +8,48 @@ from app import DEFAULT_EN_TEXT, DEFAULT_RU_TEXT
|
|
8 |
app = FastAPI()
|
9 |
pipe = Summarizer()
|
10 |
|
11 |
-
|
12 |
@app.post("/summ_ru", response_model=Result)
|
13 |
async def ru_summ_api(request: Request):
|
14 |
-
results = pipe.summarize(request.text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
return results
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
|
|
|
|
|
8 |
app = FastAPI()
|
9 |
pipe = Summarizer()
|
10 |
|
|
|
11 |
@app.post("/summ_ru", response_model=Result)
|
12 |
async def ru_summ_api(request: Request):
|
13 |
+
results = pipe.summarize(request.text, lang='ru')
|
14 |
+
return results
|
15 |
+
|
16 |
+
|
17 |
+
|
18 |
+
@app.post("/summ_en", response_model=Result)
|
19 |
+
async def ru_summ_api(request: Request):
|
20 |
+
results = pipe.summarize(request.text, lang='en')
|
21 |
return results
|
22 |
+
|
23 |
+
|
24 |
+
with gr.Blocks() as demo:
|
25 |
+
with gr.Row():
|
26 |
+
with gr.Column(scale=2, min_width=600):
|
27 |
+
en_sum_description=gr.Markdown(value=f"Model for Summary: {EN_SUMMARY_MODEL}")
|
28 |
+
en_sent_description=gr.Markdown(value=f"Model for Sentiment: {EN_SENTIMENT_MODEL}")
|
29 |
+
en_inputs=gr.Textbox(label="en_input", lines=5, value=DEFAULT_EN_TEXT, placeholder=DEFAULT_EN_TEXT)
|
30 |
+
en_lang=gr.Textbox(value='en',visible=False)
|
31 |
+
en_outputs=gr.Textbox(label="en_output", lines=5, placeholder="Summary and Sentiment would be here...")
|
32 |
+
en_inbtn = gr.Button("Proceed")
|
33 |
+
with gr.Column(scale=2, min_width=600):
|
34 |
+
ru_sum_description=gr.Markdown(value=f"Model for Summary: {RU_SUMMARY_MODEL}")
|
35 |
+
ru_sent_description=gr.Markdown(value=f"Model for Sentiment: {RU_SENTIMENT_MODEL}")
|
36 |
+
ru_inputs=gr.Textbox(label="ru_input", lines=5, value=DEFAULT_RU_TEXT, placeholder=DEFAULT_RU_TEXT)
|
37 |
+
ru_lang=gr.Textbox(value='ru',visible=False)
|
38 |
+
ru_outputs=gr.Textbox(label="ru_output", lines=5, placeholder="Здесь будет обобщение и эмоциональный окрас текста...")
|
39 |
+
ru_inbtn = gr.Button("Запустить")
|
40 |
+
|
41 |
+
en_inbtn.click(
|
42 |
+
pipe.summ,
|
43 |
+
[en_inputs, en_lang],
|
44 |
+
[en_outputs],
|
45 |
+
)
|
46 |
+
ru_inbtn.click(
|
47 |
+
pipe.summ,
|
48 |
+
[ru_inputs, ru_lang],
|
49 |
+
[ru_outputs],
|
50 |
+
)
|
51 |
+
|
52 |
+
# demo.launch(show_api=False)
|
53 |
+
|
54 |
+
# mounting at the root path
|
55 |
+
app = gr.mount_gradio_app(app, demo, path="/")
|
run.py
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import uvicorn
|
2 |
+
|
3 |
+
if __name__ == "__main__":
|
4 |
+
uvicorn.run(
|
5 |
+
app="main:app",
|
6 |
+
host="localhost",
|
7 |
+
port=8000,
|
8 |
+
reload=True
|
9 |
+
)
|
static/css/style.css
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
display: flex;
|
3 |
+
justify-content: center;
|
4 |
+
align-items: center;
|
5 |
+
height: 100vh;
|
6 |
+
margin: 0;
|
7 |
+
background-color: #f0f0f0;
|
8 |
+
font-family: Arial, sans-serif;
|
9 |
+
}
|
10 |
+
|
11 |
+
.container {
|
12 |
+
height: 50vh;
|
13 |
+
width: 25vw;
|
14 |
+
display: flex;
|
15 |
+
padding-block: 2rem;
|
16 |
+
justify-content: space-between;
|
17 |
+
flex-direction: column;
|
18 |
+
align-items: center;
|
19 |
+
border: 1px solid #47616c;
|
20 |
+
border-radius: 12px;
|
21 |
+
}
|
22 |
+
|
23 |
+
.header {
|
24 |
+
display: flex;
|
25 |
+
flex-direction: column;
|
26 |
+
align-items: center;
|
27 |
+
}
|
28 |
+
|
29 |
+
.img-box {
|
30 |
+
width: 70px;
|
31 |
+
height: 70px;
|
32 |
+
border-radius: 50%;
|
33 |
+
overflow: hidden;
|
34 |
+
}
|
35 |
+
|
36 |
+
.img {
|
37 |
+
width: 100%;
|
38 |
+
}
|
39 |
+
|
40 |
+
.google-btn {
|
41 |
+
display: flex;
|
42 |
+
align-items: center;
|
43 |
+
background-color: #47616c;
|
44 |
+
color: white;
|
45 |
+
width: 200px;
|
46 |
+
height: 50px;
|
47 |
+
border-radius: 5px;
|
48 |
+
box-shadow: 0px 3px 10px -2px rgba(0, 0, 0, 0.15);
|
49 |
+
overflow: hidden;
|
50 |
+
padding-inline: 8px;
|
51 |
+
text-decoration: none;
|
52 |
+
}
|
53 |
+
|
54 |
+
.google-icon {
|
55 |
+
background: url("https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg") transparent 5px 50% no-repeat;
|
56 |
+
display: inline-block;
|
57 |
+
vertical-align: middle;
|
58 |
+
width: 35px;
|
59 |
+
height: 50px;
|
60 |
+
}
|
61 |
+
|
62 |
+
.btn-text {
|
63 |
+
margin-left: 10px;
|
64 |
+
}
|
templates/error.html
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
+
<title>Error</title>
|
7 |
+
</head>
|
8 |
+
<body>
|
9 |
+
<h2>{{error}}</h2>
|
10 |
+
</body>
|
11 |
+
</html>
|
templates/home.html
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8"/>
|
5 |
+
<link rel="stylesheet" href="{{ url_for('static',path='/css/style.css')}}"/>
|
6 |
+
<title>Home Page</title>
|
7 |
+
</head>
|
8 |
+
<body>
|
9 |
+
<h1>Welcome!</h1>
|
10 |
+
</body>
|
11 |
+
</html>
|
tox.ini
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[flake8]
|
2 |
+
exclude = .git, __pycache__, env, .venv
|
3 |
+
max-line-length = 88
|
4 |
+
max-complexity = 18
|
5 |
+
extend-ignore = E203, E266, E501, W503
|