|
import language_tool_python |
|
import openai |
|
import streamlit as st |
|
import subprocess |
|
from pathlib import Path |
|
import html |
|
import docx |
|
from io import BytesIO |
|
|
|
is_java_installed = False |
|
prompt = """ |
|
Ты должен писать комментарии об ошибках в тексте. |
|
Тебе дан кусок текст, в котором есть ошибка, контекст, в котором стоит это слово и сообщение об ошибке. Твоя задача - кратко описать суть ошибки и, если необходимо, исправить её. |
|
Исправляй только ту ошибку, на которую указывает сообщение. Отвечай на русском языке. |
|
|
|
### Пример 1: |
|
Кусок текста: |
|
"кросивую" |
|
|
|
Сообщение об ошибке: |
|
"Возможно найдена орфографическая ошибка." |
|
|
|
Текст: |
|
"...т! Сегодня я был в парке и встретил там кросивую собаку. Она повиляла хвостом и побежа..." |
|
|
|
Твой ответ: |
|
Орфографическая ошибка в слове "кросивую" - правильно "красивую". |
|
|
|
### Пример 2: |
|
Кусок текста: |
|
" " |
|
|
|
Сообщение об ошибке: |
|
"Повтор пробела." |
|
|
|
Текст: |
|
"...ретил там кросивую собаку. Она повиляла хвостом и побежала к речке. Я решил что ..." |
|
|
|
Твой ответ: |
|
Обнаружен повтор пробела между словами. |
|
|
|
Теперь твоя очередь: |
|
Кусок текста: |
|
"{}" |
|
|
|
Сообщение об ошибке: |
|
"{}" |
|
|
|
Текст: |
|
"{}" |
|
""" |
|
|
|
|
|
def install_java(): |
|
global is_java_installed |
|
if is_java_installed: return |
|
try: |
|
|
|
subprocess.run(["apt-get", "update"], check=True) |
|
subprocess.run(["apt-get", "install", "-y", "openjdk-17-jdk"], check=True) |
|
|
|
is_java_installed = True |
|
except Exception as e: |
|
st.error(f"Ошибка установки Java: {e}") |
|
|
|
|
|
@st.cache_resource |
|
def load_assets(): |
|
openai.api_key = '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' |
|
install_java() |
|
tool = language_tool_python.LanguageTool('ru-RU', |
|
language_tool_download_version="6.1") |
|
return tool |
|
|
|
|
|
def generate_gpt_comment_openai(inp): |
|
response = openai.ChatCompletion.create( |
|
model="gpt-4o-mini", |
|
messages=[ |
|
{"role": "user", "content": inp} |
|
] |
|
) |
|
return response.choices[0].message['content'] |
|
|
|
|
|
def generate_gpt_comment_vsegpt(inp): |
|
client = openai.OpenAI( |
|
api_key='sk-or-vv-44ca318747a45e810149bf769e9dfbe5f42046695875efd7c9121cca590d6906', |
|
base_url='https://api.vsegpt.ru/v1' |
|
) |
|
response = client.chat.completions.create( |
|
model="gpt-4o-mini", |
|
messages=[{"role": "user", "content": inp}] |
|
).choices[0].message.content |
|
|
|
return response |
|
|
|
|
|
def generate_gpt_comment(inp, client_name): |
|
if client_name == "openai": |
|
return generate_gpt_comment_openai(inp) |
|
elif client_name == "vsegpt": |
|
return generate_gpt_comment_vsegpt(inp) |
|
else: |
|
raise ValueError(f"Unsupported client: {client_name}") |
|
|
|
|
|
def check_text(text, tool): |
|
matches = tool.check(text) |
|
errors = [] |
|
for match in matches: |
|
inp = prompt.format(text[match.offset:match.offset + match.errorLength], |
|
match.message, match.context) |
|
error_info = { |
|
'start': match.offset, |
|
'end': match.offset + match.errorLength, |
|
'message': generate_gpt_comment(inp, client_name="openai"), |
|
} |
|
errors.append(error_info) |
|
|
|
errors = sorted(errors, key=lambda x: x['end']) |
|
|
|
shift = 0 |
|
for i, error in enumerate(errors, 1): |
|
error['start'] += shift |
|
error['end'] += shift |
|
inp = f"({i})" |
|
text = text[:error['end']] + inp + text[error['end']:] |
|
shift += len(inp) |
|
|
|
return text.replace("\n", " "), errors |
|
|
|
|
|
def extract_text_from_docx(file): |
|
doc = docx.Document(BytesIO(file.getvalue())) |
|
full_text = [] |
|
for para in doc.paragraphs: |
|
full_text.append(para.text) |
|
return "\n".join(full_text) |
|
|
|
|
|
def main(): |
|
st.markdown(""" |
|
<style> |
|
/* Сохраняем белый текст для темной темы */ |
|
.stApp, .stTextInput, .stTextArea, .stMarkdown { |
|
color: white; |
|
} |
|
|
|
/* Специальный стиль для текста внутри выделенных ошибок */ |
|
.error-highlight { |
|
background-color: #ffe066; |
|
border-bottom: 2px dotted #ff9900; |
|
position: relative; |
|
color: black !important; /* Принудительно черный текст в выделениях */ |
|
} |
|
|
|
/* Стиль для контейнера с результатом */ |
|
.result-container { |
|
background: rgba(255, 255, 255, 0.1); |
|
padding: 20px; |
|
border-radius: 8px; |
|
border: 1px solid #444; |
|
white-space: pre-wrap; |
|
font-family: monospace; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
st.title('Проверка орфографии') |
|
|
|
|
|
input_mode = st.radio( |
|
"Выберите способ ввода текста:", |
|
("Ввести текст вручную", "Загрузить Word документ"), |
|
horizontal=True |
|
) |
|
|
|
text = "" |
|
if input_mode == "Загрузить Word документ": |
|
uploaded_file = st.file_uploader("Загрузите Word документ", type=['docx']) |
|
if uploaded_file is not None: |
|
try: |
|
text = extract_text_from_docx(uploaded_file) |
|
st.text_area("Текст из документа:", value=text, height=200, key="docx_text") |
|
except Exception as e: |
|
st.error(f"Ошибка при чтении файла: {e}") |
|
else: |
|
text = st.text_area("Введите текст для проверки:", height=200, key="manual_text") |
|
|
|
tool = load_assets() |
|
|
|
if st.button('Проверить текст'): |
|
if not text.strip(): |
|
st.warning("Введите текст для проверки") |
|
else: |
|
text, errors = check_text(text, tool) |
|
if not errors: |
|
st.success("Ошибок не найдено! 👍") |
|
else: |
|
sorted_errors = sorted(errors, key=lambda x: x['start']) |
|
|
|
highlighted = [] |
|
last_pos = 0 |
|
|
|
for error in sorted_errors: |
|
highlighted.append(html.escape(text[last_pos:error['start']])) |
|
|
|
highlighted.append( |
|
f'<span style="background-color: #ffe066; border-bottom: 2px dotted #ff9900; ' |
|
f'position: relative;" title="{html.escape(error["message"])}">' |
|
f'{html.escape(text[error["start"]:error["end"]])}' |
|
f'</span>' |
|
) |
|
|
|
last_pos = error['end'] |
|
|
|
highlighted.append(html.escape(text[last_pos:])) |
|
|
|
html_content = f""" |
|
<div style=" |
|
background: white; |
|
padding: 20px; |
|
border-radius: 8px; |
|
border: 1px solid #ddd; |
|
white-space: pre-wrap; |
|
font-family: monospace; |
|
color: #000000; |
|
"> |
|
{''.join(highlighted)} |
|
</div> |
|
""" |
|
|
|
st.markdown("### Результат проверки:") |
|
st.markdown(html_content, unsafe_allow_html=True) |
|
|
|
st.markdown("### Найденные ошибки:") |
|
for i, error in enumerate(errors, 1): |
|
st.markdown(f"{i}. {error['message']}") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |
|
|